This commit is contained in:
2026-06-30 20:08:11 +00:00
parent d65a084614
commit d0f734cf36
3 changed files with 170 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
const express = require('express');
const router = express.Router();
router.get('/login', (req, res) => {
if (req.session.authenticated) return res.redirect('/');
res.render('login', { error: null, title: 'Anmelden' });
});
router.post('/login', (req, res) => {
const { username, password } = req.body;
const validUser = process.env.APP_USERNAME || 'admin';
const validPass = process.env.APP_PASSWORD || 'changeme';
if (username === validUser && password === validPass) {
req.session.authenticated = true;
req.session.username = username;
return res.redirect('/');
}
res.render('login', {
error: 'Benutzername oder Passwort ist falsch.',
title: 'Anmelden',
});
});
router.post('/logout', (req, res) => {
req.session.destroy(() => {
res.redirect('/login');
});
});
module.exports = router;
+101
View File
@@ -0,0 +1,101 @@
const express = require('express');
const router = express.Router();
const { requireAuth } = require('../middleware/auth');
const TimeEntry = require('../models/TimeEntry');
const Settings = require('../models/Settings');
const {
computeWorkedMinutes,
computeBalanceMinutes,
formatBalance,
minutesToHM,
formatDateDisplay,
formatMonthDisplay,
} = require('../utils/time');
router.get('/', requireAuth, async (req, res) => {
const settings = await Settings.getSingleton();
const entries = await TimeEntry.find().sort({ date: -1 }).limit(90).lean();
const totalAgg = await TimeEntry.aggregate([
{ $group: { _id: null, total: { $sum: '$balanceMinutes' } } },
]);
const totalBalanceMinutes = settings.startingBalanceMinutes + (totalAgg[0]?.total || 0);
const monthlyAgg = await TimeEntry.aggregate([
{
$group: {
_id: { $substrCP: ['$date', 0, 7] },
balance: { $sum: '$balanceMinutes' },
worked: { $sum: '$workedMinutes' },
days: { $sum: 1 },
},
},
{ $sort: { _id: -1 } },
{ $limit: 12 },
]);
let editEntry = null;
if (req.query.edit) {
editEntry = await TimeEntry.findOne({ date: req.query.edit }).lean();
}
res.render('dashboard', {
title: 'Übersicht',
entries,
settings,
totalBalanceMinutes,
monthlyAgg,
editEntry,
formatBalance,
minutesToHM,
formatDateDisplay,
formatMonthDisplay,
error: req.query.error || null,
});
});
router.post('/entries', requireAuth, async (req, res) => {
try {
const { date, startTime, endTime, breakMinutes, note } = req.body;
if (!date || !startTime || !endTime) {
return res.redirect('/?error=Bitte+Datum%2C+Start-+und+Endzeit+angeben.');
}
const workedMinutes = computeWorkedMinutes(startTime, endTime, breakMinutes);
if (workedMinutes < 0) {
return res.redirect('/?error=Die+Endzeit+muss+nach+der+Startzeit+liegen.');
}
const settings = await Settings.getSingleton();
const balanceMinutes = computeBalanceMinutes(workedMinutes, date, settings);
await TimeEntry.findOneAndUpdate(
{ date },
{
date,
startTime,
endTime,
breakMinutes: Number(breakMinutes || 0),
note: note || '',
workedMinutes,
balanceMinutes,
},
{ upsert: true, new: true }
);
res.redirect('/');
} catch (err) {
console.error(err);
res.redirect('/?error=Beim+Speichern+ist+ein+Fehler+aufgetreten.');
}
});
router.post('/entries/:id/delete', requireAuth, async (req, res) => {
await TimeEntry.findByIdAndDelete(req.params.id);
res.redirect('/');
});
module.exports = router;
+36
View File
@@ -0,0 +1,36 @@
const express = require('express');
const router = express.Router();
const { requireAuth } = require('../middleware/auth');
const Settings = require('../models/Settings');
const { WEEKDAY_NAMES } = require('../utils/time');
router.get('/settings', requireAuth, async (req, res) => {
const settings = await Settings.getSingleton();
res.render('settings', {
title: 'Einstellungen',
settings,
weekdayNames: WEEKDAY_NAMES,
saved: req.query.saved === '1',
});
});
router.post('/settings', requireAuth, async (req, res) => {
const { targetHoursPerDay, startingBalanceHours, workDays } = req.body;
const days = Array.isArray(workDays)
? workDays.map(Number)
: workDays
? [Number(workDays)]
: [];
const settings = await Settings.getSingleton();
settings.targetHoursPerDay = Number(targetHoursPerDay) || 0;
settings.workDays = days;
settings.startingBalanceMinutes = Math.round(Number(startingBalanceHours || 0) * 60);
await settings.save();
res.redirect('/settings?saved=1');
});
module.exports = router;