Initial commit
This commit is contained in:
commit
d282773b3c
|
@ -0,0 +1,7 @@
|
||||||
|
DATABASE_URL=postgres://user:psw@localhost:5432/postgres
|
||||||
|
SESSIONSECRET=Your Secret
|
||||||
|
MAILHOST=
|
||||||
|
MAILUSER=
|
||||||
|
MAILFROM=
|
||||||
|
MAILPASS=
|
||||||
|
PORT=2000
|
|
@ -0,0 +1,3 @@
|
||||||
|
.env
|
||||||
|
node_modules/
|
||||||
|
logs/*
|
|
@ -0,0 +1,331 @@
|
||||||
|
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}/`);
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "training",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"ejs": "^3.1.10",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"express-session": "^1.18.0",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"node-file-logger": "^0.9.5",
|
||||||
|
"nodemailer": "^6.9.13",
|
||||||
|
"pg": "^8.11.5"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,21 @@
|
||||||
|
const { Pool } = require('pg');
|
||||||
|
require('dotenv').config();
|
||||||
|
const moment = require('moment');
|
||||||
|
|
||||||
|
// Datenbankverbindung
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
async function writeKW() {
|
||||||
|
const resultTrainings = await pool.query('SELECT * FROM trainings');
|
||||||
|
const trainings = resultTrainings.rows;
|
||||||
|
for (const training of trainings) {
|
||||||
|
const jahr = moment(training.datum).year();
|
||||||
|
const kw = moment(training.datum).isoWeek();
|
||||||
|
await pool.query('UPDATE trainings SET kw = $1, jahr = $2 WHERE id = $3', [kw, jahr, training.id])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writeKW();
|
|
@ -0,0 +1,91 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Admin Panel</h1>
|
||||||
|
|
||||||
|
<!-- Nav tabs -->
|
||||||
|
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" id="tab-1" data-bs-toggle="tab" data-bs-target="#cont-1" type="button" role="tab" aria-controls="tab-1" aria-selected='true'> Mitglied anlegen </button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="tab-2" data-bs-toggle="tab" data-bs-target="#cont-2" type="button" role="tab" aria-controls="tab-2" aria-selected='false'> User Freischalten </button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tab-content" id="myTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="cont-1" role="tabpanel" aria-labelledby="tab-1">
|
||||||
|
<form method="POST" action="/new-member">
|
||||||
|
Name: <input type="text" id="vorname" placeholder="Vorname" name="vorname">
|
||||||
|
<input type="text" id="nachname" placeholder="Nachname" name="nachname"> </br>
|
||||||
|
Geburtsdatum: <input type="date" id="geburt" name="geburt"></br>
|
||||||
|
<select name="riege">
|
||||||
|
<option value=1> Riege 1</option>
|
||||||
|
<option value=2> Riege 2</option>
|
||||||
|
<option value=3> Riege 3</option>
|
||||||
|
<option value=4> Riege 4</option>
|
||||||
|
<option value=5> Riege 5</option>
|
||||||
|
</select></br>
|
||||||
|
Adresse: <input type="text" id="adresse" placeholder="Adresse" name="adresse"></br>
|
||||||
|
<button type="submit">Speichern</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="cont-2" role="tabpanel" aria-labelledby="tab-2">
|
||||||
|
<ul>
|
||||||
|
<% users.forEach(user => { %>
|
||||||
|
<li>
|
||||||
|
<%= user.username %> - <%= user.email %>
|
||||||
|
<% if (!user.is_active) { %>
|
||||||
|
<form action="/userrights" method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="type" value="activate">
|
||||||
|
<input type="hidden" name="userId" value="<%= user.id %>">
|
||||||
|
<button type="submit" class="btn btn-success">Activate</button>
|
||||||
|
</form>
|
||||||
|
<form action="/userrights" method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="type" value="delete">
|
||||||
|
<input type="hidden" name="userId" value="<%= user.id %>">
|
||||||
|
<button type="submit" class="btn btn-danger">Delete</button>
|
||||||
|
</form>
|
||||||
|
<% } else if (user.role === 'user') { %>
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#adminModal">
|
||||||
|
Admin
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal fade" id="adminModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="staticBackdropLabel">Zum Admin machen?</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="/userrights" method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="type" value="admin">
|
||||||
|
<input type="hidden" name="userId" value="<%= user.id %>">
|
||||||
|
<button type="submit" class="btn btn-success">Dauerhaft</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form action="/userrights" method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="type" value="admint">
|
||||||
|
<input type="hidden" name="userId" value="<%= user.id %>">
|
||||||
|
<button type="submit" class="btn btn-success">Temporär (24h)</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<%= message %>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Forgot Password</h1>
|
||||||
|
<form action="/send-password" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Send Reset Link</button>
|
||||||
|
</form>
|
||||||
|
<a href="/login">Back to Login</a>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<p>Du musst erst freigeschaltet werden um dich einloggen zu können.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Turnstunden Organisation</h1>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Login</h1>
|
||||||
|
|
||||||
|
<% if (locals.message) { %>
|
||||||
|
<p><%= message %></p>
|
||||||
|
<% } %>
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Login</button>
|
||||||
|
</form>
|
||||||
|
<a href="/register">Register</a>
|
||||||
|
<a href="/forgot-password">Forgot Password?</a>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,5 @@
|
||||||
|
</div>
|
||||||
|
<script src="/js/jquery.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,76 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Turnstunden WebApp</title>
|
||||||
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="/">Klaas</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<% if (session && ( session.role =='admin' || session.role =='user' )) { %>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/profile">Profil</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<% } else {%>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/register">Registrieren</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<% } %>
|
||||||
|
<% if (session && session.role === 'admin') { %>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="/admin">Admin</a></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<% if (session && ( session.role =='admin' || session.role =='user' )) { %>
|
||||||
|
<form class="d-flex" role="search" method="get" action="/logout">
|
||||||
|
<button class="btn btn-outline-success" type="submit">Logout</button>
|
||||||
|
</form>
|
||||||
|
<% } else { %>
|
||||||
|
<form class="d-flex" role="search" method="POST" action="/login">
|
||||||
|
<input type="text" class="form-control" placeholder="username" name="username">
|
||||||
|
<input type="password" class="form-control" placeholder="passwort" name="password">
|
||||||
|
<button class="btn btn-outline-success" type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||||
|
<symbol id="check-circle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="info-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
|
||||||
|
</symbol>
|
||||||
|
<symbol id="exclamation-triangle-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<% if (session.message){ %>
|
||||||
|
<% if (session && session.message[2] === 'success') { %>
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
</svg> <%= session.message[0] %> - <%= session.message[1] %>
|
||||||
|
</div>
|
||||||
|
<% session.message[2] = 'none' %>
|
||||||
|
<% } %>
|
||||||
|
<% if (session && session.message[2] === 'error') { %>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
</svg> <%= session.message[0] %> - <%= session.message[1] %>
|
||||||
|
</div>
|
||||||
|
<% session.message[2] = 'none' %>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Profile</h1>
|
||||||
|
<form action="/profile" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">New Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Update Profile</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Register</h1>
|
||||||
|
<form action="/register" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Register</button>
|
||||||
|
</form>
|
||||||
|
<a href="/login">Login</a>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<%- include('partials/header') %>
|
||||||
|
|
||||||
|
<h1>Reset Password</h1>
|
||||||
|
<form action="/reset-password/<%= token %>" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">New Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Reset Password</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<%- include('partials/footer') %>
|
Loading…
Reference in New Issue