const express = require('express'); const bcrypt = require('bcryptjs'); const { randomUUID } = require('crypto'); const rateLimit = require('express-rate-limit'); const db = require('../db'); const asyncHandler = require('../middleware/asyncHandler'); const { requireLogin } = require('../middleware/auth'); const router = express.Router(); const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 20, message: { error: 'Zu viele Anmeldeversuche, bitte später erneut versuchen.' }, standardHeaders: true, legacyHeaders: false, }); // Whether the system needs initial setup (no users yet). router.get('/setup-required', asyncHandler(async (req, res) => { const row = await db.get('SELECT COUNT(*) AS n FROM users'); res.json({ setupRequired: row.n === 0 }); })); // One-shot setup: create the first admin user. Only works when users table is empty. router.post('/setup', asyncHandler(async (req, res) => { const existing = await db.get('SELECT COUNT(*) AS n FROM users'); if (existing.n > 0) { return res.status(403).json({ error: 'Setup bereits abgeschlossen' }); } const { username, password, displayName } = req.body || {}; if (!username || !password) { return res.status(400).json({ error: 'Username und Passwort erforderlich' }); } if (password.length < 8) { return res.status(400).json({ error: 'Passwort muss mindestens 8 Zeichen lang sein' }); } const hash = await bcrypt.hash(password, 10); const uuid = randomUUID(); const result = await db.run( 'INSERT INTO users (uuid, username, password_hash, display_name, is_admin) VALUES (?, ?, ?, ?, 1)', [uuid, username, hash, displayName || username] ); req.session.userId = result.lastID; req.session.username = username; req.session.isAdmin = true; res.json({ id: result.lastID, username, isAdmin: true }); })); router.post('/login', loginLimiter, asyncHandler(async (req, res) => { const { username, password } = req.body || {}; if (!username || !password) { return res.status(400).json({ error: 'Username und Passwort erforderlich' }); } const user = await db.get( 'SELECT id, username, password_hash, display_name, is_admin FROM users WHERE username = ?', [username] ); if (!user) { return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); } const ok = await bcrypt.compare(password, user.password_hash); if (!ok) { return res.status(401).json({ error: 'Ungültige Anmeldedaten' }); } req.session.regenerate((err) => { if (err) return res.status(500).json({ error: 'Session-Fehler' }); req.session.userId = user.id; req.session.username = user.username; req.session.isAdmin = !!user.is_admin; res.json({ id: user.id, username: user.username, displayName: user.display_name, isAdmin: !!user.is_admin, }); }); })); router.post('/logout', (req, res) => { if (!req.session) return res.json({ ok: true }); req.session.destroy(() => { res.clearCookie('connect.sid'); res.json({ ok: true }); }); }); router.get('/me', requireLogin, asyncHandler(async (req, res) => { const user = await db.get( 'SELECT id, username, display_name, is_admin FROM users WHERE id = ?', [req.session.userId] ); if (!user) { req.session.destroy(() => {}); return res.status(401).json({ error: 'Benutzer nicht gefunden' }); } res.json({ id: user.id, username: user.username, displayName: user.display_name, isAdmin: !!user.is_admin, }); })); router.post('/change-password', requireLogin, asyncHandler(async (req, res) => { const { oldPassword, newPassword } = req.body || {}; if (!oldPassword || !newPassword) { return res.status(400).json({ error: 'Altes und neues Passwort erforderlich' }); } if (newPassword.length < 8) { return res.status(400).json({ error: 'Neues Passwort muss mindestens 8 Zeichen lang sein' }); } const user = await db.get('SELECT password_hash FROM users WHERE id = ?', [req.session.userId]); if (!user) return res.status(401).json({ error: 'Benutzer nicht gefunden' }); const ok = await bcrypt.compare(oldPassword, user.password_hash); if (!ok) return res.status(401).json({ error: 'Altes Passwort falsch' }); const newHash = await bcrypt.hash(newPassword, 10); await db.run('UPDATE users SET password_hash = ? WHERE id = ?', [newHash, req.session.userId]); res.json({ ok: true }); })); module.exports = router;