const http = require("http"); const fs = require("fs"); const path = require("path"); const PORT = Number(process.env.RESULT_SERVER_PORT || 3000); const ROOT = process.env.RESULT_ROOT || "/tests"; const FILES = [ { title: "Updated Excel Result", path: "results/Audit_Firm_ERP_Master_UAT_VAPT_Checklist_v2_4_Results.xlsx", type: "Excel", }, { title: "Merged Results JSON", path: "results/merged-results.json", type: "JSON", }, { title: "API Check Results JSON", path: "results/api-check-results.json", type: "JSON", }, { title: "Playwright Report Index", path: "playwright-report/index.html", type: "HTML", }, ]; function safeJoin(relativePath) { const fullPath = path.resolve(ROOT, relativePath); if (!fullPath.startsWith(path.resolve(ROOT))) { throw new Error("Invalid path"); } return fullPath; } function fileInfo(relativePath) { try { const full = safeJoin(relativePath); const stat = fs.statSync(full); return { exists: true, size: stat.size, mtime: stat.mtime, }; } catch { return { exists: false, size: 0, mtime: null, }; } } function formatBytes(bytes) { if (!bytes) return "-"; const units = ["B", "KB", "MB", "GB"]; let size = bytes; let unit = 0; while (size >= 1024 && unit < units.length - 1) { size = size / 1024; unit++; } return `${size.toFixed(unit === 0 ? 0 : 2)} ${units[unit]}`; } function contentType(filePath) { if (filePath.endsWith(".xlsx")) { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; } if (filePath.endsWith(".json")) return "application/json"; if (filePath.endsWith(".html")) return "text/html; charset=utf-8"; if (filePath.endsWith(".png")) return "image/png"; if (filePath.endsWith(".webm")) return "video/webm"; if (filePath.endsWith(".zip")) return "application/zip"; return "application/octet-stream"; } function renderHome() { const rows = FILES.map((item) => { const info = fileInfo(item.path); const status = info.exists ? "Available" : "Missing"; const downloadLink = info.exists ? `Download` : `Not available`; return ` ${item.title} ${item.type} ${status} ${formatBytes(info.size)} ${info.mtime ? info.mtime.toLocaleString() : "-"} ${downloadLink} `; }).join(""); return ` Playwright UAT/VAPT Results

Playwright UAT/VAPT Results

Last refreshed: ${new Date().toLocaleString()}
${rows}
File Type Status Size Modified Action
Keep this page private. Test reports may contain screenshots, URLs, user emails, and error details. Recommended Coolify port: 3000.
`; } function sendFile(res, relativePath) { let fullPath; try { fullPath = safeJoin(relativePath); } catch { res.writeHead(400); res.end("Invalid file path"); return; } if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) { res.writeHead(404); res.end("File not found"); return; } res.writeHead(200, { "Content-Type": contentType(fullPath), "Content-Disposition": `attachment; filename="${path.basename(fullPath)}"`, }); fs.createReadStream(fullPath).pipe(res); } const server = http.createServer((req, res) => { const url = new URL(req.url, `http://${req.headers.host}`); if (url.pathname === "/") { res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); res.end(renderHome()); return; } if (url.pathname === "/download") { const file = url.searchParams.get("file"); if (!file) { res.writeHead(400); res.end("Missing file parameter"); return; } sendFile(res, file); return; } res.writeHead(404); res.end("Not found"); }); server.listen(PORT, "0.0.0.0", () => { console.log(`Playwright result server running on http://0.0.0.0:${PORT}`); });