Files
SDSStundenerfassung/checkin-server.js
2026-03-10 14:39:11 +01:00

165 lines
6.5 KiB
JavaScript

// Check-in Server (separater Express-App auf Port 3334)
const express = require('express');
const path = require('path');
const { db } = require('./database');
const { getCurrentDate, getCurrentTime, updateTotalHours, formatHoursMin } = require('./helpers/utils');
const checkinApp = express();
const CHECKIN_PORT = 3334;
// View-Engine für Browser-Aufrufe (Bestätigungsseiten)
checkinApp.set('view engine', 'ejs');
checkinApp.set('views', path.join(__dirname, 'views'));
// Middleware für Check-in-Server
checkinApp.use(express.json());
checkinApp.use(express.static('public'));
/** Erkennt Browser-Aufruf: Accept-Header enthält text/html (z. B. beim Aufruf per QR/Link im Browser). */
function wantsHtml(req) {
const accept = req.get('Accept') || '';
return /text\/html/i.test(accept);
}
/** Sendet je nach Client entweder HTML-Seite oder JSON. */
function sendResponse(req, res, success, data) {
if (wantsHtml(req)) {
const title = data.title || (success ? 'Erfolg' : 'Fehler');
const message = data.message || data.error || (success ? 'Aktion erfolgreich.' : 'Ein Fehler ist aufgetreten.');
if (!success && data.status) {
res.status(data.status);
}
res.render('checkin-result', { success, title, message });
} else {
if (success) {
res.json({ success: true, ...data });
} else {
res.status(data.status || 500).json({ success: false, error: data.error });
}
}
}
// API: Check-in (Kommen)
checkinApp.get('/api/checkin/:userId', (req, res) => {
const userId = parseInt(req.params.userId);
const currentDate = getCurrentDate();
const currentTime = getCurrentTime();
// Prüfe ob User existiert
db.get('SELECT id, default_break_minutes FROM users WHERE id = ?', [userId], (err, user) => {
if (err || !user) {
return sendResponse(req, res, false, { error: 'Benutzer nicht gefunden', status: 404 });
}
// Prüfe ob bereits ein Eintrag für heute existiert
db.get('SELECT * FROM timesheet_entries WHERE user_id = ? AND date = ? ORDER BY updated_at DESC, id DESC LIMIT 1',
[userId, currentDate], (err, entry) => {
if (err) {
return sendResponse(req, res, false, { error: 'Fehler beim Abrufen des Eintrags', status: 500 });
}
const successTitle = 'Hallo, du wurdest erfolgreich eingecheckt';
const userDefaultBreakMinutes = Number.isInteger(user?.default_break_minutes) && user.default_break_minutes >= 0
? user.default_break_minutes
: 30;
if (!entry) {
// Kein Eintrag existiert → Erstelle neuen mit start_time
db.run(`INSERT INTO timesheet_entries (user_id, date, start_time, break_minutes, updated_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`,
[userId, currentDate, currentTime, userDefaultBreakMinutes], (err) => {
if (err) {
return sendResponse(req, res, false, { error: 'Fehler beim Erstellen des Eintrags', status: 500 });
}
sendResponse(req, res, true, {
title: successTitle,
message: `Start-Zeit erfasst: ${currentTime}`,
start_time: currentTime,
date: currentDate
});
});
} else if (!entry.start_time) {
// Eintrag existiert, aber keine Start-Zeit → Setze start_time
db.run('UPDATE timesheet_entries SET start_time = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[currentTime, entry.id], (err) => {
if (err) {
return sendResponse(req, res, false, { error: 'Fehler beim Aktualisieren', status: 500 });
}
sendResponse(req, res, true, {
title: successTitle,
message: `Start-Zeit erfasst: ${currentTime}`,
start_time: currentTime,
date: currentDate
});
});
} else {
// Start-Zeit bereits vorhanden → Ignoriere weiteren Check-in
sendResponse(req, res, true, {
title: successTitle,
message: `Bereits eingecheckt um ${entry.start_time}. Check-in ignoriert.`,
start_time: entry.start_time,
date: currentDate
});
}
});
});
});
// API: Check-out (Gehen)
checkinApp.get('/api/checkout/:userId', (req, res) => {
const userId = parseInt(req.params.userId);
const currentDate = getCurrentDate();
const currentTime = getCurrentTime();
// Prüfe ob User existiert
db.get('SELECT id FROM users WHERE id = ?', [userId], (err, user) => {
if (err || !user) {
return sendResponse(req, res, false, { error: 'Benutzer nicht gefunden', status: 404 });
}
// Prüfe ob bereits ein Eintrag für heute existiert
db.get('SELECT * FROM timesheet_entries WHERE user_id = ? AND date = ? ORDER BY updated_at DESC, id DESC LIMIT 1',
[userId, currentDate], (err, entry) => {
if (err) {
return sendResponse(req, res, false, { error: 'Fehler beim Abrufen des Eintrags', status: 500 });
}
if (!entry || !entry.start_time) {
return sendResponse(req, res, false, {
error: 'Bitte zuerst einchecken (Kommen).',
status: 400
});
}
// Berechne total_hours basierend auf start_time, end_time und break_minutes
const breakMinutes = entry.break_minutes || 0;
const totalHours = updateTotalHours(entry.start_time, currentTime, breakMinutes);
// Setze end_time (überschreibt vorherige End-Zeit falls vorhanden)
db.run('UPDATE timesheet_entries SET end_time = ?, total_hours = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[currentTime, totalHours, entry.id], (err) => {
if (err) {
return sendResponse(req, res, false, { error: 'Fehler beim Aktualisieren', status: 500 });
}
const successTitle = 'Schönen Feierabend, du wurdest erfolgreich ausgecheckt';
const successMessage = `End-Zeit erfasst: ${currentTime}. Gesamtstunden: ${formatHoursMin(totalHours)}`;
sendResponse(req, res, true, {
title: successTitle,
message: successMessage,
end_time: currentTime,
total_hours: totalHours,
date: currentDate
});
});
});
});
});
// Check-in-Server starten (auf Port 3334)
checkinApp.listen(CHECKIN_PORT, () => {
console.log(`Check-in Server läuft auf http://localhost:${CHECKIN_PORT}`);
});
module.exports = checkinApp;