/** * quiXzoom Frilans Payout Server * Express backend for multi-market payout * * Built per REXO standards: * - Apple iPhone Native UI * - Raw SQL via pg * - JWT auth * - pino logging * - systemd deployment */ import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; import pino from 'pino'; import routes from './routes.mjs'; const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty', options: { colorize: true } } : undefined, }); const app = express(); const PORT = process.env.PORT || 3450; // ── Middleware ────────────────────────────────────────────────────────────── app.use(helmet()); app.use(cors({ origin: process.env.CORS_ORIGIN || '*', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], })); app.use(express.json()); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: { error: 'Too many requests, please try again later' }, }); app.use('/api/', limiter); // Request logging app.use((req, res, next) => { logger.info({ method: req.method, path: req.path, ip: req.ip }, 'Request'); next(); }); // ── Auth Middleware (JWT) ─────────────────────────────────────────────────── app.use('/api/aamos/frilans', (req, res, next) => { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ error: 'Authentication required' }); } // TODO: Validate JWT token // For now, mock user req.user = { id: token }; next(); }); // ── Routes ────────────────────────────────────────────────────────────────── app.use('/api/aamos/frilans', routes); // ── Health Check ──────────────────────────────────────────────────────────── app.get('/health', async (req, res) => { try { const { healthCheck } = await import('./db.mjs'); await healthCheck(); res.json({ status: 'healthy', version: '2.0.0', timestamp: new Date().toISOString(), }); } catch (error) { logger.error(error, 'Health check failed'); res.status(503).json({ status: 'unhealthy', error: error.message }); } }); // ── Error Handling ────────────────────────────────────────────────────────── app.use((err, req, res, next) => { logger.error(err, 'Unhandled error'); res.status(500).json({ error: 'Internal server error', message: process.env.NODE_ENV === 'development' ? err.message : undefined, }); }); // ── Start ─────────────────────────────────────────────────────────────────── app.listen(PORT, () => { logger.info(` ╔══════════════════════════════════════════════════════════╗ ║ quiXzoom Frilans Payout Server v2.0.0 ║ ║ Port: ${PORT} ║ ║ Environment: ${process.env.NODE_ENV || 'development'} ║ ╚══════════════════════════════════════════════════════════╝ `); }); export default app;