import { Router, Request, Response } from 'express'; import { query } from '../db/client'; import { logger } from '../services/logger'; /** * Dev-only Routes — werden in Production blockiert * Erlaubt Daten-Manipulation für Testzwecke */ const router = Router(); // Sicherheitscheck: nur in Development verfügbar router.use((_req: Request, res: Response, next: Function) => { if (process.env.NODE_ENV === 'production') { res.status(404).json({ error: 'Not found' }); return; } next(); }); /** * POST /api/dev/match/:id/set-time * Setzt das Datum eines Spiels auf einen bestimmten Zeitpunkt * Body: { minutesFromNow: number } (negativ = Vergangenheit) */ router.post('/match/:id/set-time', async (req: Request, res: Response): Promise => { const matchId = parseInt(req.params.id); const { minutesFromNow } = req.body as { minutesFromNow: number }; if (isNaN(matchId) || typeof minutesFromNow !== 'number') { res.status(400).json({ error: 'matchId und minutesFromNow erforderlich' }); return; } const newDate = new Date(Date.now() + minutesFromNow * 60 * 1000); try { // Original-Datum beim ersten set-time sichern (nur wenn noch nicht gesetzt) await query( `UPDATE matches SET original_utc_date = COALESCE(original_utc_date, utc_date), utc_date = $1 WHERE id = $2`, [newDate.toISOString(), matchId] ); logger.info(`[DEV] Match ${matchId} utc_date gesetzt auf ${newDate.toISOString()}`); res.json({ success: true, matchId, newDate: newDate.toISOString(), minutesFromNow }); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST /api/dev/match/:id/set-status * Setzt den Status eines Spiels (z.B. FINISHED, IN_PLAY, TIMED) * Body: { status: string, scoreHome?: number, scoreAway?: number } */ router.post('/match/:id/set-status', async (req: Request, res: Response): Promise => { const matchId = parseInt(req.params.id); const { status, scoreHome, scoreAway } = req.body as { status: string; scoreHome?: number; scoreAway?: number; }; const VALID_STATUSES = ['TIMED', 'SCHEDULED', 'IN_PLAY', 'PAUSED', 'FINISHED']; if (!VALID_STATUSES.includes(status)) { res.status(400).json({ error: `Status muss einer von: ${VALID_STATUSES.join(', ')} sein` }); return; } try { await query( `UPDATE matches SET status = $1, score_home = $2, score_away = $3 WHERE id = $4`, [status, scoreHome ?? null, scoreAway ?? null, matchId] ); logger.info(`[DEV] Match ${matchId} Status → ${status}`); res.json({ success: true, matchId, status, scoreHome, scoreAway }); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST /api/dev/reset-match * Setzt ein einzelnes Spiel zurück: Status → TIMED, Score → null, null * Body: { matchId?: number } — ohne matchId werden ALLE Spiele zurückgesetzt */ router.post('/reset-match', async (req: Request, res: Response): Promise => { const { matchId } = req.body as { matchId?: number }; try { if (matchId) { await query( `UPDATE matches SET status = 'TIMED', score_home = NULL, score_away = NULL, utc_date = COALESCE(original_utc_date, utc_date), original_utc_date = NULL WHERE id = $1`, [matchId] ); logger.info(`[DEV] Match ${matchId} zurückgesetzt auf TIMED + Original-Datum`); res.json({ success: true, reset: 'single', matchId }); } else { const result = await query<{ id: number }>( `UPDATE matches SET status = 'TIMED', score_home = NULL, score_away = NULL, utc_date = COALESCE(original_utc_date, utc_date), original_utc_date = NULL WHERE status IN ('IN_PLAY','PAUSED','FINISHED') OR original_utc_date IS NOT NULL RETURNING id` ); logger.info(`[DEV] ${result.length} Spiele zurückgesetzt`); res.json({ success: true, reset: 'all', count: result.length }); } } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST /api/dev/reset * Setzt alle manipulierten Spiele zurück (löscht Dev-Tipps, setzt Status zurück) * Nur Spiele die durch Dev-User angelegt wurden */ router.post('/reset-tips', async (req: Request, res: Response): Promise => { const { userId } = req.body as { userId?: string }; try { const result = await query<{ count: string }>( `DELETE FROM tips WHERE user_id = $1 RETURNING id`, [userId ?? 'dev-user-001'] ); await query('REFRESH MATERIALIZED VIEW CONCURRENTLY leaderboard'); res.json({ success: true, deletedTips: result.length }); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); export default router;