File size: 2,345 Bytes
5816640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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}`);
});