import 'dotenv/config'; import express from 'express'; import helmet from 'helmet'; import compression from 'compression'; import cors from 'cors'; import morgan from 'morgan'; import { z } from 'zod'; import { registerRiverRoutes } from './routes/river.js'; import { registerFlowRoutes } from './routes/flows.js'; import { registerMoneyRoutes } from './routes/money.js'; import { registerBookEmpireRoutes } from './routes/bookEmpire.js'; const app = express(); const PORT = Number(process.env.PORT || process.env.HTTP_PORT || 7860); const AIS3_API_KEY = process.env.AIS3_API_KEY; const requiredEnvSchema = z.object({ SUPABASE_URL: z.string().url(), SUPABASE_SERVICE_ROLE_KEY: z.string().min(10), }); requiredEnvSchema.parse({ SUPABASE_URL: process.env.SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY, }); app.use(helmet()); app.use(cors()); app.use(express.json({ limit: '2mb' })); app.use(express.urlencoded({ extended: true })); app.use(compression()); app.use(morgan('combined')); // Health endpoint (no auth) app.get('/health', (_req, res) => { res.json({ status: 'ok', service: 'hAPI-face2', timestamp: new Date().toISOString(), }); }); // Simple Bearer auth for everything else app.use((req, res, next) => { if (!AIS3_API_KEY) { return next(); // auth disabled when key missing } const header = req.get('authorization'); if (!header || !header.toLowerCase().startsWith('bearer ')) { return res.status(401).json({ error: 'Unauthorized' }); } const token = header.slice(7); if (token !== AIS3_API_KEY) { return res.status(403).json({ error: 'Forbidden' }); } next(); }); // Register API routes under /api const apiRouter = express.Router(); registerRiverRoutes(apiRouter); registerFlowRoutes(apiRouter); registerMoneyRoutes(apiRouter); registerBookEmpireRoutes(apiRouter); app.use('/api', apiRouter); // Error handler // eslint-disable-next-line @typescript-eslint/no-unused-vars app.use((error: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => { console.error('API error:', error); res.status(500).json({ error: 'Internal Server Error', message: error?.message ?? 'Unknown error', }); }); app.listen(PORT, '0.0.0.0', () => { console.log(`hAPI-face2 mediator listening on port ${PORT}`); });