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 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 }); } }); // 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 }); // Startseite app.get('/', (req, res) => { req.session.message = ['', '', 'none'] res.render('index', { session: req.session }); }); const server = app.listen(port, '0.0.0.0', () => { log.Info(`Server is running on ${process.env.HOST}:${port}/`); });