302 lines
10 KiB
JavaScript
302 lines
10 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');
|
||
|
require('dotenv').config();
|
||
|
const router = express.Router();
|
||
|
|
||
|
// Session-Konfiguration
|
||
|
app.use(session({
|
||
|
secret: 'your_secret_key',
|
||
|
resave: false,
|
||
|
saveUninitialized: false,
|
||
|
cookie: { maxAge: 60000 }
|
||
|
}));
|
||
|
|
||
|
// 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();
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
// Registrierung
|
||
|
router.post('/register', async (req, res) => {
|
||
|
const { username, password } = req.body;
|
||
|
try {
|
||
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||
|
await pool.query('INSERT INTO users (username, password) VALUES ($1, $2)', [username, hashedPassword]);
|
||
|
res.redirect('/login');
|
||
|
} catch (error) {
|
||
|
console.error('Error registering user:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// 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];
|
||
|
const match = await bcrypt.compare(password, user.password);
|
||
|
if (match) {
|
||
|
req.session.userId = user.id;
|
||
|
req.session.role=user.role;
|
||
|
res.redirect('/');
|
||
|
} else {
|
||
|
res.redirect('/login');
|
||
|
}
|
||
|
} else {
|
||
|
res.redirect('/login');
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('Error logging in:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Logout
|
||
|
router.post('/logout', (req, res) => {
|
||
|
req.session.destroy(err => {
|
||
|
if (err) {
|
||
|
return res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
res.redirect('/login');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Benutzer freischalten (nur Admin)
|
||
|
router.post('/activate', requireAuth, requireAdmin, async (req, res) => {
|
||
|
const { userId } = req.body;
|
||
|
try {
|
||
|
await pool.query('UPDATE users SET is_active = TRUE WHERE id = $1', [userId]);
|
||
|
res.redirect('/admin');
|
||
|
} catch (error) {
|
||
|
console.error('Error activating user:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Passwort-Zurücksetzung anfordern
|
||
|
router.post('/forgot-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, Date.now() + 3600000, 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);
|
||
|
res.status(500).send('Error sending email');
|
||
|
} else {
|
||
|
res.send('Password reset link sent');
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
res.status(400).send('Email not found');
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('Error in forgot-password:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Passwort zurücksetzen
|
||
|
router.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 {
|
||
|
res.status(400).send('Password reset token is invalid or has expired');
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('Error in reset-password:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
router.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 {
|
||
|
res.status(400).send('Password reset token is invalid or has expired');
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('Error in reset-password:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Profilseite
|
||
|
router.get('/profile', requireAuth, (req, res) => {
|
||
|
res.render('profile', { session: req.session}); // Stelle sicher, dass es eine profile.ejs gibt
|
||
|
});
|
||
|
|
||
|
router.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);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
router.post('/update-leader', async (req, res) => {
|
||
|
const { trainingId, type, leaderId } = req.body;
|
||
|
|
||
|
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('/');
|
||
|
} catch (err) {
|
||
|
console.error(err);
|
||
|
res.send("Error " + err);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Admin-Seite
|
||
|
router.get('/admin', requireAuth, requireAdmin, async (req, res) => {
|
||
|
const usersResult = await pool.query('SELECT * FROM users WHERE is_active = FALSE');
|
||
|
res.render('admin', { users: usersResult.rows, session: req.session }); // Stelle sicher, dass es eine admin.ejs gibt
|
||
|
});
|
||
|
|
||
|
// Teilnehmer_innen
|
||
|
router.get('/', requireAuth, async (req, res) => {
|
||
|
try {
|
||
|
const selectedDate = req.query.date;
|
||
|
const training = await getTraining(selectedDate);
|
||
|
|
||
|
const trainingsResult = await pool.query('SELECT datum FROM trainings ORDER BY datum ASC');
|
||
|
const trainingsDates = trainingsResult.rows.map(tr => ({
|
||
|
datum: formatDate(tr.datum),
|
||
|
rawDatum: tr.datum
|
||
|
}));
|
||
|
|
||
|
if (training) {
|
||
|
training.datum = formatDate(training.datum);
|
||
|
}
|
||
|
|
||
|
const aufwaermleiterCandidates = await getCandidatesForAufwaermleiter();
|
||
|
const spielleiterCandidates = await getCandidatesForSpielleiter();
|
||
|
const spielCandidates = await getAllSpiele();
|
||
|
const aufwaermenCandidates = await getAllSpiele();
|
||
|
|
||
|
res.render('trainings', {
|
||
|
training,
|
||
|
trainingsDates,
|
||
|
selectedDate: formatDate(selectedDate),
|
||
|
aufwaermleiterCandidates,
|
||
|
spielleiterCandidates,
|
||
|
aufwaermenCandidates, // Übergeben der Kandidaten für Aufwärmleiter
|
||
|
spielCandidates, // Übergeben der Spiele
|
||
|
session: req.session
|
||
|
});
|
||
|
} catch (err) {
|
||
|
console.error(err);
|
||
|
res.send("Error " + err);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
router.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.geburtsdatum, r.helfer
|
||
|
FROM riegen r
|
||
|
JOIN teilnehmende t ON r.fremdID_Teilnehmende = t.id
|
||
|
ORDER BY r.riegennummer, t.geburtsdatum ASC
|
||
|
`);
|
||
|
|
||
|
// Gruppieren der Riegenteilnehmer nach Riegennummer
|
||
|
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,
|
||
|
age: age,
|
||
|
helfer: row.helfer,
|
||
|
});
|
||
|
});
|
||
|
|
||
|
res.render('riegen', { riegen: riegen, session: req.session });
|
||
|
} catch (error) {
|
||
|
console.error('Error fetching riegen:', error);
|
||
|
res.status(500).send('Internal Server Error');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
router.get('/teilnehmer', requireAuth, async (req, res) => {
|
||
|
try {
|
||
|
const teilnehmendeResult = await pool.query('SELECT * FROM teilnehmende');
|
||
|
|
||
|
const teilnehmende = teilnehmendeResult.rows.map(t => ({
|
||
|
...t,
|
||
|
age: calculateAge(t.geburtsdatum)
|
||
|
})).sort((a, b) => b.age - a.age);
|
||
|
|
||
|
res.render('teilnehmer', { teilnehmende, session: req.session });
|
||
|
} catch (err) {
|
||
|
console.error(err);
|
||
|
res.send("Error " + err);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Login und Registrierung anzeigen
|
||
|
router.get('/login', (req, res) => {
|
||
|
res.render('login', {session: req.session}); // Stelle sicher, dass es eine login.ejs gibt
|
||
|
});
|
||
|
|
||
|
router.get('/register', (req, res) => {
|
||
|
res.render('register'); // Stelle sicher, dass es eine register.ejs gibt
|
||
|
});
|