Läuft die Frist eines Veto-Schritts ab (VETO_STEP_SECONDS, Standard 30, 0 = aus), wählt das Backend für das Team am Zug eine zufällige Map bzw. Seite. Die Veto-Anzeige zeigt die verbleibende Zeit.
68 lines
2.5 KiB
JavaScript
68 lines
2.5 KiB
JavaScript
import fs from 'node:fs';
|
|
import http from 'node:http';
|
|
import path from 'node:path';
|
|
import express from 'express';
|
|
import { REPO_ROOT } from './config.js';
|
|
import { openDatabase } from './db.js';
|
|
import { createApiRouter } from './http.js';
|
|
import { createDiscordNotifier } from './discord.js';
|
|
import { loadMapPools } from './mapPools.js';
|
|
import { MatchService } from './matchService.js';
|
|
import { attachRealtime } from './realtime.js';
|
|
import { createServerAdapter } from './servers/adapter.js';
|
|
import { ServerManager } from './servers/serverManager.js';
|
|
|
|
/** Baut die komplette Anwendung zusammen (auch für Tests nutzbar). */
|
|
export function createApp(config, { adapter: adapterOverride, logger = console } = {}) {
|
|
const db = openDatabase(config.databaseFile);
|
|
const pools = loadMapPools(config.mapPoolFile);
|
|
const notifier = createDiscordNotifier(config.discordWebhookUrl, { logger });
|
|
const service = new MatchService({ db, pools, config, notifier });
|
|
|
|
let manager;
|
|
const adapter = adapterOverride ?? createServerAdapter(config, { getUsedPorts: () => manager.usedPorts() });
|
|
manager = new ServerManager({ db, service, adapter, config, logger });
|
|
manager.init();
|
|
service.startVetoTimer();
|
|
|
|
const app = express();
|
|
if (config.trustProxy) app.set('trust proxy', 1);
|
|
app.disable('x-powered-by');
|
|
app.use((req, res, next) => {
|
|
res.set('X-Content-Type-Options', 'nosniff');
|
|
res.set('Referrer-Policy', 'no-referrer');
|
|
next();
|
|
});
|
|
app.use('/api', createApiRouter({ service, manager, config, pools, adapter }));
|
|
|
|
// Gebautes Frontend ausliefern (npm run build), SPA-Fallback auf index.html.
|
|
const dist = path.join(REPO_ROOT, 'client', 'dist');
|
|
if (fs.existsSync(dist)) {
|
|
app.use(express.static(dist, { index: false, maxAge: '1h' }));
|
|
app.get('*', (req, res) => res.sendFile(path.join(dist, 'index.html')));
|
|
}
|
|
|
|
const server = http.createServer(app);
|
|
const io = attachRealtime(server, { service, config });
|
|
|
|
return {
|
|
app, server, io, db, service, manager, adapter,
|
|
async start() {
|
|
await manager.resume();
|
|
manager.startLoops();
|
|
await new Promise((resolve) => server.listen(config.port, resolve));
|
|
return server.address().port;
|
|
},
|
|
async close() {
|
|
manager.stopLoops();
|
|
service.stopVetoTimer();
|
|
// Laufende Server-Aktionen (Start, Konfiguration) kurz abwarten, bevor die Datenbank schließt
|
|
await manager.idle(5000);
|
|
io.disposeRealtime();
|
|
io.close();
|
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
db.close();
|
|
},
|
|
};
|
|
}
|