Initial commit: TravelHub multi-tenant flight booking platform
Angular + Go + PostgreSQL sample with strict per-tenant isolation. - Backend: Go API (basic auth, tenant-scoped repository layer, pgx pool) - Frontend: Angular 22 SPA (search, checkout, bookings, dashboard) - Database: schema, seed data and demo bookings migrations - Tests: 311 across Go (integration), Angular (unit) and Playwright (e2e), plus a generated evidence report with PDF export - Security: whitebox VAPT report under security/ Secrets (.env, tokens, DB credentials) are git-ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Serves the evidence report over HTTP.
|
||||
*
|
||||
* The report is a plain folder of files and opens perfectly well from disk, but
|
||||
* a localhost URL is easier to share with a colleague on the same machine, and
|
||||
* some browsers treat file:// pages more suspiciously than http://.
|
||||
*
|
||||
* node scripts/serve-report.mjs http://localhost:9400
|
||||
* node scripts/serve-report.mjs --port 8123
|
||||
* node scripts/serve-report.mjs --no-open
|
||||
*/
|
||||
|
||||
import { createServer } from 'node:http';
|
||||
import { createReadStream, existsSync, statSync } from 'node:fs';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { dirname, extname, join, normalize, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const DIR = join(ROOT, 'e2e', 'evidence-report');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const portFlag = args.indexOf('--port');
|
||||
const PORT = portFlag !== -1 ? Number(args[portFlag + 1]) : 9400;
|
||||
const shouldOpen = !args.includes('--no-open');
|
||||
|
||||
if (!existsSync(join(DIR, 'index.html'))) {
|
||||
console.error(`No report at ${DIR}\nRun the suite first: npm run test:e2e`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const TYPES = {
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.md': 'text/markdown; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.pdf': 'application/pdf',
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.js': 'text/javascript; charset=utf-8',
|
||||
};
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const requested = decodeURIComponent((req.url ?? '/').split('?')[0]);
|
||||
const relative = requested === '/' ? 'index.html' : requested.replace(/^\/+/, '');
|
||||
|
||||
// normalize collapses any ".." before it can escape the report directory.
|
||||
const target = join(DIR, normalize(relative));
|
||||
if (!target.startsWith(DIR)) {
|
||||
res.writeHead(403).end('Forbidden');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existsSync(target) || statSync(target).isDirectory()) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
||||
res.end(`Not found: ${relative}\n\nThis server only holds the evidence report.`);
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': TYPES[extname(target).toLowerCase()] ?? 'application/octet-stream',
|
||||
'Cache-Control': 'no-store',
|
||||
});
|
||||
createReadStream(target).pipe(res);
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.error(
|
||||
`Port ${PORT} is already in use.\n` +
|
||||
`Try another: npm run report:serve -- --port ${PORT + 1}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
const url = `http://localhost:${PORT}/`;
|
||||
console.log(
|
||||
[
|
||||
'',
|
||||
` Test report: ${url}`,
|
||||
` PDF: ${url}TravelHub-test-report.pdf`,
|
||||
` Markdown: ${url}report.md`,
|
||||
'',
|
||||
' Press Ctrl+C to stop.',
|
||||
'',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
if (shouldOpen) {
|
||||
const [cmd, cmdArgs] =
|
||||
process.platform === 'win32'
|
||||
? ['cmd', ['/c', 'start', '', url]]
|
||||
: process.platform === 'darwin'
|
||||
? ['open', [url]]
|
||||
: ['xdg-open', [url]];
|
||||
spawn(cmd, cmdArgs, { detached: true, stdio: 'ignore' }).unref();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user