local-app.ts 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import cors from 'cors';
  2. import express, { type ErrorRequestHandler } from 'express';
  3. import { ZodError } from 'zod';
  4. import type { DomesticDataset } from './types/domestic-dataset.js';
  5. import { LocalSnapshotService } from './modules/domestic-voc/local/local-snapshot.service.js';
  6. import { LocalSyncJobStore } from './modules/domestic-voc/local/local-sync-job.store.js';
  7. import { createDomesticVocRouter } from './modules/domestic-voc/routes.js';
  8. import { SyncService } from './modules/domestic-voc/services/sync.service.js';
  9. export function createLocalDemoApp(input: {
  10. dataset: DomesticDataset;
  11. corsOrigins: string[];
  12. }) {
  13. const app = express();
  14. app.disable('x-powered-by');
  15. app.use(cors({
  16. origin(origin, callback) {
  17. if (!origin || input.corsOrigins.includes(origin)) return callback(null, true);
  18. return callback(new Error('Origin is not allowed'));
  19. },
  20. }));
  21. app.use(express.json({ limit: '1mb' }));
  22. const jobs = new LocalSyncJobStore(input.dataset);
  23. const sync = new SyncService(jobs);
  24. const snapshot = new LocalSnapshotService(input.dataset);
  25. app.get('/health', (_request, response) => {
  26. response.json({
  27. service: 'saas-voc-server',
  28. status: 'ok',
  29. mode: 'local-demo',
  30. database: 'deferred',
  31. dataset: {
  32. caseName: input.dataset.caseName,
  33. platform: input.dataset.platform,
  34. products: input.dataset.products.length,
  35. reviews: input.dataset.reviews.length,
  36. },
  37. timestamp: new Date().toISOString(),
  38. });
  39. });
  40. app.use('/api/domestic-voc', createDomesticVocRouter({ jobs, sync, snapshot }));
  41. app.use((_request, response) => {
  42. response.status(404).json({ error: 'not_found' });
  43. });
  44. const errorHandler: ErrorRequestHandler = (error, _request, response, _next) => {
  45. if (error instanceof ZodError) {
  46. response.status(400).json({
  47. error: 'invalid_request',
  48. issues: error.issues.map((issue) => ({ path: issue.path.join('.'), message: issue.message })),
  49. });
  50. return;
  51. }
  52. console.error('[local-http] unhandled request error', error instanceof Error ? error.message : error);
  53. response.status(500).json({ error: 'internal_error' });
  54. };
  55. app.use(errorHandler);
  56. return app;
  57. }