119 lines
5.3 KiB
Markdown
119 lines
5.3 KiB
Markdown
# Kalendartool
|
||
|
||
Einfaches Kalender- und Reservierungstool: Ein Admin legt Kalender an und ordnet vorhandene Benutzer direkt zu. 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
|
||
- **Mitglieder**: Administratoren ordnen vorhandene Benutzer direkt einem Kalender zu
|
||
- **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
|
||
|
||
## MPM Marketplace
|
||
|
||
Das Repository ist als MPM-Modul vorbereitet. `module.json` beschreibt das
|
||
Modul und seine Konfigurationsfelder; `compose.yml` enthält App, PostgreSQL
|
||
und tägliche Backups. Die Modul-App veröffentlicht keinen Host-Port. MPM
|
||
erreicht sie intern über Port `41030` und die Health-Route `/health`.
|
||
|
||
Die Anmeldung und Rollen kommen im MPM-Betrieb signiert vom MPM-Gateway.
|
||
Die lokale Cookie-Anmeldung bleibt für den eigenständigen Betrieb erhalten.
|
||
Kalender und Reservierungen liegen in PostgreSQL. Die benannten Volumes
|
||
`pgdata` und `pgbackups` bleiben beim Entfernen des Moduls erhalten.
|
||
|
||
Bei der ersten MPM-Konfiguration werden zwei getrennte, zufällige Passwörter
|
||
für PostgreSQL-Administration und App-Zugriff benötigt. Die Anmeldung läuft
|
||
über MPM; lokale Konten und Einladungslinks sind im MPM-Betrieb deaktiviert.
|
||
|
||
Benutzer und Modulzugriff werden in MPM verwaltet. Öffne das Modul zunächst
|
||
einmal mit dem neuen Benutzer, damit sein lokales Profil angelegt wird; ein
|
||
Kalenderadministrator kann ihn danach direkt einem Kalender zuordnen. Die
|
||
Datenbank-Initialisierung wird nur bei einem leeren Volume ausgeführt. Ein
|
||
späteres Ändern der Datenbankpasswörter im MPM-Konfigurationsdialog ändert die
|
||
Zugangsdaten eines bestehenden PostgreSQL-Volumes nicht automatisch.
|
||
|
||
## Schnellstart mit Docker (eigenständiger Betrieb)
|
||
|
||
```bash
|
||
# 1. Session-Secret erzeugen und .env anlegen
|
||
cp .env.example .env
|
||
# CALENDAR_ADMIN_PASSWORD, CALENDAR_DB_PASSWORD und SESSION_SECRET in .env
|
||
# durch zufällige Werte ersetzen. SESSION_SECRET braucht mindestens 32 Zeichen:
|
||
node -e "console.log('SESSION_SECRET=' + require('crypto').randomBytes(48).toString('base64url'))"
|
||
|
||
# 2. Stack starten (PostgreSQL + App)
|
||
docker compose -f docker-compose.yml up --build
|
||
```
|
||
|
||
Die App läuft dann auf http://localhost:3000. Migrationen werden beim Containerstart automatisch angewendet (`prisma migrate deploy`).
|
||
|
||
## Schnellstart ohne Docker
|
||
|
||
```bash
|
||
npm install
|
||
cp .env.example .env # Passwörter 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. Im Kalender unter „Mitglieder" vorhandene Benutzer zuordnen
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
app/ Next.js App Router (Seiten + API-Routen)
|
||
api/auth/ register, login, logout, forgot-password
|
||
api/calendars/ CRUD + Reservierungen + Mitglieder
|
||
api/reservations/ Bearbeiten/Löschen (atomar)
|
||
dashboard/ User: 1 Kalender · Admin: alle Kalender
|
||
calendar/[id]/ Ansicht Tag/Woche/Monat
|
||
admin/ Kalenderverwaltung
|
||
components/ UI-, Kalender-, Reservierungs-, Admin-Komponenten
|
||
lib/
|
||
auth/ Session (JWT-Cookies), Passwort-Hashing
|
||
permissions/ zentrale Berechtigungsschicht
|
||
reservations/ Konfliktlogik + atomarer Service
|
||
api/ Response-Helfer, Kontext-Lader
|
||
validation.ts Zod-Schemas
|
||
prisma/schema.prisma Datenmodell
|
||
tests/ Unit-Tests (Kollisionen, Validierung, Rechte)
|
||
```
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
npm test
|
||
```
|
||
|
||
Getestet werden insbesondere die **Kollisionslogik** (Überlappungsvarianten aus plan.md Abschnitt 6), 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 |
|
||
| 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.
|