wannpassts/README.md
Tronax f220d96e3b
feat(frontend): add dark/light theme toggle with system detection
- Add ThemeToggle component (Auto / Dark / Light) to all views
- Store preference per browser and detect system preference live
  via prefers-color-scheme
- Apply theme before first render in index.html to prevent flash
- Extend CSS with light theme variables and refactor glass styles
2026-08-25 18:59:12 +02:00

132 lines
6.7 KiB
Markdown
Raw 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.

# WannPassts
**Deine freien Zeiten geteilt per Link. Ohne deine Termine preiszugeben.**
WannPassts ist eine Self-Hosted-Buchungs-App (à la Cal.com / Calendly, aber privat):
Du verbindest deine Kalender, teilst einen Link, und andere sehen nur **frei / belegt**
und stellen Buchungsanfragen. Termintitel, Orte und Beschreibungen verlassen deine
Kalender nie.
## Features
- 🔐 **Registrierung/Login** (JWT, bcrypt-Passwort-Hashes)
- 📆 **Kalender-Connect:**
- **Google Kalender** per OAuth nutzt die FreeBusy-API, die *strukturell* keine Termindetails liefert
- **iCloud** & andere (Fastmail, Nextcloud, …) per **CalDAV** iCloud mit App-spezifischem Passwort
- **Beliebige ICS-/webcal-Links** (z. B. Google-„Private Adresse“, Ferien-/Schichtpläne)
- 🔗 **Öffentliche Buchungsseite** (`/b/<slug>`): freie Slots im von dir definierten Zeitfenster, Wochentage, Slot-Raster und Dauern **niemals Termindetails**
- 📨 **Buchungsanfragen** annehmen/ablehnen; angenommene Zeiten werden belegt und verhindern Doppelbuchungen
- ⚙️ Buchungsregeln: Zeitfenster, Slot-Raster (1060 Min), Dauern, Wochentage, Horizont (1120 Tage), Zeitzone
- 🧊 **Liquid-Glass-UI** mit **Dark-/Light-Mode**: Umschalter (Auto / Dunkel / Hell) auf jeder Seite, automatische Systemerkennung (`prefers-color-scheme`) inkl. Live-Wechsel, Auswahl wird pro Browser gespeichert
- 🔒 Provider-Tokens werden **AES-256-GCM-verschlüsselt** in der SQLite-DB gespeichert
## Tech Stack
| Teil | Technologie |
| -------- | ------------------------------------------------------------------ |
| Frontend | Vue 3 + TypeScript, Vite, Pinia, Vue Router (kein UI-Framework) |
| Backend | Go, chi, JWT, SQLite (modernc, CGO-frei), x/oauth2, go-webdav |
## Schnellstart (Entwicklung)
```bash
# Terminal 1 Backend (http://localhost:8080)
cd backend
cp .env.example .env # optional anpassen; ohne .env läuft es mit Defaults
go run ./cmd/server
# Terminal 2 Frontend (http://localhost:5173, Proxied /api → 8080)
cd frontend
npm install
npm run dev
```
Dann http://localhost:5173 öffnen, Konto erstellen, Kalender verbinden, Link teilen. 🎉
## Produktion (ein einziger Go-Prozess)
```bash
cd frontend && npm run build && cd ..
cd backend
STATIC_DIR=../frontend/dist \
APP_URL=https://deine-domain.de \
FRONTEND_URL=https://deine-domain.de \
JWT_SECRET=$(openssl rand -hex 32) \
ENCRYPTION_KEY=$(openssl rand -hex 32) \
go run ./cmd/server
```
Der Go-Server liefert dann das Frontend (`dist/`) **und** die API über einen Port.
## Kalender-Anbindung einrichten
### Google
1. [Google Cloud Console](https://console.cloud.google.com): Projekt anlegen, **Google Calendar API** aktivieren.
2. OAuth-Zustimmungsbildschirm (External) einrichten, Scope `calendar.readonly`.
3. **OAuth-Client-ID** (Webanwendung) erstellen; als autorisierte Redirect-URI
`{APP_URL}/api/calendars/google/callback` eintragen (lokal: `http://localhost:8080/api/calendars/google/callback`).
4. `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` in `backend/.env` setzen.
Beim Verbinden wird nur der **Primärkalender** angebunden; gelesen wird ausschließlich Free/Busy.
### iCloud
1. Auf [appleid.apple.com](https://appleid.apple.com/account/manage) → *Anmeldung und Sicherheit***App-spezifisches Passwort** erzeugen.
2. In WannPassts: *Kalender → iCloud / CalDAV*:
- Server: `https://caldav.icloud.com`
- Benutzername: Apple-ID (E-Mail)
- Passwort: das App-spezifische Passwort (nicht das normale!)
3. Der erste Kalender mit Terminen wird automatisch erkannt (Kalender-Pfad ist optional manuell überschreibbar).
### Andere (Fastmail, Nextcloud, GMX, …)
Gleiche CalDAV-Maske mit dem jeweiligen CalDAV-Server des Anbieters, oder beliebige **ICS-Links** abonnieren.
## Datenschutz-Design
- Besucher sehen ausschließlich **Start/Ende von busy-Zeiträumen** die API hat gar keine Felder für Titel & Co.
- Google: FreeBusy-API liefert von sich aus nur Belegungszeiträume.
- CalDAV/ICS: Termine werden serverseitig auf `DTSTART/DTEND` reduziert, abgesagte und „Verfügbar“-Termine (TRANSPARENT) werden ignoriert.
- Tokens/Passwörter der Verbindungen liegen verschlüsselt (AES-GCM aus `ENCRYPTION_KEY`) in der DB.
- Sync erfolgt bedarfsgesteuert (max. alle 5 Min) beim Aufruf der Buchungsseite plus manueller Button.
## Bekannte Grenzen (MVP)
- **Kein E-Mail-Versand** Anfragen/Freigaben erscheinen nur im Dashboard (Mail-Versand via SMTP wäre der nächste Ausbauschritt).
- **ICS-Abos** expandieren Serientermine nicht**; CalDAV und Google tun dies serverseitig korrekt.
- **Ausstehende (pending) Anfragen blockieren noch keine Zeit** erst angenommene. Bei zwei parallelen Anfragen für denselben Slot gewinnt, wer zuerst angenommen wird (Kollision wird beim Annehmen geprüft).
- Ein Google-Konto verbindet aktuell den Primärkalender; weitere Kalender lassen sich als ICS-Abo hinzufügen.
## Projektstruktur
```
backend/
cmd/server/ # Einstieg
internal/config/ # Env-/Secret-Handling
internal/db/ # SQLite-Schema & Queries
internal/crypto/ # AES-GCM für gespeicherte Provider-Tokens
internal/calendar/ # Provider: google.go, caldav.go, ics.go (+ Syncer)
internal/httpapi/ # Router, Middleware, Handler (auth, calendars, bookings, public)
frontend/
src/lib/ # api-Client, Typen, Zeitzonen-Helfer
src/stores/ # Pinia: auth, toast
src/views/ # Landing, Login, Register, Dashboard, Booking (/b/:slug)
src/components/ # GlassCard, ShareLink, Sektionen (Kalender/Anfragen/Einstellungen)
src/styles/main.css # Liquid-Glass-Dark-Designsystem
```
## API-Überblick
| Methode | Pfad | Auth | Zweck |
| ------------------- | --------------------------------- | ---- | ---------------------------------------- |
| POST | `/api/auth/register` \| `/login` | | Konto / JWT |
| GET/PATCH | `/api/me` | ✅ | Profil & Buchungsregeln |
| POST | `/api/me/slug` | ✅ | Neuen Buchungslink erzeugen |
| GET/DELETE | `/api/calendars…` | ✅ | Verbinden (google/caldav/ics), verwalten, sync |
| GET | `/api/public/{slug}` | | Buchungsseite: Regeln + busy-Zeiträume |
| POST | `/api/public/{slug}/bookings` | | Buchungsanfrage stellen |
| GET | `/api/bookings`, `…/accept\|decline` | ✅ | Anfragen verwalten |
## Tests
```bash
cd backend && go test ./... # ICS-Parser, Dauer-Handling, Interval-Clamping
```