1196 lines
36 KiB
JavaScript
1196 lines
36 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");
|
|
require("dotenv").config();
|
|
const log = require("node-file-logger");
|
|
|
|
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 = {
|
|
timeZone: "europe/Berlin",
|
|
folderPath: "./logs/",
|
|
dateBasedFileNaming: true,
|
|
fileNamePrefix: "DailyLogs_",
|
|
fileNameExtension: ".log",
|
|
dateFormat: "YYYY_MM_DD",
|
|
timeFormat: "H:mm:ss",
|
|
};
|
|
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,
|
|
});
|
|
|
|
// Altersberechnung
|
|
const calculateAge = (birthdate) => {
|
|
const today = new Date();
|
|
const birthDate = new Date(birthdate);
|
|
let age = today.getFullYear() - birthDate.getFullYear();
|
|
const m = today.getMonth() - birthDate.getMonth();
|
|
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
|
|
age--;
|
|
}
|
|
return age;
|
|
};
|
|
|
|
const isBirthday = (birthdate, date, previousTraining) => {
|
|
let adjustedBirthdate = new Date(birthdate);
|
|
adjustedBirthdate.setFullYear(date.getFullYear());
|
|
|
|
if (adjustedBirthdate.getMonth() == 10) {
|
|
console.log("checking:", adjustedBirthdate, date, previousTraining);
|
|
}
|
|
|
|
if (adjustedBirthdate.getTime() === date.getTime()) {
|
|
console.log("birthday");
|
|
return 2;
|
|
}
|
|
if (
|
|
adjustedBirthdate.getTime() >= previousTraining.getTime() &&
|
|
adjustedBirthdate.getTime() <= date.getTime()
|
|
) {
|
|
console.log("had birthday");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
// Datumsformatierung
|
|
const formatDate = (date) => {
|
|
const d = new Date(date);
|
|
const day = String(d.getDate()).padStart(2, "0");
|
|
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
const year = d.getFullYear();
|
|
return `${day}.${month}.${year}`;
|
|
};
|
|
|
|
// Funktion zum Laden des aktuellen oder letzten Trainings
|
|
const getTraining = async (kw, jahr) => {
|
|
const result = await pool.query(
|
|
`
|
|
SELECT tr.*, g1.name AS geraet_riege_1_name, g2.name AS geraet_riege_2_name,
|
|
g3.name AS geraet_riege_3_name, g4.name AS geraet_riege_4_name,
|
|
g5.name AS geraet_riege_5_name,
|
|
t1.name AS aufwaermleiter_name, t2.name AS spielleiter_name,
|
|
sp1.name AS aufwaermen_name, sp2.name AS spiel_name
|
|
FROM trainings tr
|
|
LEFT JOIN geraete g1 ON tr.geraet_riege_1 = g1.id
|
|
LEFT JOIN geraete g2 ON tr.geraet_riege_2 = g2.id
|
|
LEFT JOIN geraete g3 ON tr.geraet_riege_3 = g3.id
|
|
LEFT JOIN geraete g4 ON tr.geraet_riege_4 = g4.id
|
|
LEFT JOIN geraete g5 ON tr.geraet_riege_5 = g5.id
|
|
LEFT JOIN teilnehmende t1 ON tr.aufwaermleiter = t1.id
|
|
LEFT JOIN teilnehmende t2 ON tr.spielleiter = t2.id
|
|
LEFT JOIN spiele sp1 ON tr.aufwaermen = sp1.id
|
|
LEFT JOIN spiele sp2 ON tr.spiel = sp2.id
|
|
WHERE tr.kw <= $1 AND tr.jahr = $2
|
|
ORDER BY tr.kw DESC
|
|
LIMIT 1
|
|
`,
|
|
[kw, jahr]
|
|
);
|
|
return result.rows[0];
|
|
};
|
|
|
|
// Funktion zum Laden aller Spiele
|
|
const getAllSpiele = async () => {
|
|
const result = await pool.query(`
|
|
SELECT * FROM spiele ORDER BY name ASC;
|
|
`);
|
|
return result.rows;
|
|
};
|
|
|
|
// Funktion zum Hinzufügen eines neuen Spiels, falls es noch nicht existiert
|
|
const addNewSpiel = async (spielName) => {
|
|
try {
|
|
const result = await pool.query(
|
|
"INSERT INTO spiele (name) VALUES ($1) RETURNING id",
|
|
[spielName]
|
|
);
|
|
return result.rows[0].id;
|
|
} catch (error) {
|
|
console.error("Error adding new spiel:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
function getdayOfWeek(week, year) {
|
|
const weekday = parseInt(process.env.WEEKDAY, 10);
|
|
const firstDayOfYear = new Date(year, 0, 1);
|
|
const dayOfWeek = firstDayOfYear.getDay();
|
|
const daysUntilFirstThursday =
|
|
dayOfWeek <= weekday ? weekday - dayOfWeek : 11 - dayOfWeek;
|
|
const daysUntilThursdayOfWeek = (week - 1) * 7 + daysUntilFirstThursday;
|
|
const thursdayOfWeek = new Date(year, 0, 1 + daysUntilThursdayOfWeek);
|
|
return thursdayOfWeek;
|
|
}
|
|
|
|
// Funktion zum Laden der vier Leute, die am längsten nicht Aufwärmen oder Spiel geleitet haben
|
|
const getCandidatesForAufwaermleiter = async () => {
|
|
const result = await pool.query(`
|
|
SELECT t.id, t.name,
|
|
COALESCE(EXTRACT(EPOCH FROM (NOW() - MAX(tr.datum))) / 604800, EXTRACT(EPOCH FROM (NOW() - '1970-01-01'::date)) / 604800) AS weeks_since_last
|
|
FROM teilnehmende t
|
|
LEFT JOIN trainings tr ON t.id = tr.aufwaermleiter
|
|
WHERE t.helfer = true
|
|
GROUP BY t.id
|
|
ORDER BY weeks_since_last DESC;
|
|
`);
|
|
return result.rows;
|
|
};
|
|
|
|
const putInRiege = async (riege, teilnehmerID) => {
|
|
try {
|
|
if (riege == 0) {
|
|
const resultRiege = await pool.query(
|
|
"DELETE FROM riegen WHERE fremdid_teilnehmende = $1;",
|
|
[teilnehmerID]
|
|
);
|
|
} else {
|
|
const resultRiege = await pool.query(
|
|
"SELECT * FROM riegen WHERE fremdid_teilnehmende = $1;",
|
|
[teilnehmerID]
|
|
);
|
|
|
|
if (resultRiege.rows.length > 0) {
|
|
const resultRiege = await pool.query(
|
|
"UPDATE riegen SET riegennummer = $1 WHERE fremdid_teilnehmende = $2",
|
|
[riege, teilnehmerID]
|
|
);
|
|
log.Info("Mitglied " + teilnehmerID + " ist jetzt in Riege " + riege);
|
|
} else {
|
|
const resultRiege = await pool.query(
|
|
"INSERT INTO riegen (fremdid_teilnehmende, riegennummer) VALUES ($1, $2)",
|
|
[teilnehmerID, riege]
|
|
);
|
|
log.Info(
|
|
"Mitglied " + teilnehmerID + " neu in Riege " + riege + " eingefügt."
|
|
);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error adding new spiel:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
// Funktion zum Laden der vier Leute, die am längsten nicht Spiel geleitet haben
|
|
const getCandidatesForSpielleiter = async () => {
|
|
const result = await pool.query(`
|
|
SELECT t.id, t.name,
|
|
COALESCE(EXTRACT(EPOCH FROM (NOW() - MAX(tr.datum))) / 604800, EXTRACT(EPOCH FROM (NOW() - '1970-01-01'::date)) / 604800) AS weeks_since_last
|
|
FROM teilnehmende t
|
|
LEFT JOIN trainings tr ON t.id = tr.spielleiter
|
|
WHERE t.helfer = true
|
|
GROUP BY t.id
|
|
ORDER BY weeks_since_last DESC;
|
|
`);
|
|
return result.rows;
|
|
};
|
|
|
|
// Funktion zum Laden der Riegenteilnehmer absteigend nach Alter sortiert
|
|
const getRiegenMitgliederSortedByAge = async () => {
|
|
const result = await pool.query(`
|
|
SELECT r.riegennummer, t.name, t.alter
|
|
FROM riegen r
|
|
JOIN teilnehmende t ON r.fremdid_teilnehmende = t.id
|
|
ORDER BY r.riegennummer, t.alter DESC
|
|
`);
|
|
return result.rows;
|
|
};
|
|
|
|
const getAnwesenheit = function (id, anwesend) {
|
|
for (let item of anwesend) {
|
|
if (item.fid_teilnehmer === id) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// 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")];
|
|
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("/training");
|
|
} else {
|
|
res.redirect("/freischaltung");
|
|
}
|
|
} else {
|
|
const message = "Falsches Passwort";
|
|
res.render("login", { session: req.session, username, message });
|
|
}
|
|
} else {
|
|
const message = "Unbekannter Benutzer";
|
|
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()]);
|
|
const userResult = await pool.query(
|
|
"SELECT *, CASE WHEN reset_password_token IS NOT NULL AND (now() - reset_password_expires) > interval '22 hours' THEN 'expired' ELSE 'valid' END AS reset_status FROM users WHERE reset_password_token = $1",
|
|
[token]
|
|
);
|
|
if (userResult.rows.length > 0) {
|
|
res.render("reset-password", { session: req.session, 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 *, CASE WHEN reset_password_token IS NOT NULL AND (now() - reset_password_expires) > interval '22 hours' THEN 'expired' ELSE 'valid' END AS reset_status FROM users WHERE reset_password_token = $1",
|
|
[token]
|
|
);
|
|
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 });
|
|
}
|
|
});
|
|
|
|
app.post("/update-training", requireAuth, async (req, res) => {
|
|
const { trainingId, type, spielName } = req.body;
|
|
req.session.activeTab = "spiel";
|
|
let spielId;
|
|
|
|
const spiel = await pool.query(`SELECT * FROM spiele WHERE name = $1`, [
|
|
spielName,
|
|
]);
|
|
|
|
if (spiel.rows.length > 0) {
|
|
spielId = spiel.rows[0].id;
|
|
} else {
|
|
const newSpiel = await pool.query(
|
|
"INSERT INTO spiele (name) VALUES ($1) RETURNING id",
|
|
[spielName]
|
|
);
|
|
spielId = newSpiel.rows[0].id;
|
|
req.session.message = [
|
|
(title = "Neues Spiel"),
|
|
(body = "Das Spiel ${ spielName } wurde angelegt."),
|
|
];
|
|
}
|
|
|
|
try {
|
|
if (type === "spiel") {
|
|
await pool.query("UPDATE trainings SET spiel = $1 WHERE id = $2", [
|
|
spielId,
|
|
trainingId,
|
|
]);
|
|
} else if (type === "aufwaermen") {
|
|
await pool.query("UPDATE trainings SET aufwaermen = $1 WHERE id = $2", [
|
|
spielId,
|
|
trainingId,
|
|
]);
|
|
}
|
|
|
|
res.redirect("/training");
|
|
} catch (error) {
|
|
console.error(error);
|
|
const message = "Error:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
app.post("/update-leader", requireAuth, async (req, res) => {
|
|
const { trainingId, type, leaderId } = req.body;
|
|
req.session.activeTab = "spiel";
|
|
try {
|
|
if (type === "aufwaermleiter") {
|
|
await pool.query(
|
|
"UPDATE trainings SET aufwaermleiter = $1 WHERE id = $2",
|
|
[leaderId, trainingId]
|
|
);
|
|
} else if (type === "spielleiter") {
|
|
await pool.query("UPDATE trainings SET spielleiter = $1 WHERE id = $2", [
|
|
leaderId,
|
|
trainingId,
|
|
]);
|
|
}
|
|
res.redirect("/training");
|
|
} catch (error) {
|
|
console.error(error);
|
|
const message = "Error:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
// 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
|
|
});
|
|
|
|
app.post("/new-member", requireAuth, requireAdmin, async (req, res) => {
|
|
var {
|
|
from,
|
|
vorname,
|
|
nachname,
|
|
geburt,
|
|
riege,
|
|
adresse,
|
|
probe,
|
|
from,
|
|
training,
|
|
} = req.body;
|
|
var geb = "01.01.2024";
|
|
const name = vorname + " " + nachname;
|
|
try {
|
|
if (geburt) {
|
|
geb = geburt;
|
|
}
|
|
const teilnehmerID = await pool.query(
|
|
"INSERT INTO teilnehmende (name, geburtsdatum, adresse, vorname, nachname, probe) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id",
|
|
[name, geb, adresse, vorname, nachname, probe ? true : false]
|
|
);
|
|
if (from === "admin") {
|
|
selectedKW = moment().isoWeek();
|
|
selectedYear = moment().year();
|
|
const trainingresult = await getTraining(selectedKW, selectedYear);
|
|
training = trainingresult.id;
|
|
}
|
|
await pool.query(
|
|
"INSERT INTO anwesend (fid_teilnehmer, fid_training) VALUES ($1, $2)",
|
|
[teilnehmerID.rows[0].id, training]
|
|
);
|
|
|
|
log.Info(
|
|
"Mitglied " + name + " durch " + req.session.userName + " angelegt"
|
|
);
|
|
putInRiege(riege, teilnehmerID.rows[0].id);
|
|
req.session.activeRiege = riege;
|
|
req.session.activeTab = "anwesend";
|
|
res.redirect("/training");
|
|
} catch (error) {
|
|
const message = "Error:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
// Teilnehmer_innen
|
|
app.get("/training", requireAuth, async (req, res) => {
|
|
try {
|
|
let dateParam = req.query.date;
|
|
let kwParam = req.query.kw;
|
|
let jahrParam = req.query.jahr;
|
|
let selectedDate;
|
|
let selectedKW;
|
|
let selectetYear;
|
|
|
|
if (kwParam) {
|
|
selectedKW = kwParam;
|
|
} else {
|
|
selectedKW = moment().isoWeek();
|
|
}
|
|
|
|
if (jahrParam) {
|
|
selectedYear = jahrParam;
|
|
} else {
|
|
selectedYear = moment().year();
|
|
}
|
|
|
|
const training = await getTraining(selectedKW, selectedYear);
|
|
|
|
const anwesendResult = await pool.query(
|
|
"SELECT * FROM anwesend WHERE fid_training = $1",
|
|
[training.id]
|
|
);
|
|
anwesend = anwesendResult.rows;
|
|
const anzahl = anwesend.length;
|
|
const trainingsResult = await pool.query(
|
|
"SELECT kw, jahr FROM trainings ORDER BY kw ASC"
|
|
);
|
|
const trainingsDates = trainingsResult.rows.map((tr) => ({
|
|
kw: tr.kw,
|
|
datum: formatDate(getdayOfWeek(tr.kw, tr.jahr)),
|
|
}));
|
|
|
|
// Vorheriges Training ermitteln
|
|
const previousTrainingResult = await pool.query(
|
|
"SELECT * FROM trainings WHERE id < $1 ORDER BY id DESC LIMIT 1",
|
|
[training.id]
|
|
);
|
|
const nextTrainingResult = await pool.query(
|
|
"SELECT * FROM trainings WHERE id > $1 ORDER BY id ASC LIMIT 1",
|
|
[training.id]
|
|
);
|
|
const previousTraining =
|
|
previousTrainingResult.rows.length > 0
|
|
? previousTrainingResult.rows[0]
|
|
: null;
|
|
const nextTraining =
|
|
nextTrainingResult.rows.length > 0 ? nextTrainingResult.rows[0] : null;
|
|
// Abrufen der Riegendaten einschließlich der Teilnehmer und deren Altersberechnung
|
|
const result = await pool.query(`
|
|
SELECT r.riegennummer,
|
|
t.id,
|
|
t.name,
|
|
t.vorname,
|
|
t.nachname,
|
|
t.geburtsdatum,
|
|
t.probe,
|
|
r.helfer,
|
|
COUNT(a.fid_teilnehmer) AS anwesenheit
|
|
FROM riegen r
|
|
JOIN teilnehmende t ON r.fremdID_Teilnehmende = t.id
|
|
LEFT JOIN anwesend a ON t.id = a.fid_teilnehmer
|
|
GROUP BY r.riegennummer, t.id, t.name, t.vorname, t.nachname, t.geburtsdatum, r.helfer
|
|
ORDER BY r.riegennummer, t.geburtsdatum ASC;
|
|
`);
|
|
|
|
// Gruppieren der Riegenteilnehmer nach Riegennummer
|
|
const riegen = {};
|
|
result.rows.forEach((row) => {
|
|
const age = calculateAge(row.geburtsdatum);
|
|
const hasBirthday = isBirthday(
|
|
row.geburtsdatum,
|
|
training.datum,
|
|
previousTraining.datum
|
|
);
|
|
const tnAnwesend = getAnwesenheit(row.id, anwesend);
|
|
if (!riegen[row.riegennummer]) {
|
|
riegen[row.riegennummer] = [];
|
|
}
|
|
riegen[row.riegennummer].push({
|
|
id: row.id,
|
|
name: row.name,
|
|
vorname: row.vorname,
|
|
nachname: row.nachname,
|
|
geb: row.geburtsdatum,
|
|
hasBirthday: hasBirthday,
|
|
age: age,
|
|
helfer: row.helfer,
|
|
probe: row.probe,
|
|
anwesenheit: row.anwesenheit,
|
|
anwesend: tnAnwesend,
|
|
anzahl,
|
|
});
|
|
});
|
|
if (training) {
|
|
training.datum = getdayOfWeek(training.kw, training.jahr);
|
|
}
|
|
|
|
const aufwaermleiterCandidates = await getCandidatesForAufwaermleiter();
|
|
const spielleiterCandidates = await getCandidatesForSpielleiter();
|
|
const spielCandidates = await getAllSpiele();
|
|
const aufwaermenCandidates = await getAllSpiele();
|
|
|
|
const abteilung = process.env.ABTEILUNG;
|
|
|
|
let view;
|
|
|
|
if (abteilung == "KiTu") {
|
|
view = "trainings_riegen";
|
|
} else {
|
|
view = "trainings";
|
|
}
|
|
|
|
res.render(view, {
|
|
training,
|
|
trainingsDates,
|
|
selectedKW: training.kw,
|
|
aufwaermleiterCandidates,
|
|
spielleiterCandidates,
|
|
aufwaermenCandidates, // Übergeben der Kandidaten für Aufwärmleiter
|
|
spielCandidates, // Übergeben der Spiele
|
|
session: req.session,
|
|
previousTraining,
|
|
nextTraining,
|
|
riegen,
|
|
activeRiege: req.session.activeRiege,
|
|
activeTab: req.session.activeTab,
|
|
moment,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
const message = "Error:" + error;
|
|
res.render("error", { session: req.session, message });
|
|
}
|
|
});
|
|
|
|
app.get("/riege", requireAuth, async (req, res) => {
|
|
try {
|
|
// Abrufen der Riegendaten einschließlich der Teilnehmer und deren Altersberechnung
|
|
const result = await pool.query(`
|
|
SELECT r.riegennummer,
|
|
t.id,
|
|
t.name,
|
|
t.vorname,
|
|
t.nachname,
|
|
t.geburtsdatum,
|
|
t.probe,
|
|
r.helfer,
|
|
COUNT(a.fid_teilnehmer) AS anwesenheit
|
|
FROM riegen r
|
|
JOIN teilnehmende t ON r.fremdID_Teilnehmende = t.id
|
|
LEFT JOIN anwesend a ON t.id = a.fid_teilnehmer
|
|
GROUP BY r.riegennummer, t.id, t.name, t.vorname, t.nachname, t.geburtsdatum, r.helfer
|
|
ORDER BY r.riegennummer, t.geburtsdatum ASC;
|
|
|
|
`);
|
|
// Gruppieren der Riegenteilnehmer nach Riegennummer
|
|
const date = new Date();
|
|
let previous = new Date(date);
|
|
previous.setDate(date.getDate() - 7);
|
|
const riegen = {};
|
|
result.rows.forEach((row) => {
|
|
const age = calculateAge(row.geburtsdatum);
|
|
if (!riegen[row.riegennummer]) {
|
|
riegen[row.riegennummer] = [];
|
|
}
|
|
riegen[row.riegennummer].push({
|
|
id: row.id,
|
|
name: row.name,
|
|
vorname: row.vorname,
|
|
nachname: row.nachname,
|
|
hasBirthday: isBirthday(row.geburtsdatum, date, previous),
|
|
age: age,
|
|
helfer: row.helfer,
|
|
probe: row.probe,
|
|
anwesenheit: row.anwesenheit,
|
|
});
|
|
});
|
|
|
|
res.render("riegen", { riegen: riegen, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error fetching riegen:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.render("error", { session: req.session });
|
|
}
|
|
});
|
|
|
|
app.get("/teilnehmer", requireAuth, requireAdmin, async (req, res) => {
|
|
try {
|
|
const teilnehmendeResult = await pool.query(
|
|
"SELECT m.*, COUNT(a.fid_teilnehmer) AS anwesenheit FROM teilnehmende m LEFT JOIN anwesend a ON m.id = a.fid_teilnehmer GROUP BY m.id ORDER BY helfer DESC, vorname ASC"
|
|
);
|
|
const date = new Date();
|
|
let previous = new Date(date);
|
|
previous.setDate(date.getDate() - 7);
|
|
const teilnehmende = teilnehmendeResult.rows.map((t) => ({
|
|
...t,
|
|
age: calculateAge(t.geburtsdatum),
|
|
hasBirthday: isBirthday(t.geburtsdatum, date, previous),
|
|
}));
|
|
|
|
res.render("teilnehmer", { teilnehmende, session: req.session });
|
|
} catch (error) {
|
|
console.error(error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.render("error", { session: req.session });
|
|
}
|
|
});
|
|
|
|
app.get("/mitglied/:id", requireAuth, requireAdmin, async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
var riege = 0;
|
|
const userResult = await pool.query(
|
|
"SELECT * FROM teilnehmende WHERE id = $1",
|
|
[id]
|
|
);
|
|
const riegeResult = await pool.query(
|
|
"SELECT * FROM riegen WHERE fremdid_teilnehmende = $1",
|
|
[id]
|
|
);
|
|
if (riegeResult.rows.length > 0) {
|
|
riege = riegeResult.rows[0].riegennummer;
|
|
}
|
|
req.session.activeRiege = riege;
|
|
const anwesendResult = await pool.query(
|
|
"SELECT * FROM anwesend WHERE fid_teilnehmer = $1",
|
|
[id]
|
|
);
|
|
const anwesend = anwesendResult.rows.length;
|
|
const numberResult = await pool.query(
|
|
"SELECT telefonnummern.*, telverbindung.bezeichnung FROM telefonnummern JOIN telverbindung ON telefonnummern.verbindung = telverbindung.id WHERE telefonnummern.fid_teilnehmer = $1",
|
|
[id]
|
|
);
|
|
const numbers = numberResult.rows;
|
|
if (userResult.rows.length > 0) {
|
|
const mitglied = userResult.rows.map((t) => ({
|
|
...t,
|
|
age: calculateAge(t.geburtsdatum),
|
|
}));
|
|
res.render("mitglied", {
|
|
id,
|
|
mitglied: mitglied[0],
|
|
numbers,
|
|
riege,
|
|
anwesend,
|
|
session: req.session,
|
|
});
|
|
} else {
|
|
req.session.message = ["Error", "Mitglied existiert nicht", "error"];
|
|
res.redirect("/teilnehmer");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in Mitglied:", error);
|
|
req.session.message = ["Error", "Mitglied existiert nicht", "error"];
|
|
res.redirect("/teilnehmer");
|
|
}
|
|
});
|
|
|
|
app.post("/set-riege", requireAuth, async (req, res) => {
|
|
const { riege, id } = req.body;
|
|
putInRiege(riege, id);
|
|
res.redirect("/riege");
|
|
});
|
|
|
|
app.post("/update-mitglied", requireAdmin, async (req, res) => {
|
|
const {
|
|
type,
|
|
id,
|
|
geburt,
|
|
telid,
|
|
adresse,
|
|
name,
|
|
verbindung,
|
|
nummer,
|
|
vorname,
|
|
nachname,
|
|
probe,
|
|
helfer,
|
|
} = req.body;
|
|
try {
|
|
if (type == "tel") {
|
|
await pool.query(
|
|
"INSERT INTO telefonnummern (fid_teilnehmer, name, verbindung, nummer, stand) VALUES ($1, $2, $3, $4, $5)",
|
|
[id, name, verbindung, nummer, moment().toDate()]
|
|
);
|
|
} else if (type == "tel-delete") {
|
|
await pool.query("DELETE FROM telefonnummern WHERE id = $1", [telid]);
|
|
} else if (type == "adresse") {
|
|
await pool.query(
|
|
"UPDATE teilnehmende SET vorname = $1, nachname = $2, geburtsdatum = $3, adresse = $4, probe = $5, helfer = $6 WHERE id = $7",
|
|
[
|
|
vorname,
|
|
nachname,
|
|
geburt,
|
|
adresse,
|
|
probe ? true : false,
|
|
helfer ? true : false,
|
|
id,
|
|
]
|
|
);
|
|
}
|
|
|
|
res.redirect("/mitglied/" + id);
|
|
} catch (error) {
|
|
console.error(error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.redirect("/mitglied/" + id);
|
|
}
|
|
});
|
|
|
|
// 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
|
|
});
|
|
|
|
// Spieleliste
|
|
app.get("/spiele", async (req, res) => {
|
|
try {
|
|
const spieleResult = await pool.query(
|
|
"SELECT * FROM spiele ORDER BY name ASC;"
|
|
);
|
|
const spiele = spieleResult.rows;
|
|
res.render("spiele", { spiele, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error in Mitglied:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.render("spiele", { spiele, session: req.session });
|
|
}
|
|
});
|
|
|
|
app.post("/delete-spiel", requireAdmin, async (req, res) => {
|
|
const { spielId } = req.body;
|
|
await pool.query("DELETE FROM spiele WHERE id = $1", [spielId]);
|
|
res.redirect("/spiele");
|
|
});
|
|
|
|
// Gerenderte Seite für gewähltes Spiel
|
|
app.get("/spiel/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
const spieleResult = await pool.query(
|
|
"SELECT * FROM spiele WHERE id = $1",
|
|
[id]
|
|
);
|
|
if (spieleResult.rows.length > 0) {
|
|
const spiel = spieleResult.rows[0];
|
|
res.render("spiel", { spiel, session: req.session });
|
|
} else {
|
|
req.session.message = ["Error", "Spiel existiert nicht", "error"];
|
|
res.redirect("/spiele");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error in Spiele:", error);
|
|
req.session.message = ["Error", "Spiel existiert nicht", "error"];
|
|
res.redirect("/spiele");
|
|
}
|
|
});
|
|
|
|
// Postseite für Änderungen
|
|
app.post("/edit-spiel", requireAuth, async (req, res) => {
|
|
const { material, dauer, regeln, variationen, type, id } = req.body;
|
|
try {
|
|
const queryResult = await pool.query(
|
|
"UPDATE spiele set material = $1, regeln = $2, dauer = $3, variationen = $4, type = $5 Where id = $6 ",
|
|
[material, regeln, dauer, variationen, type, id]
|
|
);
|
|
req.session.message = ["Erfolg", "Das Spiel wurde gespeichert", "success"];
|
|
res.redirect("/spiel/" + id);
|
|
} catch (error) {
|
|
console.error("Error in edit Spiel:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.redirect("/spiel/" + id);
|
|
}
|
|
});
|
|
|
|
// Startseite
|
|
app.get("/", (req, res) => {
|
|
req.session.message = ["", "", "none"];
|
|
res.render("index", { session: req.session });
|
|
});
|
|
|
|
// Changelog
|
|
app.get("/changelog", async (req, res) => {
|
|
try {
|
|
const changeResult = await pool.query(
|
|
"SELECT * FROM changelog ORDER BY datetime DESC;"
|
|
);
|
|
const changes = changeResult.rows;
|
|
req.session.message = ["Erfolg", "Log gespeichert", "success"];
|
|
res.render("changelog", { changes, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.redirect("/changelog");
|
|
}
|
|
});
|
|
|
|
app.post("/changelog", requireAdmin, async (req, res) => {
|
|
const { title, body } = req.body;
|
|
try {
|
|
await pool.query("INSERT INTO changelog (title, body) VALUES ($1, $2);", [
|
|
title,
|
|
body,
|
|
]);
|
|
|
|
const changeResult = await pool.query(
|
|
"SELECT * FROM changelog ORDER BY datetime DESC;"
|
|
);
|
|
const changes = changeResult.rows;
|
|
res.render("changelog", { changes, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.render("changelog", { changes, session: req.session });
|
|
}
|
|
});
|
|
|
|
app.post("/anwesend", requireAuth, async (req, res) => {
|
|
var { anw, inriege, trainingId, riege } = req.body;
|
|
req.session.activeTab = "anwesend";
|
|
try {
|
|
for (const mitgliedId of inriege) {
|
|
const Idresult = await pool.query(
|
|
"SELECT *From anwesend WHERE fid_teilnehmer = $1 AND fid_training=$2",
|
|
[mitgliedId, trainingId]
|
|
);
|
|
if (Idresult.rows.length > 0) {
|
|
if (!anw.includes(mitgliedId)) {
|
|
await pool.query(
|
|
"Delete FROM anwesend WHERE fid_teilnehmer = $1 AND fid_training=$2",
|
|
[mitgliedId, trainingId]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (!Array.isArray(anw)) {
|
|
anw = [anw];
|
|
}
|
|
for (const teilnehmer of anw) {
|
|
const Tnresult = await pool.query(
|
|
"SELECT *From anwesend WHERE fid_teilnehmer = $1 AND fid_training=$2",
|
|
[teilnehmer, trainingId]
|
|
);
|
|
if (Tnresult.rows.length === 0) {
|
|
await pool.query(
|
|
"INSERT INTO anwesend (fid_teilnehmer, fid_training) VALUES ($1, $2)",
|
|
[teilnehmer, trainingId]
|
|
);
|
|
}
|
|
}
|
|
req.session.activeRiege = riege;
|
|
req.session.activeTab = "anwesend";
|
|
req.session.message = [
|
|
(title = "Erfolg"),
|
|
(body = "Die Anwesenheit wurde gespeichert"),
|
|
(type = "success"),
|
|
];
|
|
res.redirect("/training");
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.redirect("/training");
|
|
}
|
|
});
|
|
|
|
app.post("/deleteMember", requireAuth, async (req, res) => {
|
|
const { id } = req.body;
|
|
try {
|
|
await pool.query("DELETE from teilnehmende WHERE id = $1", [id]);
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
}
|
|
res.redirect("/teilnehmer");
|
|
});
|
|
|
|
app.get("/feature", requireAuth, async (req, res) => {
|
|
try {
|
|
const featureResult = await pool.query(
|
|
"SELECT * FROM features ORDER BY datetime DESC;"
|
|
);
|
|
const features = featureResult.rows;
|
|
res.render("feature", { features, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.redirect("/feature");
|
|
}
|
|
});
|
|
|
|
app.post("/feature", requireAdmin, async (req, res) => {
|
|
const { title, body, type, urgency, user } = req.body;
|
|
const userResult = await pool.query(
|
|
"SELECT id FROM users WHERE username = $1",
|
|
[user]
|
|
);
|
|
try {
|
|
await pool.query(
|
|
"INSERT INTO features (title, body, type, urgency, fid_user) VALUES ($1, $2, $3, $4, $5);",
|
|
[title, body, type, urgency, userResult.rows[0].id]
|
|
);
|
|
|
|
const featureResult = await pool.query(
|
|
"SELECT * FROM features ORDER BY datetime DESC;"
|
|
);
|
|
const features = featureResult.rows;
|
|
req.session.message = ["Erfolg", "Feature-request gespeichert", "success"];
|
|
res.render("feature", { features, session: req.session });
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
req.session.message = ["Error", error, "error"];
|
|
res.render("feature", { features, session: req.session });
|
|
}
|
|
});
|
|
|
|
const server = app.listen(port, "0.0.0.0", () => {
|
|
log.Info(`Server is running on ${process.env.HOST}:${port}/`);
|
|
});
|