File size: 2,089 Bytes
521a9b6
 
 
 
 
 
 
 
51648f8
521a9b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51648f8
521a9b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import express, { Express, Request, Response, NextFunction } from 'express';
import { config } from '../config';
import { logger } from '../utils/logger';
import healthRouter from './routes/health';
import submitRouter from './routes/submit';
import internalRouter from './routes/internal';
import reportsRouter from './routes/reports';
import accountsRouter from './routes/accounts';
import adminUsersRouter from './routes/admin-users';

/**
 * Create and configure the Express application with all routes and middleware.
 */
export function createApp(): Express {
  const app = express();

  app.disable('x-powered-by');

  app.use((req: Request, res: Response, next: NextFunction) => {
    const origin = req.headers.origin;
    const allowOrigin =
      origin &&
      (config.allowedOrigins.length === 0 || config.allowedOrigins.includes(origin));

    if (allowOrigin) {
      res.setHeader('Access-Control-Allow-Origin', origin);
      res.setHeader('Vary', 'Origin');
    }
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Authorization,Content-Type,Idempotency-Key,X-Admin-Secret');
    res.setHeader('Access-Control-Max-Age', '86400');
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.setHeader('Referrer-Policy', 'no-referrer');

    if (req.method === 'OPTIONS') {
      res.status(204).end();
      return;
    }

    next();
  });

  // --- Body parsers ---
  app.use(express.json({ limit: '1mb' }));

  // --- Routes ---
  app.use(healthRouter);
  app.use(submitRouter);
  app.use(reportsRouter);
  app.use(accountsRouter);
  app.use(adminUsersRouter);
  app.use(internalRouter);

  app.use((req: Request, res: Response) => {
    res.status(404).json({ error: 'Not found' });
  });

  // --- Global error handler ---
  app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
    logger.error('Unhandled server error', {
      error: err.message,
      stack: err.stack,
    });

    res.status(500).json({ error: 'Internal server error' });
  });

  return app;
}