fix: code review fixes

This commit is contained in:
Ayobami
2025-08-12 17:41:06 +01:00
parent bd70df60b9
commit 08615ff590
7 changed files with 1389 additions and 16 deletions
+168 -1
View File
@@ -7,7 +7,9 @@ const airportData = JSON.parse(
fs.readFileSync(path.join(__dirname, "../airportdata.json"), "utf8")
);
const db = require("../models");
const { create } = require("xmlbuilder2");
const { create, parse } = require("xmlbuilder2");
const speakeasy = require("speakeasy");
const QRCode = require("qrcode");
const rateLimitMap = new Map();
const multer = require("multer");
const uploadDir = path.join(__dirname, "../public/uploads");
@@ -28,6 +30,15 @@ const redis = require("redis");
const client = redis.createClient();
client.connect().catch(console.error);
// 2FA middleware
function require2FA(req, res, next) {
if (req.session && req.session.twoFactorVerified) {
next();
} else {
res.status(403).json({ error: "2FA verification required" });
}
}
/* GET home page. */
router.get("/", function (req, res, next) {
res.sendFile(path.join(__dirname, "../views/index.html"));
@@ -147,6 +158,84 @@ router.get("/analytic/export", async function (req, res) {
}
});
// Import analytic from XML
router.post(
"/analytic/import",
uploadMulter.single("xmlfile"),
async function (req, res) {
try {
if (!req.file) {
return res.status(400).json({ error: "No XML file uploaded" });
}
// Check file type
if (
req.file.mimetype !== "text/xml" &&
req.file.mimetype !== "application/xml"
) {
return res.status(400).json({ error: "File must be XML format" });
}
// Read and parse the uploaded XML file
const xmlContent = fs.readFileSync(req.file.path, "utf8");
const doc = parse(xmlContent);
// Extract analytics data from XML
const analytics = [];
const analyticNodes = doc.find("//analytic");
for (const node of analyticNodes) {
const id = node.find("id")[0]?.text || null;
const createAt = node.find("create_at")[0]?.text || null;
const widgetName = node.find("widget_name")[0]?.text || null;
const browserType = node.find("browser_type")[0]?.text || null;
if (widgetName && browserType) {
analytics.push({
id: id ? parseInt(id) : undefined,
create_at: createAt ? new Date(createAt) : new Date(),
widget_name: widgetName,
browser_type: browserType,
});
}
}
if (analytics.length === 0) {
return res
.status(400)
.json({ error: "No valid analytics data found in XML" });
}
// Insert analytics into database
const insertedAnalytics = await db.analytic.bulkCreate(analytics, {
ignoreDuplicates: true,
updateOnDuplicate: ["widget_name", "browser_type"],
});
// Clean up uploaded file
fs.unlinkSync(req.file.path);
res.json({
success: true,
message: `Successfully imported ${insertedAnalytics.length} analytics records`,
count: insertedAnalytics.length,
});
} catch (err) {
// Clean up uploaded file on error
if (req.file && req.file.path) {
try {
fs.unlinkSync(req.file.path);
} catch (cleanupErr) {
console.error("Failed to cleanup uploaded file:", cleanupErr);
}
}
console.error("Import error:", err);
res.status(500).json({ error: "Failed to import analytics from XML" });
}
}
);
// Reddit widget route
router.get("/reddit", async function (req, res) {
try {
@@ -484,4 +573,82 @@ router.get("/flow/:id/trigger", async function (req, res) {
}
});
// 2FA Routes
// Generate 2FA secret
router.get("/2fa/generate", async function (req, res) {
try {
const secret = speakeasy.generateSecret({
name: "Dashboard 2FA",
length: 20,
});
// Store secret in session for verification
req.session = req.session || {};
req.session.tempSecret = secret.base32;
// Generate QR code as data URL
const qrCodeDataUrl = await QRCode.toDataURL(secret.otpauth_url, {
width: 200,
margin: 2,
color: {
dark: "#000000",
light: "#FFFFFF",
},
});
res.json({
secret: secret.base32,
qrCode: qrCodeDataUrl,
qrCodeUrl: secret.otpauth_url,
});
} catch (err) {
console.error("2FA generation error:", err);
res.status(500).json({ error: "Failed to generate 2FA secret" });
}
});
// Verify 2FA token
router.post("/2fa/verify", function (req, res) {
try {
const { token } = req.body;
const tempSecret = req.session?.tempSecret;
if (!tempSecret) {
return res
.status(400)
.json({ error: "No 2FA secret found. Please generate a new one." });
}
if (!token) {
return res.status(400).json({ error: "Token required" });
}
const verified = speakeasy.totp.verify({
secret: tempSecret,
encoding: "base32",
token: token,
window: 2, // Allow 2 time steps for clock skew
});
if (verified) {
// Mark 2FA as verified in session
req.session = req.session || {};
req.session.twoFactorVerified = true;
delete req.session.tempSecret; // Clean up temp secret
res.json({ success: true, message: "2FA verification successful" });
} else {
res.status(400).json({ error: "Invalid 2FA token" });
}
} catch (err) {
res.status(500).json({ error: "Failed to verify 2FA token" });
}
});
// Check 2FA status
router.get("/2fa/status", function (req, res) {
const verified = req.session?.twoFactorVerified || false;
res.json({ verified });
});
module.exports = router;