const express = require('express'); const fs = require('fs'); const path = require('path'); const initiateResources = require('./utils/initiate-resources'); const dbManager = require('./database/DatabaseManager'); const SchedulerService = require('./services/SchedulerService'); const TelegramNotificationService = require('./services/TelegramNotificationService'); // Singleton-Instanz des Telegram Service const telegramService = new TelegramNotificationService(); // Dev: Swagger UI (mount only in non-production) — require lazily let swaggerUi = null; try { swaggerUi = require('swagger-ui-express'); } catch (e) { swaggerUi = null; } class Server { _port; _app; constructor(port) { this._port = port; this._app = express(); const trustProxyHops = Number.parseInt(process.env.TRUST_PROXY_HOPS ?? '1', 10); if (!Number.isNaN(trustProxyHops) && trustProxyHops > 0) { this._app.set('trust proxy', trustProxyHops); } } async generateOpenApiSpecIfNeeded() { if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') { return; } try { const generateOpenApi = require('./generate-openapi'); console.log('🔄 Generating OpenAPI specification...'); await generateOpenApi(); console.log('✓ OpenAPI spec generated'); } catch (error) { console.warn('⚠️ Failed to generate OpenAPI spec:', error.message); } } loadSwaggerDocument() { try { const specPath = path.join(__dirname, '..', 'docs', 'openapi.json'); const raw = fs.readFileSync(specPath, 'utf8'); return JSON.parse(raw); } catch (error) { console.warn('⚠️ Unable to load Swagger document:', error.message); return null; } } async start() { try { await this.generateOpenApiSpecIfNeeded(); // Initialisiere Datenbank console.log('🔄 Initialisiere Datenbank...'); await dbManager.initialize(); console.log('✓ Datenbank bereit'); // Starte Express Server initiateResources(this._app); this._app.use('/upload', express.static( __dirname + '/upload')); this._app.use('/api/previews', express.static( __dirname + '/data/previews')); // Mount Swagger UI in dev only when available if (process.env.NODE_ENV !== 'production' && swaggerUi) { const swaggerDocument = this.loadSwaggerDocument(); if (swaggerDocument) { this._app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); console.log('ℹ️ Swagger UI mounted at /api/docs (dev only)'); } } this._app.listen(this._port, () => { console.log(`✅ Server läuft auf Port ${this._port}`); console.log(`📊 SQLite Datenbank aktiv`); // Speichere SchedulerService in app für Admin-Endpoints this._app.set('schedulerService', SchedulerService); // Starte Scheduler für automatisches Cleanup SchedulerService.start(); // Teste Telegram-Service (nur in Development) if (process.env.NODE_ENV === 'development' && telegramService.isAvailable()) { telegramService.sendTestMessage() .catch(err => console.error('[Telegram] Test message failed:', err.message)); } }); } catch (error) { console.error('💥 Fehler beim Serverstart:', error); process.exit(1); } } // Expose app for testing getApp() { return this._app; } // Initialize app without listening (for tests) async initializeApp() { await dbManager.initialize(); initiateResources(this._app); this._app.use('/upload', express.static( __dirname + '/upload')); this._app.use('/api/previews', express.static( __dirname + '/data/previews')); if (process.env.NODE_ENV !== 'production' && swaggerUi) { const swaggerDocument = this.loadSwaggerDocument(); if (swaggerDocument) { this._app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); } } return this._app; } } module.exports = Server;