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) und USER (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

  1. Erste Registrierung unter /register → der erste Benutzer wird automatisch ADMIN
  2. Im Dashboard einen Kalender anlegen
  3. Unter „Benutzer einladen" einen Invite-Link erstellen und verschicken
  4. 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.

Description
No description provided
Readme 244 KiB
Languages
TypeScript 89.8%
JavaScript 8.7%
Dockerfile 1.1%
Shell 0.2%
CSS 0.2%