initial
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user