Files
LightningLotto/back_end/src/app.ts
Michilis d3bf8080b6 Initial commit: Lightning Lottery - Bitcoin Lightning Network powered lottery
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
2025-11-27 22:13:37 +00:00

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;