# Slim API Verbesserungen — Implementierung Plan > **Für Hermes:** Mit subagent-driven-development skill umsetzen, task für task. **Ziel:** Produktionsreife Slim API für fahrschultermin.de — Security, Robustheit, Multi-Tenant Isolation **Architektur:** - Frontend (React) → `fahrschultermin.de` → API Calls direkt an `api.fahrschultermin.de` - API (Slim 4) → PostgreSQL auf `127.0.0.1:5433` - JWT Bearer Token Auth - Multi-Tenant via `tenant_id` Filter in allen Queries **Tech Stack:** Slim 4, Eloquent ORM, Firebase JWT, PHP-DI --- ## Task 1: JWT Secret in `.env` auslagern **Ziel:** Hardcoded Secret durch Environment Variable ersetzen **Dateien:** - Erstellen: `api/.env` - Modifizieren: `api/src/Config/Container.php:45` **Schritt 1: `.env` Datei erstellen** ```bash echo 'JWT_SECRET=your-256-bit-secret-change-in-production' > api/.env ``` **Schritt 2: Container.php anpassen** Ersetze in `Container.php` Zeile 45: ```php 'jwt.secret' => 'your-256-bit-secret-change-in-production', ``` mit: ```php 'jwt.secret' => $_ENV['JWT_SECRET'] ?? throw new \RuntimeException('JWT_SECRET not set'), ``` **Schritt 3: Testen** ```bash ssh -p 2225 fahrschuldesk@fahrschultermin.de "cd /srv/web/fahrschuldesk/domains/api.fahrschultermin.de && ls .env && echo 'JWT_SECRET defined'" ``` Erwartet: `JWT_SECRET defined` **Schritt 4: Commit** ```bash git add api/.env api/src/Config/Container.php git commit -m "security: move JWT secret to .env" git push ``` --- ## Task 2: JWT Secret generieren & auf Server deployen **Ziel:** Echten 256-bit Secret generieren und sicher auf Server bringen **Schritt 1: Secret generieren** ```bash openssl rand -base64 32 ``` **Schritt 2: Secret in Git encrypted speichern** ```bash # Neues Secret als GitHub Secret oder verschlüsselt speichern # Hier: Nur auf Server setzen via ssh ssh -p 2225 fahrschuldesk@fahrschultermin.de 'echo "JWT_SECRET=$(openssl rand -base64 32)" >> /srv/web/fahrschuldesk/domains/api.fahrschultermin.de/.env' ``` **Schritt 3: Verifizieren** ```bash ssh -p 2225 fahrschuldesk@fahrschultermin.de 'grep JWT_SECRET /srv/web/fahrschuldesk/domains/api.fahrschultermin.de/.env' ``` Erwartet: `JWT_SECRET=` **Schritt 4: Commit** ```bash git add api/.env.example git commit -m "security: add .env.example with JWT_SECRET placeholder" git push ``` --- ## Task 3: Multi-Tenant Isolation — Tenant ID in allen Queries **Ziel:** Sicherstellen dass jede Query nur Daten des eigenen Tenants zurückgibt **Dateien:** - Modifizieren: `api/src/Controllers/AppointmentsController.php` - Modifizieren: `api/src/Controllers/StudentsController.php` - Modifizieren: `api/src/Controllers/InstructorsController.php` - Modifizieren: `api/src/Controllers/LessonTypesController.php` - Modifizieren: `api/src/Controllers/TemplatesController.php` **Schritt 1: Prüfen dass alle Controller `tenant_id` nutzen** Alle Controller müssen bei `index()`, `store()`, `update()`, `destroy()` den tenant_id Filter haben. **Schritt 2: BootstrapController prüfen** (hat bereits tenant isolation) **Schritt 3: Commit** ```bash git add api/src/Controllers/ git commit -m "security: ensure tenant_id filtering on all endpoints" git push ``` --- ## Task 4: Password Hash System verstehen und fixen **Ziel:** Verstehen warum lokales `password_verify` FAIL gab, Server OK **Schritt 1: Test-Script schreiben** ```php where(function($q) use ($start, $end) { $q->whereBetween('start_at', [$start, $end]) ->orWhereBetween('end_at', [$start, $end]) ->orWhere(function($q2) use ($start, $end) { $q2->where('start_at', '<=', $start)->where('end_at', '>=', $end); }); }) ->where('status', '!=', 'cancelled'); if ($excludeId) { $query->where('id', '!=', $excludeId); } return $query->exists(); } ``` **Schritt 2: Store und Update anpassen** Vor dem Erstellen: `if (Appointment::hasConflict($data['instructor_id'], $data['start_at'], $data['end_at'])) { return $this->json($response, ['message' => 'Instructor conflict'], 409); }` **Schritt 3: Commit** ```bash git add api/src/Controllers/AppointmentsController.php api/src/Models/Appointment.php git commit -m "feat: add conflict detection for instructor scheduling" git push ``` --- ## Task 6: Frontend → API Integration testen **Ziel:** Verify dass React App mit neuer Slim API funktioniert **Schritt 1: API Endpoints testen mit curl** ```bash # Login TOKEN=$(curl -s -X POST https://api.fahrschultermin.de/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@nordring.test","password":"Tanja82"}' | jq -r '.token') echo "Token: ${TOKEN:0:20}..." # Bootstrap curl -s https://api.fahrschultermin.de/api/v1/bootstrap \ -H "Authorization: Bearer $TOKEN" | jq '.tenant.name' # Students curl -s https://api.fahrschultermin.de/api/v1/students \ -H "Authorization: Bearer $TOKEN" | jq 'length' ``` **Schritt 2: Browser testen** (manuell) - Öffne https://fahrschultermin.de - Login mit admin@nordring.test / Tanja82 - Prüfe Console Errors - Prüfe Network Tab → api.fahrschultermin.de calls **Schritt 3: Commit** ```bash git add -A && git commit -m "test: verify frontend API integration" git push ``` --- ## Task 7: API Documentation mit README **Ziel:** API dokumentieren für zukünftige Entwicklung **Dateien:** - Erstellen: `api/README.md` **Schritt 1: README schreiben** ```markdown # DriveTime Planner API ## Base URL `https://api.fahrschultermin.de/api/v1` ## Authentication JWT Bearer Token ``` Authorization: Bearer ``` ## Endpoints ### Auth - `POST /auth/login` — Login mit email/password - `POST /auth/logout` — Logout (client-side token verwerfen) - `GET /auth/me` — Aktueller User (requires auth) ### Bootstrap - `GET /bootstrap` — Vollständige Daten für Frontend (requires auth) ### CRUD - `GET/POST /students` — Schüler Liste/Anlegen - `PATCH/DELETE /students/{id}` — Schüler Ändern/Löschen - `GET/POST /instructors` — Fahrlehrer Liste/Anlegen - `PATCH /instructors/{id}` — Fahrlehrer Ändern - `GET/POST /appointments` — Termine Liste/Anlegen - `PATCH/DELETE /appointments/{id}` — Termine Ändern/Löschen - `GET /lesson-types` — Unterrichtstypen - `GET /license-class-templates` — Führerschein-Klassen ## Error Responses - 400: Bad Request - 401: Unauthorized - 404: Not Found - 409: Conflict (z.B. Terminkonflikt) - 500: Server Error ## Environment Variables - `JWT_SECRET`: 256-bit secret für JWT signing ``` **Schritt 2: Commit** ```bash git add api/README.md git commit -m "docs: add API documentation" git push ``` --- ## Task 8: Deployment Script erstellen **Ziel:** Ein Script das API auf Server deployed mit `composer install` **Dateien:** - Erstellen: `scripts/deploy_api.sh` **Schritt 1: Script schreiben** ```bash #!/bin/bash set -e API_DIR="/srv/web/fahrschuldesk/domains/api.fahrschultermin.de" echo "=== Deploying Slim API ===" # Rsync rsync -avz --delete -e "ssh -p 2225" \ /home/admin-drive-time-planer/drivetimeplaner/api/ \ fahrschuldesk@fahrschultermin.de:$API_DIR/ # Composer install ssh -p 2225 fahrschuldesk@fahrschultermin.de "cd $API_DIR && composer install --no-dev --optimize-autoloader" echo "=== Deployment complete ===" ``` **Schritt 2: Commit** ```bash git add scripts/deploy_api.sh git commit -m "script: add API deployment script" git push ``` --- ## Reihenfolge 1. Task 1: JWT Secret auslagern 2. Task 2: Secret generieren & deployen 3. Task 3: Multi-Tenant Isolation prüfen 4. Task 4: Password Hash verstehen 5. Task 5: Konflikterkennung (wichtig für Produktion) 6. Task 6: Frontend → API testen 7. Task 7: API Docs 8. Task 8: Deployment Script