const express = require("express"); const pool = require("../db"); // Stelle sicher, dass dein DB-Pool importiert wird const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const crypto = require("crypto"); const nodemailer = require("nodemailer"); const { requireAuth, requireAdmin } = require("../middleware/auth"); // Falls Middleware in einer extra Datei liegt const { transporter, sendActivationEmail } = require("../middleware/mail"); const router = express.Router(); // Registrierung router.post("/register", async (req, res) => { const { username, email, password } = req.body; try { const hashedPassword = await bcrypt.hash(password, 10); await pool.query( "INSERT INTO users (username, email, password) VALUES ($1, $2, $3)", [username, email, hashedPassword] ); const message = "Registrierung erfolgreich. Ein Admin wird dich in kürze freischalten"; const mailOptions = { to: "admin@boergmann.it", from: "admin@boergmann.it", subject: "Neue Registrierung", text: `${username} hat sich registriert`, }; transporter.sendMail(mailOptions, (error) => { if (error) { console.error("Error sending email:", error); res.status(500).json({ error: "Interner Serverfehler" }); } }); res.status(201).json({ message: message }); } catch (error) { console.error("Error registering user:", error); res.status(500).json({ error: "Interner Serverfehler" }); } }); // Login router.post("/login", async (req, res) => { const { username, password } = req.body; try { const userResult = await pool.query( "SELECT *, CASE WHEN admin_temp IS NOT NULL AND (now() - admin_temp) > interval '22 hours' THEN 'expired' ELSE 'valid' END AS admin_status FROM users WHERE username = $1", [username] ); if (userResult.rows.length > 0) { const user = userResult.rows[0]; console.log(user); const match = await bcrypt.compare(password, user.password); if (match) { if (user.is_active) { const token = jwt.sign( { id: user.id, username: user.username, role: user.role }, process.env.JWT_SECRET, { expiresIn: "24h", } ); if (user.admin_status === "expired") { await pool.query( "UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2", ["user", user.id] ); } res.json({ token }); } else { return res.status(401).json({ error: "Auf Freischlatung warten" }); } } else { const message = "Falsches Passwort"; return res.status(401).json({ error: "Ungültige Anmeldedaten" }); } } else { const message = "Unbekannter Benutzer"; return res.status(401).json({ error: "Ungültige Anmeldedaten" }); } } catch (error) { console.error("Error logging in:", error); const message = "Error logging in:" + error; return res.status(401).json({ error: "Ungültige Anmeldedaten" }); } }); // Reset initiieren router.post("/reset-password", async (req, res) => { const { email } = req.body; if (!email) { return res.status(400).json({ error: "E-Mail-Adresse ist erforderlich" }); } try { // Prüfen, ob die E-Mail existiert const userResult = await pool.query( "SELECT id, username FROM users WHERE email = $1", [email] ); if (userResult.rows.length === 0) { return res .status(404) .json({ error: "Benutzer mit dieser E-Mail nicht gefunden" }); } const { id, username } = userResult.rows[0]; // Token generieren (random 32 Byte) const token = crypto.randomBytes(32).toString("hex"); const expires = new Date(Date.now() + 3600000); // Token 1 Stunde gültig // Token und Ablaufdatum in die DB speichern await pool.query( "UPDATE users SET reset_password_token = $1, reset_password_expires = $2 WHERE id = $3", [token, expires, id] ); // Mail senden const resetLink = `https://${process.env.HOST}/reset/${token}`; const mailOptions = { from: process.env.MAILUSER, to: email, subject: "Passwort zurücksetzen", text: `Hallo ${username},\n\nKlicke auf den folgenden Link, um dein Passwort zurückzusetzen:\n\n${resetLink}\n\nDer Link ist 1 Stunde gültig.\n\nFalls du das Zurücksetzen nicht beantragt hast, ignoriere diese Nachricht.`, }; await transporter.sendMail(mailOptions); res.json({ message: "Passwort-Reset-Link wurde per E-Mail gesendet." }); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); // Reset Abschließen router.post("/reset/:token", async (req, res) => { const { token } = req.params; const { password } = req.body; if (!password) { return res.status(400).json({ error: "Neues Passwort ist erforderlich" }); } try { // Token in der Datenbank suchen und prüfen, ob es noch gültig ist const result = await pool.query( "SELECT id FROM users WHERE reset_password_token = $1 AND reset_password_expires > NOW()", [token] ); if (result.rows.length === 0) { return res .status(400) .json({ error: "Ungültiges oder abgelaufenes Token" }); } const userId = result.rows[0].id; // Passwort hashen const hashedPassword = await bcrypt.hash(password, 10); // Passwort speichern und Token-Felder leeren await pool.query( "UPDATE users SET password = $1, reset_password_token = NULL, reset_password_expires = NULL WHERE id = $2", [hashedPassword, userId] ); res.json({ message: "Passwort erfolgreich zurückgesetzt" }); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); // Email und/oder Passwort ändern router.post("/update", requireAuth, async (req, res) => { const { email, password } = req.body; try { if (email) { await pool.query("UPDATE users SET email = $1 WHERE id = $2", [ email, req.user.id, ]); } if (password) { const hashedPassword = await bcrypt.hash(password, 10); await pool.query("UPDATE users SET password = $1 WHERE id = $2", [ hashedPassword, req.user.id, ]); } res.json({ message: "Benutzerdaten erfolgreich aktualisiert", }); } catch (error) { console.error("Error updating profile:", error); const message = "Error updating profile:" + error; res.status(400).json({ error: "Keine Änderungen vorgenommen" }); } }); // Liste der User mit Rollen und is_active router.get("/admin", requireAuth, requireAdmin, async (req, res) => { try { const result = await pool.query( "SELECT id, username, email, role, is_active FROM users ORDER BY is_active ASC, role DESC" ); res.json(result.rows); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); router.put("/activate/:id", requireAuth, requireAdmin, async (req, res) => { const { id } = req.params; try { // Benutzer-Daten abrufen (inklusive E-Mail und Benutzername) const userResult = await pool.query( "SELECT username, email FROM users WHERE id = $1", [id] ); if (userResult.rows.length === 0) { return res.status(404).json({ error: "Benutzer nicht gefunden" }); } const { username, email } = userResult.rows[0]; // Benutzer aktivieren const result = await pool.query( "UPDATE users SET is_active = true WHERE id = $1 RETURNING id, username, is_active", [id] ); if (result.rows.length === 0) { return res.status(404).json({ error: "Benutzer nicht gefunden" }); } // E-Mail senden (wenn vorhanden) await sendActivationEmail(email, username); res.json({ message: "Benutzer erfolgreich aktiviert", user: result.rows[0], }); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); router.delete("/:id", requireAuth, requireAdmin, async (req, res) => { const { id } = req.params; try { // Prüfen, ob der Benutzer existiert const userResult = await pool.query( "SELECT id, username FROM users WHERE id = $1", [id] ); if (userResult.rows.length === 0) { return res.status(404).json({ error: "Benutzer nicht gefunden" }); } // Verhindern, dass sich Admins selbst löschen if (req.user.id == id) { return res .status(400) .json({ error: "Ein Admin kann sich nicht selbst löschen" }); } // Benutzer löschen await pool.query("DELETE FROM users WHERE id = $1", [id]); res.json({ message: "Benutzer erfolgreich gelöscht", user: userResult.rows[0], }); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); // User zu Admins machen router.put("/admin/:id", requireAuth, requireAdmin, async (req, res) => { const { id } = req.params; const { temporary } = req.body; // erwartet { "temporary": true } oder { "temporary": false } if (temporary === undefined) { return res.status(400).json({ error: "Es muss angegeben werden, ob der Admin-Zugang temporär sein soll", }); } try { let query, values; if (temporary) { query = ` UPDATE users SET role = 'admin', admin_temp = NOW() + INTERVAL '48 hours' WHERE id = $1 RETURNING id, username, role, admin_temp `; values = [id]; } else { query = ` UPDATE users SET role = 'admin', admin_temp = NULL WHERE id = $1 RETURNING id, username, role, admin_temp `; values = [id]; } const result = await pool.query(query, values); if (result.rows.length === 0) { return res.status(404).json({ error: "Benutzer nicht gefunden" }); } res.json({ message: "Benutzer wurde zum Admin gemacht", user: result.rows[0], }); } catch (err) { console.error(err); res.status(500).json({ error: "Interner Serverfehler" }); } }); module.exports = router;