tkd-api/routes/user.js

342 lines
10 KiB
JavaScript

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;