| const express = require('express'); | |
| const cors = require('cors'); | |
| const path = require('path'); | |
| const app = express(); | |
| // Trust HuggingFace / reverse-proxy X-Forwarded-For headers (required for rate limiting) | |
| app.set('trust proxy', 1); | |
| // ββ View engine βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.set('view engine', 'ejs'); | |
| app.set('views', path.join(__dirname, 'views', 'screens')); | |
| // ββ Static files ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.use(express.static(path.join(__dirname, '..', 'public'))); | |
| // ββ Middleware ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.use(cors()); | |
| // Request logger β commented out to reduce noise; re-enable for debugging | |
| // app.use((req, res, next) => { | |
| // const start = Date.now(); | |
| // res.on('finish', () => { | |
| // const body = req.body && Object.keys(req.body).length ? ' body=' + JSON.stringify(req.body) : ''; | |
| // console.log(`[http] ${req.method} ${req.originalUrl}${body} β ${res.statusCode} (${Date.now() - start}ms)`); | |
| // }); | |
| // next(); | |
| // }); | |
| // NOTE: /api/webhooks/snippe needs raw body β mount it BEFORE json middleware | |
| app.use('/api/webhooks', require('./routes/webhooks.routes')); | |
| // JSON body parser for everything else | |
| app.use(express.json()); | |
| app.use(express.urlencoded({ extended: false })); | |
| // ββ Routes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Public captive portal | |
| app.use('/portal', require('./routes/portalAssets.routes')); | |
| app.use('/portal', require('./routes/portal.routes')); | |
| // Tenant API | |
| app.use('/api/auth', require('./routes/auth.routes')); | |
| app.use('/api/devices', require('./routes/devices.routes')); | |
| app.use('/api/plans', require('./routes/plans.routes')); | |
| app.use('/api/dashboard', require('./routes/dashboard.routes')); | |
| app.use('/api/payouts', require('./routes/payouts.routes')); | |
| app.use('/api/sessions', require('./routes/sessions.routes')); | |
| app.use('/api/alerts', require('./routes/alerts.routes')); | |
| app.use('/api/manual-sales', require('./routes/manualSales.routes')); | |
| app.use('/api/sales', require('./routes/sales.routes')); | |
| app.use('/api/tokens', require('./routes/tokens.routes')); | |
| // Admin API | |
| app.use('/api/admin', require('./routes/admin.routes')); | |
| // ββ Health check ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.get('/health', (req, res) => res.json({ status: 'ok', ts: new Date() })); | |
| // ββ 404 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.use((req, res) => res.status(404).json({ error: 'Not found' })); | |
| // ββ Error handler βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| app.use((err, req, res, next) => { | |
| console.error('[Unhandled]', err.message); | |
| res.status(500).json({ error: 'Internal server error' }); | |
| }); | |
| module.exports = app; | |