const express = require('express'); const http = require('http'); const path = require('path'); const { getEnvConfig, readConfig, writeConfig } = require('./config'); const { listRows, batchCreateRows } = require('./baserow'); const { setupWebSocket, getSessions } = require('./live-session'); const { sanitizeQuestion, shuffleArray } = require('./quiz-logic'); const app = express(); const server = http.createServer(app); app.use(express.json({ limit: '5mb' })); app.use(express.static(path.join(__dirname, '..', 'public'))); function requireAdmin(req, res, next) { if (req.headers['x-admin-token'] !== getEnvConfig().adminPassword) { return res.status(401).json({ error: 'Nicht autorisiert' }); } next(); } /* ============================================================ */ /* Public API */ /* ============================================================ */ app.get('/api/sets', (req, res) => { const config = readConfig(); console.log(`[api] GET /api/sets — ${(config.sets || []).length} sets`); res.json((config.sets || []).map((s) => ({ id: s.id, name: s.name }))); }); app.get('/api/sets/:id/questions', async (req, res) => { try { const config = readConfig(); const set = config.sets.find((s) => s.id === req.params.id); if (!set) return res.status(404).json({ error: 'Fragenset nicht gefunden' }); console.log(`[api] Loading questions for set "${set.name}" (table ${set.tableId})`); const rows = await listRows(set.tableId); console.log(`[api] Loaded ${rows.length} questions`); if (rows.length > 0) { console.log(`[api] Fields: ${Object.keys(rows[0]).join(', ')}`); } res.json(rows); } catch (e) { console.error(`[api] Error loading questions:`, e.message); res.status(500).json({ error: e.message }); } }); app.post('/api/results', async (req, res) => { try { const config = readConfig(); if (!config.answersTableId) return res.status(400).json({ error: 'Antworten-Tabelle nicht konfiguriert' }); const answers = req.body.answers || []; console.log(`[api] Saving ${answers.length} results`); await batchCreateRows(config.answersTableId, answers); res.json({ success: true }); } catch (e) { console.error(`[api] Error saving results:`, e.message); res.status(500).json({ error: e.message }); } }); app.get('/api/results/:name', async (req, res) => { try { const config = readConfig(); if (!config.answersTableId) return res.json([]); const rows = await listRows(config.answersTableId, { Nutzername: req.params.name }); res.json(rows); } catch (e) { console.error(`[api] Error loading results:`, e.message); res.status(500).json({ error: e.message }); } }); /* ============================================================ */ /* Admin API */ /* ============================================================ */ app.post('/api/admin/login', (req, res) => { if (req.body.password === getEnvConfig().adminPassword) { res.json({ success: true, token: getEnvConfig().adminPassword }); } else { res.status(401).json({ error: 'Falsches Passwort' }); } }); app.get('/api/admin/config', requireAdmin, (req, res) => res.json(readConfig())); app.post('/api/admin/config', requireAdmin, (req, res) => { try { writeConfig(req.body); console.log('[api] Config updated'); res.json({ success: true }); } catch (e) { res.status(500).json({ error: e.message }); } }); app.get('/api/admin/sessions', requireAdmin, (req, res) => res.json(getSessions())); app.get('/api/admin/stats', requireAdmin, async (req, res) => { try { const config = readConfig(); if (!config.answersTableId) return res.json({ answers: [] }); const rows = await listRows(config.answersTableId); res.json({ answers: rows }); } catch (e) { console.error('[api] Stats error:', e.message); res.status(500).json({ error: e.message }); } }); // Debug endpoint to test Baserow connection app.get('/api/admin/debug-baserow', requireAdmin, async (req, res) => { try { const { baserowUrl, baserowToken } = getEnvConfig(); const config = readConfig(); const result = { baserowUrl, tokenLength: baserowToken.length, sets: config.sets, tests: [] }; for (const set of config.sets) { try { const rows = await listRows(set.tableId); result.tests.push({ set: set.name, tableId: set.tableId, rowCount: rows.length, sampleFields: rows.length > 0 ? Object.keys(rows[0]) : [] }); } catch (e) { result.tests.push({ set: set.name, tableId: set.tableId, error: e.message }); } } res.json(result); } catch (e) { res.status(500).json({ error: e.message }); } }); /* ---- SPA routes ---- */ app.get('/admin', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'admin.html'))); app.get('/present', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'present.html'))); app.get('*', (req, res) => res.sendFile(path.join(__dirname, '..', 'public', 'index.html'))); /* ---- Start ---- */ setupWebSocket(server); const { port } = getEnvConfig(); server.listen(port, '0.0.0.0', () => { console.log(`[quizalarm] Server laeuft auf Port ${port}`); console.log(`[quizalarm] Baserow URL: ${getEnvConfig().baserowUrl}`); console.log(`[quizalarm] Config: ${JSON.stringify(readConfig())}`); });