545 lines
16 KiB
JavaScript
545 lines
16 KiB
JavaScript
const express = require("express");
|
|
const session = require("express-session");
|
|
const bcrypt = require("bcrypt");
|
|
const crypto = require("crypto");
|
|
const nodemailer = require("nodemailer");
|
|
const { Pool } = require("pg");
|
|
const path = require("path");
|
|
const moment = require("moment");
|
|
const marked = require("marked");
|
|
const axios = require("axios");
|
|
require("dotenv").config();
|
|
const log = require("node-file-logger");
|
|
const QRCode = require("qrcode");
|
|
|
|
const app = express();
|
|
const port = process.env.PORT;
|
|
|
|
const TeleBot = require("telebot");
|
|
const bot = new TeleBot(process.env.TELEBOT);
|
|
telebotChatID = process.env.TELECHAT;
|
|
|
|
bot.on("text", (msg) => {
|
|
if (msg.from.id == telebotChatID) {
|
|
msg.reply.text(msg.text);
|
|
} else {
|
|
msg.reply.text(
|
|
"Entschuldige, " +
|
|
msg.from.username +
|
|
"\nIch darf nicht mit fremden reden."
|
|
);
|
|
log.Info("Telebot-nachricht: " + msg.from.username + " - " + msg.text);
|
|
}
|
|
});
|
|
|
|
bot.start();
|
|
|
|
const options = {
|
|
folderPath: "./logs/",
|
|
dateBasedFileNaming: true,
|
|
fileNamePrefix: "DailyLogs_",
|
|
fileNameExtension: ".log",
|
|
dateFormat: "YYYY_MM_D",
|
|
timeFormat: "h:mm:ss A",
|
|
};
|
|
log.SetUserOptions(options);
|
|
|
|
// Middleware
|
|
app.use(express.static(path.join(__dirname, "public")));
|
|
app.use(express.json());
|
|
app.set("view engine", "ejs");
|
|
app.use(express.urlencoded({ extended: false }));
|
|
|
|
// Session-Konfiguration
|
|
app.use(
|
|
session({
|
|
secret: process.env.SESSIONSECRET,
|
|
resave: false,
|
|
saveUninitialized: true,
|
|
cookie: { maxAge: 1000 * 60 * 60 * 24 * 2 },
|
|
})
|
|
);
|
|
|
|
// Authentifizierungs-Middleware
|
|
const requireAuth = (req, res, next) => {
|
|
if (!req.session.userId) {
|
|
return res.redirect("/login");
|
|
}
|
|
next();
|
|
};
|
|
|
|
const requireAdmin = (req, res, next) => {
|
|
if (req.session.role !== "admin") {
|
|
return res.status(403).send("Access denied");
|
|
}
|
|
next();
|
|
};
|
|
|
|
// Email-Konfiguration
|
|
const transporter = nodemailer.createTransport({
|
|
host: process.env.MAILHOST,
|
|
port: 465,
|
|
secure: true,
|
|
auth: {
|
|
user: process.env.MAILUSER,
|
|
pass: process.env.MAILPASS,
|
|
},
|
|
});
|
|
|
|
// Datenbankverbindung
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
});
|
|
|
|
// Registrierung
|
|
app.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);
|
|
const message = "Error sending Mail:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
res.render("error", { session: req.session, message });
|
|
} catch (error) {
|
|
console.error("Error registering user:", error);
|
|
const message = "Error registering user:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
// Login
|
|
app.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];
|
|
const match = await bcrypt.compare(password, user.password);
|
|
if (match) {
|
|
if (user.is_active) {
|
|
req.session.userId = user.id;
|
|
req.session.userName = user.username;
|
|
req.session.activeRiege = 1;
|
|
req.session.activeTab = "geraete";
|
|
req.session.message = [(title = ""), (message = ""), (type = "none")];
|
|
log.Info(username + " " + password);
|
|
if (user.admin_status === "expired") {
|
|
await pool.query(
|
|
"UPDATE users SET role = $1, admin_temp = NULL WHERE id = $2",
|
|
["user", user.id]
|
|
);
|
|
req.session.role = "user";
|
|
} else {
|
|
req.session.role = user.role;
|
|
}
|
|
res.redirect("/");
|
|
} else {
|
|
res.redirect("/freischaltung");
|
|
}
|
|
} else {
|
|
const message = "Falscher Benutzername oder falsches Passwort";
|
|
res.render("login", { session: req.session, username, message });
|
|
}
|
|
} else {
|
|
const message = "Falscher Benutzername oder falsches Passwort";
|
|
res.render("login", { session: req.session, username, message });
|
|
}
|
|
} catch (error) {
|
|
console.error("Error logging in:", error);
|
|
const message = "Error logging in:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
//Wird angezeigt, wenn ein nicht freigeschalteter User sich anmelden will.
|
|
app.get("/freischaltung", async (req, res) => {
|
|
res.render("freischaltung", { session: req.session });
|
|
});
|
|
|
|
// Logout
|
|
app.get("/logout", (req, res) => {
|
|
req.session.destroy((err) => {
|
|
if (err) {
|
|
return res.status(500).send("Internal Server Error");
|
|
}
|
|
res.redirect("/");
|
|
});
|
|
});
|
|
|
|
// Benutzer freischalten (nur Admin)
|
|
app.post("/userrights", requireAuth, requireAdmin, async (req, res) => {
|
|
const { userId, type } = req.body;
|
|
try {
|
|
if (type === "activate") {
|
|
await pool.query("UPDATE users SET is_active = TRUE WHERE id = $1", [
|
|
userId,
|
|
]);
|
|
const userResult = await pool.query("SELECT * FROM users WHERE id = $1", [
|
|
userId,
|
|
]);
|
|
if (userResult.rows.length > 0) {
|
|
if (userResult.rows[0].email) {
|
|
const mailOptions = {
|
|
to: userResult.rows[0].email,
|
|
from: "admin@boergmann.it",
|
|
subject: "Freischaltung",
|
|
text: `Hallo ${userResult.rows[0].username}, du wurdest soeben freigeschaltet.`,
|
|
};
|
|
|
|
transporter.sendMail(mailOptions, (error) => {
|
|
if (error) {
|
|
console.error("Error sending email:", error);
|
|
const message = "Error sending Mail:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
}
|
|
}
|
|
} else if (type === "admin") {
|
|
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]);
|
|
}
|
|
res.redirect("/admin");
|
|
} catch (error) {
|
|
console.error("Error activating user:", error);
|
|
const message = "Error activating user:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
// Passwort-Zurücksetzung anfordern
|
|
app.post("/send-password", async (req, res) => {
|
|
const { email } = req.body;
|
|
try {
|
|
const userResult = await pool.query(
|
|
"SELECT * FROM users WHERE email = $1",
|
|
[email]
|
|
);
|
|
if (userResult.rows.length > 0) {
|
|
const user = userResult.rows[0];
|
|
const token = crypto.randomBytes(20).toString("hex");
|
|
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]
|
|
);
|
|
|
|
const mailOptions = {
|
|
to: user.email,
|
|
from: "admin@boergmann.it",
|
|
subject: "Password Reset",
|
|
text: `Click the following link to reset your password: ${resetLink}`,
|
|
};
|
|
|
|
transporter.sendMail(mailOptions, (err) => {
|
|
if (err) {
|
|
console.error("Error sending email:", err);
|
|
const message = "Error sending Mail:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
} else {
|
|
const message = "Password reset link sent";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
} else {
|
|
const message = "Email not found";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in forgot-password:", error);
|
|
const message = "Error in forgot-password";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
app.get("/forgot-password", async (req, res) => {
|
|
res.render("forgot-password", { session: req.session });
|
|
});
|
|
|
|
// Passwort zurücksetzen
|
|
app.get("/reset-password/:token", async (req, res) => {
|
|
const { token } = req.params;
|
|
try {
|
|
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) {
|
|
res.render("reset-password", { token }); // Stelle sicher, dass es eine reset-password.ejs gibt
|
|
} else {
|
|
const message = "Token ungültig oder abgelaufen";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in reset-password:", error);
|
|
const message = "Error in reset-password";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
app.post("/reset-password/:token", async (req, res) => {
|
|
const { token } = req.params;
|
|
const { password } = req.body;
|
|
try {
|
|
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) {
|
|
const user = userResult.rows[0];
|
|
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]
|
|
);
|
|
res.redirect("/login");
|
|
} else {
|
|
const message = "Token ungültig oder abgelaufen";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in reset-password:", error);
|
|
const message = "Error in reset-password";
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
// Profilseite
|
|
app.get("/profile", requireAuth, (req, res) => {
|
|
res.render("profile", { session: req.session }); // Stelle sicher, dass es eine profile.ejs gibt
|
|
});
|
|
|
|
app.post("/profile", 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.session.userId,
|
|
]);
|
|
}
|
|
if (password) {
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
await pool.query("UPDATE users SET password = $1 WHERE id = $2", [
|
|
hashedPassword,
|
|
req.session.userId,
|
|
]);
|
|
}
|
|
res.redirect("/profile");
|
|
} catch (error) {
|
|
console.error("Error updating profile:", error);
|
|
const message = "Error updating profile:" + error;
|
|
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];
|
|
console.log(`${projekt.repository_link}/raw/branch/main/README.md`);
|
|
const url = `${projekt.repository_link}/raw/branch/main/README.md`;
|
|
|
|
// Abrufen der Datei
|
|
const response = await axios.get(url);
|
|
console.log(response.data);
|
|
// Markdown-Inhalt in HTML umwandeln
|
|
const htmlContent = marked(response.data);
|
|
//const htmlContent = "Testdata";
|
|
if (!projekt) {
|
|
return res.status(404).send("Projekt nicht gefunden");
|
|
}
|
|
|
|
res.render("project", {
|
|
projekt,
|
|
session: req.session,
|
|
content: htmlContent,
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.send(err);
|
|
}
|
|
});
|
|
|
|
app.post("/projectedit", async (req, res) => {
|
|
req.session.message = ["", "", "none"];
|
|
const { id, name, beschreibung, repository_link } = req.body;
|
|
try {
|
|
await pool.query(
|
|
"UPDATE projects SET name = $1, beschreibung = $2 repository_link =$3 WHERE id = $4",
|
|
[name, beschreibung, repository_link, id]
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.send(err);
|
|
}
|
|
res.redirect("/project/" + id);
|
|
});
|
|
|
|
// Admin-Seite
|
|
app.get("/admin", requireAuth, requireAdmin, async (req, res) => {
|
|
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
|
|
});
|
|
|
|
// Login und Registrierung anzeigen
|
|
app.get("/login", (req, res) => {
|
|
req.session.message = ["", "", "none"];
|
|
res.render("login", { session: req.session }); // Stelle sicher, dass es eine login.ejs gibt
|
|
});
|
|
|
|
// Registrierung
|
|
app.get("/register", (req, res) => {
|
|
res.render("register", { session: req.session }); // Stelle sicher, dass es eine register.ejs gibt
|
|
});
|
|
|
|
app.get("/contact", (req, res) => {
|
|
res.render("contact", { session: req.session });
|
|
});
|
|
|
|
app.get("/car", (req, res) => {
|
|
res.render("car", { session: req.session });
|
|
});
|
|
|
|
app.post("/car", (req, res) => {
|
|
const { kennzeichen, message, contact } = req.body;
|
|
let formattedLicensePlate = kennzeichen
|
|
.replace(/[^a-zA-Z0-9]/g, "")
|
|
.toUpperCase();
|
|
|
|
if (formattedLicensePlate == "DUKL445") {
|
|
if (contact != "") {
|
|
bot.sendMessage(telebotChatID, contact);
|
|
}
|
|
bot.sendMessage(telebotChatID, message);
|
|
console.log(message + contact);
|
|
req.session.message = ["Success", "Nachricht versendet.", "none"];
|
|
} else {
|
|
log.Info(
|
|
"Kontakt: " +
|
|
contact +
|
|
" - Nachricht: " +
|
|
message +
|
|
" - Kennzeichen: " +
|
|
kennzeichen
|
|
);
|
|
}
|
|
res.render("carsend", { session: req.session });
|
|
});
|
|
|
|
app.get("/qr", requireAuth, (req, res) => {
|
|
res.render("qrcode", { session: req.session });
|
|
});
|
|
|
|
app.post("/qr", requireAuth, (req, res) => {
|
|
const { text, invert } = req.body;
|
|
|
|
const options = {
|
|
color: {
|
|
dark: invert ? "#FFFFFF" : "#000000", // Farbe der QR-Code-Muster
|
|
light: invert ? "#000000" : "#FFFFFF", // Farbe des Hintergrunds
|
|
},
|
|
};
|
|
|
|
QRCode.toDataURL(text, options, (err, url) => {
|
|
if (err) {
|
|
return res.send("Fehler beim Generieren des QR-Codes");
|
|
}
|
|
|
|
res.render("qrcodeshow", {
|
|
session: req.session,
|
|
qrCodeUrl: url,
|
|
});
|
|
});
|
|
});
|
|
|
|
// Startseite
|
|
app.get("/", (req, res) => {
|
|
req.session.message = ["", "", "none"];
|
|
res.render("index", { session: req.session });
|
|
});
|
|
|
|
//Datenschutz und Impressum
|
|
app.get("/impressum", (req, res) => {
|
|
req.session.message = ["", "", "none"];
|
|
res.render("impressum", { session: req.session });
|
|
});
|
|
|
|
const server = app.listen(port, "0.0.0.0", () => {
|
|
log.Info(`Server is running on ${process.env.HOST}:${port}/`);
|
|
});
|