This commit is contained in:
klaas 2024-09-07 16:34:15 +02:00
parent c8924ba724
commit cb562870da
2 changed files with 272 additions and 155 deletions

415
app.js
View File

@ -1,53 +1,54 @@
const express = require('express'); const express = require("express");
const session = require('express-session'); const session = require("express-session");
const bcrypt = require('bcrypt'); const bcrypt = require("bcrypt");
const crypto = require('crypto'); const crypto = require("crypto");
const nodemailer = require('nodemailer'); const nodemailer = require("nodemailer");
const { Pool } = require('pg'); const { Pool } = require("pg");
const path = require('path'); const path = require("path");
const moment = require('moment'); const moment = require("moment");
require('dotenv').config(); require("dotenv").config();
const log = require('node-file-logger'); const log = require("node-file-logger");
const app = express(); const app = express();
const port = process.env.PORT; const port = process.env.PORT;
const options = { const options = {
folderPath: './logs/', folderPath: "./logs/",
dateBasedFileNaming: true, dateBasedFileNaming: true,
fileNamePrefix: 'DailyLogs_', fileNamePrefix: "DailyLogs_",
fileNameExtension: '.log', fileNameExtension: ".log",
dateFormat: 'YYYY_MM_D', dateFormat: "YYYY_MM_D",
timeFormat: 'h:mm:ss A', timeFormat: "h:mm:ss A",
} };
log.SetUserOptions(options); log.SetUserOptions(options);
// Middleware // Middleware
app.use(express.static(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, "public")));
app.use(express.json()); app.use(express.json());
app.set('view engine', 'ejs'); app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
// Session-Konfiguration // Session-Konfiguration
app.use(session({ app.use(
session({
secret: process.env.SESSIONSECRET, secret: process.env.SESSIONSECRET,
resave: false, resave: false,
saveUninitialized: true, saveUninitialized: true,
cookie: { maxAge: 1000 * 60 * 60 * 24 * 2 } cookie: { maxAge: 1000 * 60 * 60 * 24 * 2 },
})); })
);
// Authentifizierungs-Middleware // Authentifizierungs-Middleware
const requireAuth = (req, res, next) => { const requireAuth = (req, res, next) => {
if (!req.session.userId) { if (!req.session.userId) {
return res.redirect('/login'); return res.redirect("/login");
} }
next(); next();
}; };
const requireAdmin = (req, res, next) => { const requireAdmin = (req, res, next) => {
if (req.session.role !== 'admin') { if (req.session.role !== "admin") {
return res.status(403).send('Access denied'); return res.status(403).send("Access denied");
} }
next(); next();
}; };
@ -59,53 +60,58 @@ const transporter = nodemailer.createTransport({
secure: true, secure: true,
auth: { auth: {
user: process.env.MAILUSER, user: process.env.MAILUSER,
pass: process.env.MAILPASS pass: process.env.MAILPASS,
} },
}); });
// Datenbankverbindung // Datenbankverbindung
const pool = new Pool({ const pool = new Pool({
connectionString: process.env.DATABASE_URL connectionString: process.env.DATABASE_URL,
}); });
// Registrierung // Registrierung
app.post('/register', async (req, res) => { app.post("/register", async (req, res) => {
const { username, email, password } = req.body; const { username, email, password } = req.body;
try { try {
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
await pool.query('INSERT INTO users (username, email, password) VALUES ($1, $2, $3)', [username, email, hashedPassword]); await pool.query(
const message = 'Registrierung erfolgreich. Ein Admin wird dich in kürze freischalten'; "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 = { const mailOptions = {
to: 'admin@boergmann.it', to: "admin@boergmann.it",
from: 'admin@boergmann.it', from: "admin@boergmann.it",
subject: 'Neue Registrierung', subject: "Neue Registrierung",
text: `${username} hat sich registriert` text: `${username} hat sich registriert`,
}; };
transporter.sendMail(mailOptions, (error) => { transporter.sendMail(mailOptions, (error) => {
if (error) { if (error) {
console.error('Error sending email:', error); console.error("Error sending email:", error);
const message = 'Error sending Mail:' + error; const message = "Error sending Mail:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} catch (error) { } catch (error) {
console.error('Error registering user:', error); console.error("Error registering user:", error);
const message = 'Error registering user:' + error; const message = "Error registering user:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
// Login // Login
app.post('/login', async (req, res) => { app.post("/login", async (req, res) => {
const { username, password } = req.body; const { username, password } = req.body;
try { 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]); 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) { if (userResult.rows.length > 0) {
const user = userResult.rows[0]; const user = userResult.rows[0];
const match = await bcrypt.compare(password, user.password); const match = await bcrypt.compare(password, user.password);
@ -114,224 +120,331 @@ app.post('/login', async (req, res) => {
req.session.userId = user.id; req.session.userId = user.id;
req.session.userName = user.username; req.session.userName = user.username;
req.session.activeRiege = 1; req.session.activeRiege = 1;
req.session.activeTab = 'geraete'; req.session.activeTab = "geraete";
req.session.message = [title = '', message = '', type = 'none']; req.session.message = [(title = ""), (message = ""), (type = "none")];
log.Info(username + ' ' + password); log.Info(username + " " + password);
if (user.admin_status === 'expired') { if (user.admin_status === "expired") {
await pool.query('UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2', ['user', user.id]); await pool.query(
req.session.role = 'user'; "UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2",
["user", user.id]
);
req.session.role = "user";
} else { } else {
req.session.role = user.role; req.session.role = user.role;
} }
res.redirect('/'); res.redirect("/");
} else { } else {
res.redirect('/freischaltung') res.redirect("/freischaltung");
} }
} else { } else {
const message = 'Falscher Benutzername oder falsches Passwort'; const message = "Falscher Benutzername oder falsches Passwort";
res.render('login', { session: req.session, username, message }); res.render("login", { session: req.session, username, message });
} }
} else { } else {
const message = 'Falscher Benutzername oder falsches Passwort'; const message = "Falscher Benutzername oder falsches Passwort";
res.render('login', { session: req.session, username, message }); res.render("login", { session: req.session, username, message });
} }
} catch (error) { } catch (error) {
console.error('Error logging in:', error); console.error("Error logging in:", error);
const message = 'Error logging in:' + error; const message = "Error logging in:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
//Wird angezeigt, wenn ein nicht freigeschalteter User sich anmelden will. //Wird angezeigt, wenn ein nicht freigeschalteter User sich anmelden will.
app.get('/freischaltung', async (req, res) => { app.get("/freischaltung", async (req, res) => {
res.render('freischaltung', { session: req.session }); res.render("freischaltung", { session: req.session });
}) });
// Logout // Logout
app.get('/logout', (req, res) => { app.get("/logout", (req, res) => {
req.session.destroy(err => { req.session.destroy((err) => {
if (err) { if (err) {
return res.status(500).send('Internal Server Error'); return res.status(500).send("Internal Server Error");
} }
res.redirect('/'); res.redirect("/");
}); });
}); });
// Benutzer freischalten (nur Admin) // Benutzer freischalten (nur Admin)
app.post('/userrights', requireAuth, requireAdmin, async (req, res) => { app.post("/userrights", requireAuth, requireAdmin, async (req, res) => {
const { userId, type } = req.body; const { userId, type } = req.body;
try { try {
if (type === 'activate') { if (type === "activate") {
await pool.query('UPDATE users SET is_active = TRUE WHERE id = $1', [userId]); await pool.query("UPDATE users SET is_active = TRUE WHERE id = $1", [
const userResult = await pool.query('SELECT * FROM users WHERE id = $1', [userId]); userId,
]);
const userResult = await pool.query("SELECT * FROM users WHERE id = $1", [
userId,
]);
if (userResult.rows.length > 0) { if (userResult.rows.length > 0) {
if (userResult.rows[0].email) { if (userResult.rows[0].email) {
const mailOptions = { const mailOptions = {
to: userResult.rows[0].email, to: userResult.rows[0].email,
from: 'admin@boergmann.it', from: "admin@boergmann.it",
subject: 'Freischaltung', subject: "Freischaltung",
text: `Hallo ${userResult.rows[0].username}, du wurdest soeben freigeschaltet.` text: `Hallo ${userResult.rows[0].username}, du wurdest soeben freigeschaltet.`,
}; };
transporter.sendMail(mailOptions, (error) => { transporter.sendMail(mailOptions, (error) => {
if (error) { if (error) {
console.error('Error sending email:', error); console.error("Error sending email:", error);
const message = 'Error sending Mail:' + error; const message = "Error sending Mail:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}) });
} }
} }
} else if (type === 'admin') { } else if (type === "admin") {
await pool.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); await pool.query("UPDATE users SET role = $1 WHERE id = $2", [
"admin",
userId,
]);
} else if (type === "admint") {
await pool.query(
"UPDATE users SET role = $1, admin_temp = $2 WHERE id = $3",
["admin", moment().toDate(), userId]
);
} else if (type === "delete") {
await pool.query("DELETE FROM users WHERE id = $1", [userId]);
} }
else if (type === 'admint') { res.redirect("/admin");
await pool.query('UPDATE users SET role = $1, admin_temp = $2 WHERE id = $3', ['admin', moment().toDate(), userId]);
} else if (type === 'delete') {
await pool.query('DELETE FROM users WHERE id = $1', [userId]);
}
res.redirect('/admin');
} catch (error) { } catch (error) {
console.error('Error activating user:', error); console.error("Error activating user:", error);
const message = 'Error activating user:' + error; const message = "Error activating user:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
// Passwort-Zurücksetzung anfordern // Passwort-Zurücksetzung anfordern
app.post('/send-password', async (req, res) => { app.post("/send-password", async (req, res) => {
const { email } = req.body; const { email } = req.body;
try { try {
const userResult = await pool.query('SELECT * FROM users WHERE email = $1', [email]); const userResult = await pool.query(
"SELECT * FROM users WHERE email = $1",
[email]
);
if (userResult.rows.length > 0) { if (userResult.rows.length > 0) {
const user = userResult.rows[0]; const user = userResult.rows[0];
const token = crypto.randomBytes(20).toString('hex'); const token = crypto.randomBytes(20).toString("hex");
const resetLink = `http://tkd.boergmann.it/reset-password/${token}`; const resetLink = `http://tkd.boergmann.it/reset-password/${token}`;
await pool.query('UPDATE users SET reset_password_token = $1, reset_password_expires = $2 WHERE id = $3', [token, selectedDate = moment().add(1, 'd').toDate(), user.id]); await pool.query(
"UPDATE users SET reset_password_token = $1, reset_password_expires = $2 WHERE id = $3",
[token, (selectedDate = moment().add(1, "d").toDate()), user.id]
);
const mailOptions = { const mailOptions = {
to: user.email, to: user.email,
from: 'admin@boergmann.it', from: "admin@boergmann.it",
subject: 'Password Reset', subject: "Password Reset",
text: `Click the following link to reset your password: ${resetLink}` text: `Click the following link to reset your password: ${resetLink}`,
}; };
transporter.sendMail(mailOptions, (err) => { transporter.sendMail(mailOptions, (err) => {
if (err) { if (err) {
console.error('Error sending email:', err); console.error("Error sending email:", err);
const message = 'Error sending Mail:' + error; const message = "Error sending Mail:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} else { } else {
const message = 'Password reset link sent'; const message = "Password reset link sent";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
} else { } else {
const message = 'Email not found'; const message = "Email not found";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
} catch (error) { } catch (error) {
console.error('Error in forgot-password:', error); console.error("Error in forgot-password:", error);
const message = 'Error in forgot-password'; const message = "Error in forgot-password";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
app.get('/forgot-password', async (req, res) => { app.get("/forgot-password", async (req, res) => {
res.render('forgot-password', { session: req.session }) res.render("forgot-password", { session: req.session });
}) });
// Passwort zurücksetzen // Passwort zurücksetzen
app.get('/reset-password/:token', async (req, res) => { app.get("/reset-password/:token", async (req, res) => {
const { token } = req.params; const { token } = req.params;
try { try {
const userResult = await pool.query('SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2', [token, Date.now()]); const userResult = await pool.query(
"SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2",
[token, Date.now()]
);
if (userResult.rows.length > 0) { if (userResult.rows.length > 0) {
res.render('reset-password', { token }); // Stelle sicher, dass es eine reset-password.ejs gibt res.render("reset-password", { token }); // Stelle sicher, dass es eine reset-password.ejs gibt
} else { } else {
const message = 'Token ungültig oder abgelaufen'; const message = "Token ungültig oder abgelaufen";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
} catch (error) { } catch (error) {
console.error('Error in reset-password:', error); console.error("Error in reset-password:", error);
const message = 'Error in reset-password'; const message = "Error in reset-password";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
app.post('/reset-password/:token', async (req, res) => { app.post("/reset-password/:token", async (req, res) => {
const { token } = req.params; const { token } = req.params;
const { password } = req.body; const { password } = req.body;
try { try {
const userResult = await pool.query('SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2', [token, Date.now()]); const userResult = await pool.query(
"SELECT * FROM users WHERE reset_password_token = $1 AND reset_password_expires > $2",
[token, Date.now()]
);
if (userResult.rows.length > 0) { if (userResult.rows.length > 0) {
const user = userResult.rows[0]; const user = userResult.rows[0];
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
await pool.query('UPDATE users SET password = $1, reset_password_token = NULL, reset_password_expires = NULL WHERE id = $2', [hashedPassword, user.id]); await pool.query(
res.redirect('/login'); "UPDATE users SET password = $1, reset_password_token = NULL, reset_password_expires = NULL WHERE id = $2",
[hashedPassword, user.id]
);
res.redirect("/login");
} else { } else {
const message = 'Token ungültig oder abgelaufen'; const message = "Token ungültig oder abgelaufen";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
} catch (error) { } catch (error) {
console.error('Error in reset-password:', error); console.error("Error in reset-password:", error);
const message = 'Error in reset-password'; const message = "Error in reset-password";
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
// Profilseite // Profilseite
app.get('/profile', requireAuth, (req, res) => { app.get("/profile", requireAuth, (req, res) => {
res.render('profile', { session: req.session }); // Stelle sicher, dass es eine profile.ejs gibt res.render("profile", { session: req.session }); // Stelle sicher, dass es eine profile.ejs gibt
}); });
app.post('/profile', requireAuth, async (req, res) => { app.post("/profile", requireAuth, async (req, res) => {
const { email, password } = req.body; const { email, password } = req.body;
try { try {
if (email) { if (email) {
await pool.query('UPDATE users SET email = $1 WHERE id = $2', [email, req.session.userId]); await pool.query("UPDATE users SET email = $1 WHERE id = $2", [
email,
req.session.userId,
]);
} }
if (password) { if (password) {
const hashedPassword = await bcrypt.hash(password, 10); const hashedPassword = await bcrypt.hash(password, 10);
await pool.query('UPDATE users SET password = $1 WHERE id = $2', [hashedPassword, req.session.userId]); await pool.query("UPDATE users SET password = $1 WHERE id = $2", [
hashedPassword,
req.session.userId,
]);
} }
res.redirect('/profile'); res.redirect("/profile");
} catch (error) { } catch (error) {
console.error('Error updating profile:', error); console.error("Error updating profile:", error);
const message = 'Error updating profile:' + error; const message = "Error updating profile:" + error;
res.render('error', { session: req.session, message }); res.render("error", { session: req.session, message });
} }
}); });
// Route für die Projektliste
app.get("/projects", async (req, res) => {
req.session.message = ["", "", "none"];
try {
const result = await pool.query(
"SELECT id, name, beschreibung FROM projects"
);
const projekte = result.rows;
res.render("projects", { projekte, session: req.session });
} catch (err) {
console.error(err);
res.send(err);
}
});
app.post("/projects", async (req, res) => {
const { name, beschreibung } = req.body;
req.session.message = ["", "", "none"];
try {
await pool.query(
"INSERT INTO projects (name, beschreibung) VALUES ($1, $2)",
[name, beschreibung]
);
} catch (err) {
console.error(err);
res.send(err);
}
try {
const result = await pool.query(
"SELECT id, name, beschreibung FROM projects"
);
const projekte = result.rows;
res.render("projects", { projekte, session: req.session });
} catch (err) {
console.error(err);
res.send(err);
}
});
app.get("/project/:id", async (req, res) => {
req.session.message = ["", "", "none"];
try {
const { id } = req.params;
const result = await pool.query("SELECT * FROM projects WHERE id = $1", [
id,
]);
const projekt = result.rows[0];
if (!projekt) {
return res.status(404).send("Projekt nicht gefunden");
}
res.render("project", { projekt, session: req.session });
} catch (err) {
console.error(err);
res.send("Fehler beim Abrufen des Projekts");
}
});
app.post("/projectedit", async (req, res) => {
req.session.message = ["", "", "none"];
const { id, name, beschreibung } = req.body;
try {
await pool.query(
"UPDATE projects SET name = $1, beschreibung = $2 WHERE id = $3",
[name, beschreibung, id]
);
} catch (err) {
console.error(err);
res.send(err);
}
res.redirect("/project/" + id);
});
// Admin-Seite // Admin-Seite
app.get('/admin', requireAuth, requireAdmin, async (req, res) => { app.get("/admin", requireAuth, requireAdmin, async (req, res) => {
const usersResult = await pool.query('SELECT * FROM users'); const usersResult = await pool.query("SELECT * FROM users");
res.render('admin', { users: usersResult.rows, session: req.session }); // Stelle sicher, dass es eine admin.ejs gibt res.render("admin", { users: usersResult.rows, session: req.session }); // Stelle sicher, dass es eine admin.ejs gibt
}); });
// Login und Registrierung anzeigen // Login und Registrierung anzeigen
app.get('/login', (req, res) => { app.get("/login", (req, res) => {
req.session.message = ['', '', 'none']; req.session.message = ["", "", "none"];
res.render('login', { session: req.session }); // Stelle sicher, dass es eine login.ejs gibt res.render("login", { session: req.session }); // Stelle sicher, dass es eine login.ejs gibt
}); });
// Registrierung // Registrierung
app.get('/register', (req, res) => { app.get("/register", (req, res) => {
res.render('register', { session: req.session }); // Stelle sicher, dass es eine register.ejs gibt res.render("register", { session: req.session }); // Stelle sicher, dass es eine register.ejs gibt
}); });
// Startseite // Startseite
app.get('/', (req, res) => { app.get("/", (req, res) => {
req.session.message = ['', '', 'none'] req.session.message = ["", "", "none"];
res.render('index', { session: req.session }); res.render("index", { session: req.session });
}); });
//Datenschutz und Impressum //Datenschutz und Impressum
app.get('/impressum', (req, res) => { app.get("/impressum", (req, res) => {
req.session.message = ['', '', 'none'] req.session.message = ["", "", "none"];
res.render('impressum', { session: req.session }); res.render("impressum", { session: req.session });
}); });
const server = app.listen(port, '0.0.0.0', () => { const server = app.listen(port, "0.0.0.0", () => {
log.Info(`Server is running on ${process.env.HOST}:${port}/`); log.Info(`Server is running on ${process.env.HOST}:${port}/`);
}); });

View File

@ -36,6 +36,10 @@
</li> </li>
<% } %> <% } %>
<li class="nav-item"></li>
<a class="nav-link" href="/projects">Projekte</a>
</li>
<% if (session && session.role === 'admin') { %> <% if (session && session.role === 'admin') { %>
<li class="nav-item"><a class="nav-link" href="/admin">Admin</a></li> <li class="nav-item"><a class="nav-link" href="/admin">Admin</a></li>
<% } %> <% } %>