Features: - Lightning Network payments via LNbits integration - Provably fair draws using CSPRNG - Random ticket number generation - Automatic payouts with retry/redraw logic - Nostr authentication (NIP-07) - Multiple draw cycles (hourly, daily, weekly, monthly) - PostgreSQL and SQLite database support - Real-time countdown and payment animations - Swagger API documentation - Docker support Stack: - Backend: Node.js, TypeScript, Express - Frontend: Next.js, React, TailwindCSS, Redux - Payments: LNbits
101 lines
2.6 KiB
TypeScript
101 lines
2.6 KiB
TypeScript
import express, { Request, Response, NextFunction } from 'express';
|
|
import cors from 'cors';
|
|
import swaggerUi from 'swagger-ui-express';
|
|
import config from './config';
|
|
import { swaggerSpec } from './config/swagger';
|
|
|
|
// Routes
|
|
import publicRoutes from './routes/public';
|
|
import webhookRoutes from './routes/webhooks';
|
|
import userRoutes from './routes/user';
|
|
import adminRoutes from './routes/admin';
|
|
|
|
// Middleware
|
|
import { generalRateLimiter } from './middleware/rateLimit';
|
|
|
|
const app = express();
|
|
|
|
// Trust proxy for rate limiting (needed when behind reverse proxy)
|
|
if (config.app.nodeEnv === 'production') {
|
|
app.set('trust proxy', 1); // Trust first proxy
|
|
}
|
|
|
|
// CORS configuration
|
|
app.use(cors({
|
|
origin: config.app.nodeEnv === 'production'
|
|
? config.cors.allowedOrigins
|
|
: true,
|
|
credentials: true,
|
|
}));
|
|
|
|
// Body parser
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// General rate limiting
|
|
app.use(generalRateLimiter);
|
|
|
|
// Swagger API Documentation
|
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
|
customCss: '.swagger-ui .topbar { display: none }',
|
|
customSiteTitle: 'Lightning Lottery API Docs',
|
|
}));
|
|
|
|
// Swagger JSON endpoint
|
|
app.get('/api-docs.json', (req: Request, res: Response) => {
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.send(swaggerSpec);
|
|
});
|
|
|
|
// Health check endpoint
|
|
app.get('/health', async (req: Request, res: Response) => {
|
|
const { db } = await import('./database');
|
|
const { lnbitsService } = await import('./services/lnbits');
|
|
|
|
const dbHealth = await db.healthCheck();
|
|
const lnbitsHealth = await lnbitsService.healthCheck();
|
|
|
|
const healthy = dbHealth && lnbitsHealth;
|
|
|
|
res.status(healthy ? 200 : 503).json({
|
|
version: '1.0',
|
|
status: healthy ? 'healthy' : 'unhealthy',
|
|
checks: {
|
|
database: dbHealth ? 'ok' : 'failed',
|
|
lnbits: lnbitsHealth ? 'ok' : 'failed',
|
|
},
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
});
|
|
|
|
// API routes
|
|
app.use('/', publicRoutes);
|
|
app.use('/webhooks', webhookRoutes);
|
|
app.use('/', userRoutes);
|
|
app.use('/admin', adminRoutes);
|
|
|
|
// 404 handler
|
|
app.use((req: Request, res: Response) => {
|
|
res.status(404).json({
|
|
version: '1.0',
|
|
error: 'NOT_FOUND',
|
|
message: 'Endpoint not found',
|
|
});
|
|
});
|
|
|
|
// Error handler
|
|
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
console.error('Unhandled error:', err);
|
|
|
|
res.status(500).json({
|
|
version: '1.0',
|
|
error: 'INTERNAL_ERROR',
|
|
message: config.app.nodeEnv === 'production'
|
|
? 'An internal error occurred'
|
|
: err.message,
|
|
});
|
|
});
|
|
|
|
export default app;
|
|
|