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 || process.cwd(); const FILES = [ { title: 'SQLite Database', path: 'results/uat_vapt_results.sqlite', type: 'SQLite' }, { title: 'SQLite Excel Export', path: 'results/UAT_VAPT_SQLite_Export.xlsx', type: 'Excel' }, { title: 'Updated Master 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: 'Playwright Results JSON', path: 'results/playwright-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 u = 0; while (size >= 1024 && u < units.length - 1) { size /= 1024; u++; } return `${size.toFixed(u === 0 ? 0 : 2)} ${units[u]}`; } function contentType(filePath) { if (filePath.endsWith('.xlsx')) return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; if (filePath.endsWith('.sqlite') || filePath.endsWith('.db')) return 'application/vnd.sqlite3'; 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 action = info.exists ? `Download` : 'Missing'; return `${item.title}${item.type}${info.exists ? 'Available':'Missing'}${formatBytes(info.size)}${info.mtime ? info.mtime.toLocaleString() : '-'}${action}`; }).join(''); return `Playwright UAT/VAPT Results

Playwright UAT/VAPT Results

Last refreshed: ${new Date().toLocaleString()}
${rows}
FileTypeStatusSizeModifiedAction
Keep this page private. Reports may contain screenshots, URLs, user emails, and error details. Recommended 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); } 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'); }).listen(PORT, '0.0.0.0', () => console.log(`Result server running on http://0.0.0.0:${PORT}`));