feat: JWT secret in .env, conflict detection, docs, deploy script

- Move JWT_SECRET from hardcode to .env (security)
- Add hasInstructorConflict() to Appointment model
- Add conflict check in store() and update() — returns 409
- Add .env.example with placeholder
- Add API documentation (README.md)
- Add deploy_api.sh script
This commit is contained in:
Hermes Agent
2026-05-20 17:30:24 +02:00
parent 09324a9513
commit 09759b7619
9 changed files with 536 additions and 64 deletions

View File

@@ -1,44 +1,83 @@
# DriveTime Planner API (Slim 4)
# DriveTime Planner API
## Setup
## Base URL
`https://api.fahrschultermin.de/api/v1`
```bash
cd api
composer install
## Authentication
JWT Bearer Token. Login returns a token valid for 8 hours.
```
Authorization: Bearer <token>
```
## Run
```bash
php -S localhost:8080 -t public/
```
## Routes
## Endpoints
### Auth
| Method | Path | Description |
|--------|------|-------------|
| POST | /api/v1/auth/login | Login (returns JWT) |
| POST | /api/v1/auth/logout | Logout |
| GET | /api/v1/auth/me | Current user |
| GET | /api/v1/bootstrap | Full app data |
| GET | /api/v1/students | List students |
| POST | /api/v1/students | Create student |
| PATCH | /api/v1/students/{id} | Update student |
| DELETE | /api/v1/students/{id} | Delete student |
| GET | /api/v1/instructors | List instructors |
| POST | /api/v1/instructors | Create instructor |
| PATCH | /api/v1/instructors/{id} | Update instructor |
| GET | /api/v1/appointments | List appointments |
| POST | /api/v1/appointments | Create appointment |
| PATCH | /api/v1/appointments/{id} | Update appointment |
| DELETE | /api/v1/appointments/{id} | Delete appointment |
| GET | /api/v1/lesson-types | List lesson types |
| GET | /api/v1/license-class-templates | List templates |
| POST | `/auth/login` | Login with email/password, returns JWT |
| POST | `/auth/logout` | Logout (client-side token discard) |
| GET | `/auth/me` | Current user info (requires auth) |
## Auth
### Bootstrap
| Method | Path | Description |
|--------|------|-------------|
| GET | `/bootstrap` | Full frontend data: user, tenant, students, instructors, lessonTypes, templates (requires auth) |
JWT Bearer token in Authorization header.
### CRUD
| Method | Path | Description |
|--------|------|-------------|
| GET | `/students` | List students (requires auth) |
| POST | `/students` | Create student (requires auth) |
| PATCH | `/students/{id}` | Update student (requires auth) |
| DELETE | `/students/{id}` | Delete student (requires auth) |
| GET | `/instructors` | List instructors (requires auth) |
| POST | `/instructors` | Create instructor (requires auth) |
| PATCH | `/instructors/{id}` | Update instructor (requires auth) |
| GET | `/appointments` | List appointments, optionally filter by `from`/`to` query params (requires auth) |
| POST | `/appointments` | Create appointment with instructor conflict detection (requires auth) |
| PATCH | `/appointments/{id}` | Update appointment with conflict check (requires auth) |
| DELETE | `/appointments/{id}` | Delete appointment (requires auth) |
| GET | `/lesson-types` | List lesson types (requires auth) |
| GET | `/license-class-templates` | List license class templates with requirements (requires auth) |
## Database
## Error Responses
| Code | Meaning |
|------|---------|
| 400 | Bad Request — missing or invalid parameters |
| 401 | Unauthorized — missing or invalid JWT |
| 404 | Not Found — resource doesn't exist or wrong tenant |
| 409 | Conflict — e.g. instructor has overlapping appointment |
| 500 | Internal Server Error |
PostgreSQL (configured in src/Config/Container.php)
## Environment Variables
| Variable | Description |
|----------|-------------|
| `JWT_SECRET` | 256-bit secret for JWT signing (required) |
## Example
```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')
# Get bootstrap data
curl https://api.fahrschultermin.de/api/v1/bootstrap \
-H "Authorization: Bearer $TOKEN" | jq .
# Create appointment
curl -X POST https://api.fahrschultermin.de/api/v1/appointments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"instructor_id":1,"lesson_type_id":1,"start_at":"2026-05-25 10:00","end_at":"2026-05-25 11:00","title":"Test"}'
```
## Tech Stack
- Slim 4 (PSR-7 HTTP factory)
- Eloquent ORM (Illuminate Database)
- Firebase JWT for authentication
- PHP-DI for dependency injection
- CORS middleware (tuupola/cors-middleware)