342 lines
10 KiB
JavaScript
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;
|