diff --git a/docs/reference-api.md b/docs/reference-api.md index 6d9a208..7b7aeed 100644 --- a/docs/reference-api.md +++ b/docs/reference-api.md @@ -1,6 +1,6 @@ # REST-API (v1) -Letzte Aktualisierung: 2026-03-06 +Letzte Aktualisierung: 2026-03-24 ## Single Source of Truth @@ -46,7 +46,7 @@ Alle fachlichen und technischen API-Details stehen nur in `/docs/openapi.yaml`. 1. API-Änderung immer zuerst in `/docs/openapi.yaml` pflegen. 2. `/admin/api-docs` öffnen und Rendering kurz prüfen. 3. API-Request per Client testen (z. B. Yaak/Postman/cURL). -4. Nur falls nötig hier in `api.md` organisatorische Hinweise ergänzen (keine Endpoint-Details). +4. Nur falls nötig hier in `reference-api.md` organisatorische Hinweise ergänzen (keine Endpoint-Details). ## Verwandte Dokumente diff --git a/docs/reference-entwickler-checkliste.md b/docs/reference-entwickler-checkliste.md index 71d5c1a..ab0cc30 100644 --- a/docs/reference-entwickler-checkliste.md +++ b/docs/reference-entwickler-checkliste.md @@ -1,6 +1,6 @@ # Entwickler-Checkliste -Letzte Aktualisierung: 2026-03-18 +Letzte Aktualisierung: 2026-03-24 ## Ziel @@ -75,7 +75,7 @@ Kurze Definition of Done vor Merge. - [ ] bei Modul-/Manifest-/Routen-Aenderungen: `APP_ENABLED_MODULES='addressbook,bookmarks' php bin/console module:sync` - [ ] Runtime-Sync erzeugt Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) und `vendor_warnings_ignored=` - [ ] Keine First-party CLI-Warnings/Notices/Deprecations im Runtime-Sync (Vendor-Warnings sind nur temporaer toleriert und muessen gezaehlt sichtbar sein) -- [ ] bei Docs-/Skills-Aenderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillsContractTest.php` +- [ ] bei Docs-/Skills-Aenderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillReferenceContractTest.php` - [ ] bei Docs-/Skills-Aenderungen: `bin/docs-link-check.sh` (scannt standardmaessig ohne `.agents/runs/**`) - [ ] bei Docs-/Skills-Aenderungen: `bin/codex-skills-sync.sh --check` - [ ] bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors) diff --git a/docs/reference-konfiguration.md b/docs/reference-konfiguration.md index d1854f6..4bf987b 100644 --- a/docs/reference-konfiguration.md +++ b/docs/reference-konfiguration.md @@ -1,6 +1,6 @@ # Konfiguration (ENV-Variablen) -Letzte Aktualisierung: 2026-03-06 +Letzte Aktualisierung: 2026-03-24 Alle Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei. Vorlage: `.env.example` — niemals echte Credentials committen. @@ -11,7 +11,7 @@ Vorlage: `.env.example` — niemals echte Credentials committen. | Variable | Standard | Beschreibung | |---|---|---| -| `APP_NAME` | `App` | Anwendungsname (wird in UI-Titeln und E-Mails verwendet) | +| `APP_NAME` | `CoreCore` | Anwendungsname (wird in UI-Titeln und E-Mails verwendet) | | `APP_URL` | `http://localhost:8080` | Basis-URL der Anwendung (für Links in E-Mails und Redirects) | | `APP_ENV` | `dev` | Laufzeitumgebung (`dev` oder `prod`) — beeinflusst Fehlerausgabe und Caching | | `APP_DEBUG` | `true` | Debug-Modus aktivieren (`true`/`false`) — in Produktion auf `false` setzen | @@ -29,7 +29,7 @@ Vorlage: `.env.example` — niemals echte Credentials committen. | Variable | Standard | Beschreibung | |---|---|---| -| `SESSION_NAME` | `app_session` | Name des Session-Cookies im Browser | +| `SESSION_NAME` | `CoreCore` | Name des Session-Cookies im Browser | --- @@ -85,7 +85,7 @@ Die Firewall begrenzt gleichzeitige Requests pro IP (Concurrency-Schutz). | `SMTP_USER` | `no-reply@example.com` | SMTP-Benutzername / Absenderadresse | | `SMTP_PASS` | _(leer)_ | SMTP-Passwort | | `SMTP_FROM` | `no-reply@example.com` | Absender-E-Mail-Adresse | -| `SMTP_FROM_NAME` | `App` | Absendername in ausgehenden E-Mails | +| `SMTP_FROM_NAME` | `CoreCore` | Absendername in ausgehenden E-Mails | | `SMTP_SECURE` | `tls` | Verschlüsselungstyp: `tls` (STARTTLS) oder `ssl` | --- diff --git a/docs/reference-konventionen.md b/docs/reference-konventionen.md index 46af66f..5440b74 100644 --- a/docs/reference-konventionen.md +++ b/docs/reference-konventionen.md @@ -1,6 +1,6 @@ # Konventionen (kurz und verbindlich) -Letzte Aktualisierung: 2026-03-18 +Letzte Aktualisierung: 2026-03-24 ## 1) Code @@ -37,7 +37,7 @@ Letzte Aktualisierung: 2026-03-18 - UUID-first für externe Ressourcen. - Interne IDs nicht nach außen geben. -- Fehlerantworten einheitlich halten (`error`, optional `errors`). +- Fehlerantworten einheitlich halten (`ok`, `request_id`, `error_code`, `details`; kein Legacy-`error`/`errors`). - OpenAPI bei jeder API-Änderung im selben Merge aktualisieren. ## 5) Frontend diff --git a/pages/api/v1/me/avatar().php b/pages/api/v1/me/avatar().php index 78ac345..634ac93 100644 --- a/pages/api/v1/me/avatar().php +++ b/pages/api/v1/me/avatar().php @@ -21,10 +21,7 @@ if ($method === 'GET') { $path = $userAvatarService->findAvatarPath($uuid, $size); if (!$path || !is_file($path)) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $mime = $userAvatarService->detectMime($path); diff --git a/pages/api/v1/tenants/avatar($id).php b/pages/api/v1/tenants/avatar($id).php index 8eaf99c..49e719a 100644 --- a/pages/api/v1/tenants/avatar($id).php +++ b/pages/api/v1/tenants/avatar($id).php @@ -14,28 +14,19 @@ $request = requestInput(); $uuid = trim((string) ($id ?? '')); if ($uuid === '') { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $tenantAvatarService = (app(TenantServicesFactory::class))->createTenantAvatarService(); if (!$tenantAvatarService->isValidUuid($uuid)) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $tenant = app(\MintyPHP\Service\Directory\DirectoryServicesFactory::class)->createTenantService()->findByUuid($uuid); $tenantId = (int) ($tenant['id'] ?? 0); if ($tenantId <= 0) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } ApiAuth::requireResourceAccess('tenants', $tenantId); @@ -43,10 +34,7 @@ ApiAuth::requireResourceAccess('tenants', $tenantId); $size = $request->hasQuery('size') ? $request->queryInt('size') : null; $path = $tenantAvatarService->findAvatarPath($uuid, $size); if (!$path || !is_file($path)) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $mime = $tenantAvatarService->detectMime($path); diff --git a/pages/api/v1/users/avatar($id).php b/pages/api/v1/users/avatar($id).php index 4e8b5bb..fd85f6f 100644 --- a/pages/api/v1/users/avatar($id).php +++ b/pages/api/v1/users/avatar($id).php @@ -15,10 +15,7 @@ $request = requestInput(); $uuid = trim((string) ($id ?? '')); if ($uuid === '') { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $userAccountService = app(UserAccountService::class); @@ -36,26 +33,27 @@ $decision = $authorizationService->authorize(UserAuthorizationPolicy::ABILITY_AP if (!$decision->isAllowed()) { $status = $decision->status() ?: 403; - http_response_code($status); - header('Content-Type: application/json'); - echo json_encode(['error' => $decision->error() ?: 'forbidden']); - return; + $error = $decision->error() ?: 'forbidden'; + + if ($status === 404) { + ApiResponse::notFound($error); + } + + if ($status === 403) { + ApiResponse::forbidden($error); + } + + ApiResponse::error($error, $status); } if (!$user) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $size = $request->hasQuery('size') ? $request->queryInt('size') : null; $path = $userAvatarService->findAvatarPath($uuid, $size); if (!$path || !is_file($path)) { - http_response_code(404); - header('Content-Type: application/json'); - echo json_encode(['error' => 'not_found']); - return; + ApiResponse::notFound(); } $mime = $userAvatarService->detectMime($path);