const express = require('express'); const router = express.Router(); const bcrypt = require('bcryptjs'); const db = require('../config/database'); const { requireAdmin } = require('../middleware/auth'); // Login router.get('/login', (req, res) => { if (req.session.adminId) return res.redirect('/admin'); res.render('admin/login', { error: null }); }); router.post('/login', async (req, res) => { const { username, password } = req.body; try { const [admins] = await db.query('SELECT * FROM admins WHERE username = ?', [username]); if (admins.length === 0) return res.render('admin/login', { error: 'Ungültige Anmeldedaten.' }); const valid = await bcrypt.compare(password, admins[0].password_hash); if (!valid) return res.render('admin/login', { error: 'Ungültige Anmeldedaten.' }); req.session.adminId = admins[0].id; req.session.adminUser = admins[0].username; res.redirect('/admin'); } catch (err) { res.render('admin/login', { error: 'Serverfehler.' }); } }); router.get('/logout', (req, res) => { req.session.destroy(); res.redirect('/admin/login'); }); // Dashboard router.get('/', requireAdmin, async (req, res) => { try { const [tariffs] = await db.query(` SELECT t.*, c.name as category_name FROM tariffs t LEFT JOIN categories c ON t.category_id = c.id ORDER BY t.active DESC, t.created_at DESC `); const [categories] = await db.query('SELECT * FROM categories ORDER BY name ASC'); const [memberships] = await db.query(` SELECT m.*, t.name as tariff_name, t.price_monthly FROM memberships m LEFT JOIN tariffs t ON m.tariff_id = t.id ORDER BY m.created_at DESC `); const [stats] = await db.query(` SELECT COUNT(*) as total, SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active_count, SUM(CASE WHEN is_minor = 1 THEN 1 ELSE 0 END) as minors, SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as last_30_days FROM memberships `); res.render('admin/dashboard', { tariffs, categories, memberships, stats: stats[0], admin: req.session.adminUser, success: req.query.success || null, error: req.query.error || null }); } catch (err) { console.error(err); res.render('admin/dashboard', { tariffs: [], categories: [], memberships: [], stats: {}, admin: req.session.adminUser, success: null, error: 'Datenbankfehler: ' + err.message }); } }); // ===== KATEGORIEN ===== router.post('/categories', requireAdmin, async (req, res) => { const { name } = req.body; if (!name || !name.trim()) return res.redirect('/admin?error=Kategoriename+fehlt'); try { await db.query('INSERT INTO categories (name) VALUES (?)', [name.trim()]); res.redirect('/admin?success=Kategorie+erstellt#kategorien'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Erstellen'); } }); router.post('/categories/:id/update', requireAdmin, async (req, res) => { const { name } = req.body; if (!name || !name.trim()) return res.redirect('/admin?error=Kategoriename+fehlt'); try { await db.query('UPDATE categories SET name = ? WHERE id = ?', [name.trim(), req.params.id]); res.redirect('/admin?success=Kategorie+aktualisiert#kategorien'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Aktualisieren'); } }); router.post('/categories/:id/delete', requireAdmin, async (req, res) => { try { const [used] = await db.query('SELECT COUNT(*) as c FROM tariffs WHERE category_id = ?', [req.params.id]); if (used[0].c > 0) { return res.redirect('/admin?error=Kategorie+wird+von+' + used[0].c + '+Tarifen+verwendet+–+bitte+erst+Tarife+umziehen#kategorien'); } await db.query('DELETE FROM categories WHERE id = ?', [req.params.id]); res.redirect('/admin?success=Kategorie+gelöscht#kategorien'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Löschen'); } }); // ===== TARIFE ===== router.post('/tariffs', requireAdmin, async (req, res) => { const { name, category_id, duration_months, price_monthly, start_package_price, description } = req.body; try { await db.query( 'INSERT INTO tariffs (name, category_id, duration_months, price_monthly, start_package_price, description) VALUES (?, ?, ?, ?, ?, ?)', [name, category_id || null, duration_months, price_monthly, start_package_price || 35.00, description || ''] ); res.redirect('/admin?success=Tarif+erstellt'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Erstellen+des+Tarifs'); } }); router.post('/tariffs/:id/toggle', requireAdmin, async (req, res) => { try { await db.query('UPDATE tariffs SET active = NOT active WHERE id = ?', [req.params.id]); res.redirect('/admin?success=Tarif+aktualisiert'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Aktualisieren'); } }); router.post('/tariffs/:id/update', requireAdmin, async (req, res) => { const { name, category_id, duration_months, price_monthly, start_package_price, description } = req.body; try { await db.query( 'UPDATE tariffs SET name=?, category_id=?, duration_months=?, price_monthly=?, start_package_price=?, description=? WHERE id=?', [name, category_id || null, duration_months, price_monthly, start_package_price, description, req.params.id] ); res.redirect('/admin?success=Tarif+aktualisiert'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Aktualisieren'); } }); // Passwort ändern router.post('/change-password', requireAdmin, async (req, res) => { const { current_password, new_password, confirm_password } = req.body; if (new_password !== confirm_password) return res.redirect('/admin?error=Passwörter+stimmen+nicht+überein'); try { const [admins] = await db.query('SELECT * FROM admins WHERE id = ?', [req.session.adminId]); const valid = await bcrypt.compare(current_password, admins[0].password_hash); if (!valid) return res.redirect('/admin?error=Aktuelles+Passwort+falsch'); const hash = await bcrypt.hash(new_password, 12); await db.query('UPDATE admins SET password_hash = ? WHERE id = ?', [hash, req.session.adminId]); res.redirect('/admin?success=Passwort+geändert'); } catch (err) { res.redirect('/admin?error=Fehler+beim+Ändern+des+Passworts'); } }); // ===== MITGLIED DETAIL ===== router.get('/members/:id', requireAdmin, async (req, res) => { try { const [rows] = await db.query(` SELECT m.*, t.name as tariff_name, t.price_monthly, t.duration_months, c.name as category_name FROM memberships m LEFT JOIN tariffs t ON m.tariff_id = t.id LEFT JOIN categories c ON t.category_id = c.id WHERE m.id = ? `, [req.params.id]); if (rows.length === 0) return res.redirect('/admin?error=Mitglied+nicht+gefunden'); const [pauses] = await db.query( 'SELECT * FROM membership_pauses WHERE membership_id = ? ORDER BY pause_start DESC', [req.params.id] ); const [tariffs] = await db.query( 'SELECT * FROM tariffs WHERE active = 1 ORDER BY name ASC' ); res.render('admin/member-detail', { member: rows[0], pauses, tariffs, admin: req.session.adminUser, success: req.query.success || null, error: req.query.error || null }); } catch (err) { console.error(err); res.redirect('/admin?error=Fehler+beim+Laden+des+Mitglieds'); } }); router.post('/members/:id/update', requireAdmin, async (req, res) => { const { salutation, title, first_name, last_name, birth_date, email, phone, street, address_addition, zip, city, bank_name, account_holder, iban, tariff_id, status } = req.body; try { await db.query(` UPDATE memberships SET salutation=?, title=?, first_name=?, last_name=?, birth_date=?, email=?, phone=?, street=?, address_addition=?, zip=?, city=?, bank_name=?, account_holder=?, iban=?, tariff_id=?, status=? WHERE id=? `, [ salutation, title || '', first_name, last_name, birth_date, email, phone || '', street, address_addition || '', zip, city, bank_name || '', account_holder || '', iban || '', tariff_id, status, req.params.id ]); res.redirect(`/admin/members/${req.params.id}?success=Daten+gespeichert`); } catch (err) { console.error(err); res.redirect(`/admin/members/${req.params.id}?error=Fehler+beim+Speichern`); } }); module.exports = router;