File size: 2,918 Bytes
e85d815
 
 
149698e
 
 
 
 
 
 
c544c6d
 
 
149698e
 
 
 
 
 
 
 
efc415a
e85d815
149698e
 
 
 
 
 
 
 
 
 
 
 
 
c544c6d
 
149698e
 
c544c6d
149698e
 
 
 
 
 
907039a
 
 
149698e
 
 
 
 
 
 
c544c6d
149698e
 
 
 
 
 
 
 
 
 
 
 
 
 
efc415a
e85d815
149698e
c544c6d
 
 
 
 
2b48113
 
c544c6d
 
 
 
 
149698e
 
 
 
 
 
 
 
 
 
 
 
 
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Force Eastern Time for all date operations (Gatineau, QC)
process.env.TZ = 'America/Toronto';

import express from 'express';
import { createServer } from 'http';
import { Server as SocketIOServer } from 'socket.io';
import cors from 'cors';
import helmet from 'helmet';
import cookieParser from 'cookie-parser';
import pino from 'pino';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import { existsSync } from 'fs';
import { config } from './config/env.js';
import { db } from './db/index.js';
import { setupScanEvents } from './websocket/scanEvents.js';
import authRoutes from './routes/auth.js';
import emailRoutes from './routes/emails.js';
import transactionRoutes from './routes/transactions.js';
import settingsRoutes from './routes/settings.js';
import receiptRoutes from './routes/receipts.js';
import senderRoutes from './routes/senders.js';
import screenshotRoutes from './routes/screenshots.js';
import { errorHandler } from './middleware/errorHandler.js';

const logger = pino({
  level: 'info',
  transport: {
    target: 'pino-pretty',
    options: { colorize: true },
  },
});

const app = express();
const httpServer = createServer(app);

const corsOrigin = config.APP_URL === '*' ? true : config.APP_URL;

const io = new SocketIOServer(httpServer, {
  cors: {
    origin: corsOrigin,
    methods: ['GET', 'POST'],
    credentials: true,
  },
  path: '/ws',
});

// Trust reverse proxy (HuggingFace, nginx, etc.)
app.set('trust proxy', true);

// Middleware
app.use(
  helmet({
    contentSecurityPolicy: false,
    crossOriginEmbedderPolicy: false,
  })
);
app.use(cors({ origin: corsOrigin, credentials: true }));
app.use(express.json({ limit: '10mb' }));
app.use(cookieParser());

// Health check
app.get('/api/health', (_req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// Routes
app.use('/api/auth', authRoutes);
app.use('/api/scan', emailRoutes);
app.use('/api/transactions', transactionRoutes);
app.use('/api/settings', settingsRoutes);
app.use('/api/receipts', receiptRoutes);
app.use('/api/senders', senderRoutes);
app.use('/api/screenshots', screenshotRoutes);

// Serve frontend static files in production
const __dirname_server = dirname(fileURLToPath(import.meta.url));
const frontendDist = resolve(__dirname_server, '../../web/dist');
if (existsSync(frontendDist)) {
  app.use(express.static(frontendDist));
  // SPA fallback — serve index.html for non-API routes (Express 5 syntax)
  app.get('{*path}', (_req, res) => {
    res.sendFile(resolve(frontendDist, 'index.html'));
  });
  logger.info(`Serving frontend from ${frontendDist}`);
}

// Error handler
app.use(errorHandler);

// WebSocket setup
setupScanEvents(io);

// Export io for use in services
export { io };

const PORT = config.PORT;
httpServer.listen(PORT, () => {
  logger.info(`ICC Interac Manager API running on http://localhost:${PORT}`);
});