Files
admin 01a7ac8e0b Initiale Arbeitszeiterfassung (Next.js 16 + SQLite)
Web-App zur Erfassung der Arbeitszeit, Layout/Architektur an Projekt logbuch
angelehnt, mit grüner Grundfarbe und SQLite3 statt MySQL.

- Eingabemaske: Datum (mit Wochentag), Ort (Kunde FFM/Homeoffice/Andrena),
  Beginn/Ende, Reisezeit, Vertriebsunterstützung (default 2h), Kommentar (max 500)
- Berechnung: Arbeitszeit = Ende-Beginn; Mehrarbeit = Arbeitszeit - 8h - Pause
- Pause als globale Einstellung (Einstellungen-Tab)
- Tabs Eingabe/Liste/Einstellungen, letzte 10 Einträge unter der Maske
- Single-User-Login (Benutzer Birgit + Passwort fest in .env), JWT-Cookie via jose
- better-sqlite3, Docker-Setup für docker.citysensor.de

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:53:44 +02:00

39 lines
1006 B
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// In-memory rate limiter funktioniert pro Prozess (single Docker container).
// Erlaubt MAX_ATTEMPTS Versuche pro IP innerhalb WINDOW_MS Millisekunden.
const MAX_ATTEMPTS = 10;
const WINDOW_MS = 15 * 60 * 1000; // 15 Minuten
interface Entry {
count: number;
resetAt: number;
}
const store = new Map<string, Entry>();
// Aufräumen abgelaufener Einträge alle 5 Minuten
setInterval(() => {
const now = Date.now();
for (const [key, entry] of store) {
if (entry.resetAt < now) store.delete(key);
}
}, 5 * 60 * 1000);
export function checkRateLimit(ip: string): { allowed: boolean; remainingMs: number } {
const now = Date.now();
const entry = store.get(ip);
if (!entry || entry.resetAt < now) {
store.set(ip, { count: 1, resetAt: now + WINDOW_MS });
return { allowed: true, remainingMs: 0 };
}
entry.count += 1;
if (entry.count > MAX_ATTEMPTS) {
return { allowed: false, remainingMs: entry.resetAt - now };
}
return { allowed: true, remainingMs: 0 };
}