423 lines
12 KiB
JavaScript
423 lines
12 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,
|
||
requireRole,
|
||
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 * 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 rightsResult = await pool.query(
|
||
`SELECT fid_abteilung, fid_role FROM userrights WHERE fid_user = $1`,
|
||
[user.id]
|
||
);
|
||
|
||
// Rollen-Map erstellen:
|
||
const roles = {};
|
||
rightsResult.rows.forEach((row) => {
|
||
roles[row.fid_abteilung] = row.fid_role;
|
||
});
|
||
const token = jwt.sign(
|
||
{ id: user.id, username: user.username, roles },
|
||
process.env.JWT_SECRET,
|
||
{
|
||
expiresIn: "24d",
|
||
}
|
||
);
|
||
res.json({ token, roles });
|
||
} 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 {
|
||
// 1️⃣ Alle Benutzer abfragen:
|
||
const usersResult = await pool.query(
|
||
"SELECT id, username, email, is_active FROM users ORDER BY is_active ASC"
|
||
);
|
||
|
||
const users = usersResult.rows;
|
||
|
||
// 2️⃣ Alle Rollen pro User abfragen:
|
||
const rolesResult = await pool.query(
|
||
`SELECT ur.fid_user, ur.fid_abteilung, ur.fid_role, r.role
|
||
FROM userrights ur
|
||
JOIN userroles r ON ur.fid_role = r.id`
|
||
);
|
||
|
||
// 3️⃣ Rollen für Benutzer gruppieren:
|
||
const userRolesMap = {};
|
||
rolesResult.rows.forEach((row) => {
|
||
if (!userRolesMap[row.fid_user]) {
|
||
userRolesMap[row.fid_user] = [];
|
||
}
|
||
userRolesMap[row.fid_user].push({
|
||
abteilung: row.fid_abteilung,
|
||
role_id: row.fid_role,
|
||
role_name: row.role,
|
||
});
|
||
});
|
||
|
||
// 4️⃣ Benutzer + Rollen zusammenführen:
|
||
const enrichedUsers = users.map((user) => ({
|
||
...user,
|
||
roles: userRolesMap[user.id] || [],
|
||
}));
|
||
|
||
res.json(enrichedUsers);
|
||
} 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.put(
|
||
"/rights/:userId/:role",
|
||
requireAuth,
|
||
requireRole(2),
|
||
async (req, res) => {
|
||
const { userId, role } = req.params;
|
||
const abteilung = req.abteilung; // Abteilung aus Middleware
|
||
|
||
if (!abteilung || !role) {
|
||
return res
|
||
.status(400)
|
||
.json({ error: "Abteilung und Rolle müssen angegeben werden" });
|
||
}
|
||
|
||
try {
|
||
// Prüfen, ob bereits ein Recht für User & Abteilung existiert
|
||
const existing = await pool.query(
|
||
`SELECT id FROM userrights WHERE fid_user = $1 AND fid_abteilung = $2`,
|
||
[userId, abteilung]
|
||
);
|
||
|
||
if (existing.rows.length > 0) {
|
||
// Eintrag existiert → UPDATE
|
||
await pool.query(`UPDATE userrights SET fid_role = $1 WHERE id = $2`, [
|
||
role,
|
||
existing.rows[0].id,
|
||
]);
|
||
} else {
|
||
// Kein Eintrag → INSERT
|
||
await pool.query(
|
||
`INSERT INTO userrights (fid_user, fid_abteilung, fid_role) VALUES ($1, $2, $3)`,
|
||
[userId, abteilung, role]
|
||
);
|
||
}
|
||
|
||
res.json({ message: "Rolle erfolgreich vergeben/geändert" });
|
||
} 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;
|