import jwt from 'jsonwebtoken'; import { pool } from '../db.js'; function secret() { const value = process.env.JWT_SECRET; if (!value || value.length < 16) { throw new Error('JWT_SECRET fehlt oder ist zu kurz (min. 16 Zeichen).'); } return value; } export function signToken(user) { return jwt.sign({ sub: user.id, role: user.role }, secret(), { expiresIn: process.env.JWT_EXPIRES_IN || '7d', }); } /** * Prueft das Bearer-Token und laedt den aktuellen Benutzer aus der Datenbank. * * Die Spaltenliste ist bewusst vollstaendig aufgezaehlt und kein SELECT *: * das Passwort-Hash soll nicht am Request haengen. Sie muss aber alles * enthalten, was publicUser() ausliefert - sonst faellt beim Neuladen z. B. * die Theme-Wahl auf "nichts gewaehlt" zurueck. */ export async function requireAuth(req, res, next) { const header = req.headers.authorization || ''; const token = header.startsWith('Bearer ') ? header.slice(7) : null; if (!token) return res.status(401).json({ error: 'Nicht angemeldet.' }); let payload; try { payload = jwt.verify(token, secret()); } catch { return res.status(401).json({ error: 'Sitzung abgelaufen. Bitte neu anmelden.' }); } try { const [rows] = await pool.query( `SELECT id, username, email, name, role, color, avatar, theme, theme_mode, is_active, created_at, updated_at FROM icon_users WHERE id = ? LIMIT 1`, [payload.sub] ); const user = rows[0]; if (!user) return res.status(401).json({ error: 'Benutzer existiert nicht mehr.' }); if (!user.is_active) return res.status(403).json({ error: 'Benutzerkonto ist deaktiviert.' }); req.user = { ...user, is_active: Boolean(user.is_active) }; next(); } catch (err) { next(err); } } /** Nur Rolle admin - schuetzt Einstellungen und Nutzerverwaltung. */ export function requireAdmin(req, res, next) { if (req.user?.role !== 'admin') { return res.status(403).json({ error: 'Diese Aktion ist Administratoren vorbehalten.' }); } next(); }