Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Kalendartool
Einfaches Kalender- und Reservierungstool nach plan.md: Ein Admin legt Kalender an und lädt Benutzer per Invite-Link ein. Jeder User sieht ausschließlich seinen einen Kalender und kann dort Reservierungen erstellen.
Kernfunktionen (MVP)
- Auth: Registrierung, Login, Logout, Passwort vergessen – bcrypt-Hashing, signierte HttpOnly-Session-Cookies, Rate-Limiting beim Login
- Rollen:
ADMIN(0..n Kalender) undUSER(genau 1 Kalender) – serverseitig erzwungen - Kalender: Titel, Beschreibung, Zeitzone, aktiv/inaktiv
- Invite-System: nicht erratbare Tokens (32 Bytes), nur SHA-256-Hash in der DB, Ablaufdatum, Übersicht Aktiv/Abgelaufen/Verwendet
- Reservierungen: Startzeit + Dauer (15-Minuten-Schritte), Titel, Notiz
- Doppelbuchungsschutz: atomare Prüfung in serialisierbarer Transaktion (HTTP 409 bei Kollision)
- Ansichten: Tag / Woche / Monat
- Sicherheit: zentrale Berechtigungsschicht (
lib/permissions), Zod-Validierung aller Eingaben, Middleware-Schutz, keine Account-Enumeration
Tech-Stack
Next.js 14 (App Router, TypeScript) · PostgreSQL · Prisma · Tailwind CSS · Docker · Vitest
Schnellstart mit Docker (empfohlen)
# 1. Session-Secret erzeugen und .env anlegen
cp .env.example .env
# SESSION_SECRET in .env setzen (mindestens 32 Zeichen):
node -e "console.log('SESSION_SECRET=' + require('crypto').randomBytes(48).toString('base64url'))"
# 2. Stack starten (PostgreSQL + App)
docker compose up --build
Die App läuft dann auf http://localhost:3000. Migrationen werden beim Containerstart automatisch angewendet (prisma migrate deploy).
Schnellstart ohne Docker
npm install
cp .env.example .env # DATABASE_URL und SESSION_SECRET anpassen
npx prisma db push # Schema in die Datenbank schreiben
npm run dev
Voraussetzung: laufendes PostgreSQL (z. B. nur der db-Service aus docker-compose.yml: docker compose up -d db).
Erste Schritte
- Erste Registrierung unter
/register→ der erste Benutzer wird automatisch ADMIN - Im Dashboard einen Kalender anlegen
- Unter „Benutzer einladen" einen Invite-Link erstellen und verschicken
- Eingeladene Personen registrieren sich über den Link und sehen sofort nur diesen einen Kalender
Projektstruktur
app/ Next.js App Router (Seiten + API-Routen)
api/auth/ register, login, logout, forgot-password
api/calendars/ CRUD + Reservierungen + Invites
api/reservations/ Bearbeiten/Löschen (atomar)
dashboard/ User: 1 Kalender · Admin: alle Kalender
calendar/[id]/ Ansicht Tag/Woche/Monat
invite/[token]/ Invite-Annahme
admin/ Kalenderverwaltung
components/ UI-, Kalender-, Reservierungs-, Admin-Komponenten
lib/
auth/ Session (JWT-Cookies), Passwort-Hashing
permissions/ zentrale Berechtigungsschicht
reservations/ Konfliktlogik + atomarer Service
invites/ Token-Erzeugung/Hashing
api/ Response-Helfer, Kontext-Lader
validation.ts Zod-Schemas
prisma/schema.prisma Datenmodell
tests/ Unit-Tests (Kollisionen, Invites, Rechte)
Tests
npm test
Getestet werden insbesondere die Kollisionslogik (Überlappungsvarianten aus plan.md Abschnitt 6), Invite-Token-Hashing/-Ablauf, Eingabevalidierung und die komplette Berechtigungsmatrix.
Sicherheitskonzept (Kurzfassung)
| Thema | Umsetzung |
|---|---|
| Doppelbuchungen | SERIALIZABLE-Transaktion in lib/reservations/service.ts |
| Berechtigungen | zentrale Funktionen in lib/permissions/permissions.ts, geprüft in jeder API-Route |
| Invite-Tokens | 32 Byte Zufall, nur SHA-256-Hash in der DB |
| Passwörter | bcrypt (12 Runden), nie im Klartext |
| Sessions | HS256-signierte JWTs in HttpOnly/SameSite-Cookies |
| Login-Missbrauch | Rate-Limiting (5 Versuche / 15 min) |
| Account-Enumeration | generische Fehler, /forgot-password antwortet immer 200 |
| Eingaben | Zod-Schemas mit Längen-/Formatgrenzen |
Bewusste MVP-Grenzen (plan.md Abschnitt 18)
Buchungszeitfenster-Regeln, E-Mail-Versand, ICS-Export, wiederkehrende Reservierungen und Statistiken sind bewusst nicht Teil von Version 1 – die Struktur (Service-Schicht, Regeln in lib/) ist darauf vorbereitet.