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
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user