Files
boc/services/frilans-payout/index.mjs
T
Bernt 1a12fb870b dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- New /developers/ page with API docs, SDKs, pricing, use cases
- OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics
- Case study: Glasskiosken i Flatenbadet — complete ROI analysis
- Updated /order/ with recurring missions and frequency dropdown
2026-07-14 15:27:33 +00:00

113 lines
3.9 KiB
JavaScript

/**
* 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;