# File Upload API ## Test-Zugangsdaten | Feld | Wert | | ------------ | -------------- | | **Email** | `demo@iab.de` | | **Password** | `winter123456` | Nach den Migrationen kann der Administrator deterministisch angelegt oder aktualisiert werden: ```bash php artisan migrate ./bin/make-demo-admin.sh ``` Das Skript ist idempotent. Ein bestehender Account mit `demo@iab.de` behaelt seine ID und seinen Namen, erhaelt aber erneut Administratorrechte und das oben dokumentierte Passwort. --- ## Authentication Alle Verwaltungsendpunkte unter `/api/files` erfordern einen Bearer-Token. Die oeffentlichen Leseendpunkte unter `/api/public/files` benoetigen keinen Token. Token anfordern via: ```http POST /api/tokens Content-Type: application/json { "email": "demo@iab.de", "password": "winter123456", "token_name": "my-client" } ``` **Response `201 Created`** ```json { "token": "1|xxxxxxxxxxxx" } ``` Token widerrufen: ```http DELETE /api/tokens/current Authorization: Bearer ``` **Response `204 No Content`** ### Berechtigungen - Normale Benutzer koennen nur eigene Dateien auflisten, lesen, aktualisieren, ersetzen, herunterladen und loeschen. Zugriffe auf fremde IDs liefern `404`. - Administratoren koennen Dateien aller Benutzer und unabhaengig von `freigabe` verwalten. - Administratoren koennen auch unveroeffentlichte oder noch gesperrte Inhalte herunterladen. - Die Administratorrolle wird nicht ueber die HTTP-API vergeben. Dafuer dient `./bin/make-demo-admin.sh`. --- ## Endpoints ### CRUD-Uebersicht | Operation | HTTP | Endpoint | Beschreibung | | --------------- | -------- | -------------------------- | --------------------------------------------- | | Create | `POST` | `/api/files` | Eine oder mehrere Dateien hochladen | | Read (List) | `GET` | `/api/files` | Erlaubte Dateien paginiert auflisten | | Read (Single) | `GET` | `/api/files/{id}` | Metadaten einer einzelnen Datei lesen | | Download | `GET` | `/api/files/{id}/download` | Dateiinhalt herunterladen | | Update | `PATCH` | `/api/files/{id}` | Metadaten (und optional `path`) aktualisieren | | Replace Content | `POST` | `/api/files/{id}/content` | Dateiinhalt unter derselben ID ersetzen | | Delete | `DELETE` | `/api/files/{id}` | Datei inkl. Metadaten loeschen | --- ### Datei erstellen (Create) ``` POST /api/files Authorization: Bearer Content-Type: multipart/form-data ``` | Feld | Typ | Pflicht | Beschreibung | | ------------------- | ------- | ------- | ------------------------------------------------------ | | `files[]` | File(s) | ja | 1–25 Dateien, je max. 200 MB | | `path` | string | nein | Virtueller Zielpfad, z. B. `projekte/2026/q1` | | `aktiv_id` | string | nein | Externe Aktivitäts- oder Vorgangsnummer | | `reprotyp` | string | nein | Repräsentationstyp, z. B. `forschungsbericht` | | `titel` | string | nein | Titel der Datei/Publikation | | `beschreibung` | string | nein | Kurzbeschreibung (max. 5000 Zeichen) | | `autoren` | string | nein | Autorenangabe(n) | | `sachschlagwoerter` | string | nein | Sachschlagwörter | | `iab_themen` | string | nein | IAB-Themenbereich | | `sperrfrist` | date | nein | Sperrdatum (`YYYY-MM-DD`), Download bis dahin gesperrt | | `reihenfolge` | integer | nein | Sortierreihenfolge (default: 0) | | `freigabe` | boolean | nein | Ob die Datei abrufbar ist (default: `true`) | | `erstellt_von` | string | nein | Freie Angabe zur erstellenden Person oder Stelle | | `freigegeben_von` | string | nein | Freie Angabe zur freigebenden Person oder Stelle | Der `path` definiert die Ordnerstruktur. Erlaubte Zeichen: `a-z A-Z 0-9 . _ -` und `/` als Trennzeichen. Kein führender oder abschließender Slash, keine `..`-Segmente. **Beispiel** ```bash curl -X POST https://repo.iab.de/api/files \ -H "Authorization: Bearer " \ -F "files[]=@bericht.pdf" \ -F "files[]=@daten.xlsx" \ -F "path=projekte/2026/q1" ``` **Response `201`** ```json { "files": [ { "id": "018f1a2b-...", "aktiv_id": "AKT-2026-001", "original_name": "bericht.pdf", "path": "projekte/2026/q1", "mime_type": "application/pdf", "size": 204800, "reprotyp": "forschungsbericht", "titel": "Arbeitsmarktbericht 2026", "beschreibung": null, "autoren": "Max Mustermann", "sachschlagwoerter": null, "iab_themen": null, "sperrfrist": null, "reihenfolge": 0, "freigabe": true, "erstellt_von": "Redaktion Dashboard", "freigegeben_von": "Admin Dashboard", "created_at": "2026-04-30T10:00:00.000000Z" } ] } ``` --- ### Datei herunterladen ``` GET /api/files/{id}/download Authorization: Bearer ``` Liefert die Datei als Download mit korrektem `Content-Type` und originalem Dateinamen. Normale Benutzer koennen nur eigene Dateien herunterladen (sonst `404`). Bei `freigabe=false` oder `sperrfrist` in der Zukunft folgt fuer normale Benutzer `403`; Administratoren duerfen diese Inhalte herunterladen. Bei Audio-Dateien wird der server-seitig erkannte MIME-Typ im Content-Type des Downloads verwendet. Der Dateiinhalt wird beim Upload, Download und beim Ersetzen des Inhalts nicht umgewandelt. Je nach `fileinfo`-Erkennung kann eine WAV-Datei als `audio/x-wav`, `audio/wav` oder `audio/wave` gespeichert werden. OGG wird als `audio/ogg` und MP3 als `audio/mpeg` gespeichert. ```bash curl -OJ "https://repo.iab.de/api/files/018f1a2b-.../download" \ -H "Authorization: Bearer " ``` --- ### Datei lesen (Read Single) ``` GET /api/files/{id} Authorization: Bearer ``` Gibt die Metadaten einer einzelnen erlaubten Datei zurueck. Normale Benutzer sehen nur eigene, Administratoren alle Datensaetze, auch bei `freigabe=false`. **Response `200`** ```json { "id": "018f1a2b-...", "aktiv_id": "AKT-2026-001", "original_name": "bericht.pdf", "path": "projekte/2026/q1", "mime_type": "application/pdf", "size": 204800, "reprotyp": "forschungsbericht", "titel": "Arbeitsmarktbericht 2026", "beschreibung": null, "autoren": "Max Mustermann", "sachschlagwoerter": null, "iab_themen": null, "sperrfrist": null, "reihenfolge": 0, "freigabe": true, "erstellt_von": "Redaktion Dashboard", "freigegeben_von": "Admin Dashboard", "created_at": "2026-04-30T10:00:00.000000Z", "updated_at": "2026-04-30T10:00:00.000000Z" } ``` ```bash curl "https://repo.iab.de/api/files/018f1a2b-..." \ -H "Authorization: Bearer " ``` --- ### Dateien auflisten ``` GET /api/files Authorization: Bearer ``` Gibt fuer normale Benutzer alle eigenen Uploads und fuer Administratoren die Uploads aller Benutzer zurueck. Die Liste ist nach Hochladezeitpunkt sortiert (neueste zuerst) und auf 100 Eintraege pro Seite paginiert. Mit `freigabe=0` kann das Admin-Dashboard gezielt unveroeffentlichte Datensaetze abrufen. **Optionale Filter (kombinierbar):** | Parameter | Beschreibung | Beispiel | | ---------- | ------------------------------- | ----------------------------------- | | `path` | Pfad-Präfix | `path=projekte/2026` | | `aktiv_id` | Exakte Aktivitäts-ID | `aktiv_id=AKT-2026-001` | | `since` | Nur Dateien ab diesem Zeitpunkt | `since=2026-04-30T12:00:00%2B02:00` | | `freigabe` | Nach Freigabe filtern | `freigabe=1` oder `freigabe=0` | | `reprotyp` | Nach Repräsentationstyp filtern | `reprotyp=forschungsbericht` | **`since` – Zeitpunkt mit Zeitzone** Der Wert muss ISO 8601 sein. Ohne Offset wird UTC angenommen – bei lokaler Zeit (z. B. Europe/Berlin, CEST = UTC+2) immer den Offset mitsenden: ```bash # Alle Uploads seit 30. April 12:00 Uhr Berliner Zeit curl "https://repo.iab.de/api/files?since=2026-04-30T12:00:00%2B02:00" \ -H "Authorization: Bearer " ``` ```bash # Alternativ direkt als UTC curl "https://repo.iab.de/api/files?since=2026-04-30T10:00:00Z" \ -H "Authorization: Bearer " ``` ```bash curl "https://repo.iab.de/api/files?aktiv_id=AKT-2026-001" \ -H "Authorization: Bearer " ``` ```bash curl "https://repo.iab.de/api/files?path=projekte/2026" \ -H "Authorization: Bearer " ``` --- ### Datei aktualisieren (Update) ``` PATCH /api/files/{id} Authorization: Bearer Content-Type: application/json ``` Aktualisiert Metadaten einer bestehenden eigenen Datei. Es werden nur uebergebene Felder geaendert (partielles Update). Optional kann auch `path` geaendert werden. | Feld | Typ | Pflicht | Beschreibung | | ------------------- | ------- | ------- | ---------------------------------------- | | `path` | string | nein | Neuer virtueller Zielpfad | | `aktiv_id` | string | nein | Externe Aktivitaets- oder Vorgangsnummer | | `reprotyp` | string | nein | Repraesentationstyp | | `titel` | string | nein | Titel der Datei/Publikation | | `beschreibung` | string | nein | Kurzbeschreibung (max. 5000 Zeichen) | | `autoren` | string | nein | Autorenangabe(n) | | `sachschlagwoerter` | string | nein | Sachschlagwoerter | | `iab_themen` | string | nein | IAB-Themenbereich | | `sperrfrist` | date | nein | Sperrdatum (`YYYY-MM-DD`) | | `reihenfolge` | integer | nein | Sortierreihenfolge | | `freigabe` | boolean | nein | Ob die Datei abrufbar ist | | `erstellt_von` | string | nein | Erstellende Person oder Stelle | | `freigegeben_von` | string | nein | Freigebende Person oder Stelle | ```bash curl -X PATCH "https://repo.iab.de/api/files/018f1a2b-..." \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "titel": "Arbeitsmarktbericht 2026 (final)", "freigabe": true, "reihenfolge": 10 }' ``` **Response `200`** ```json { "id": "018f1a2b-...", "titel": "Arbeitsmarktbericht 2026 (final)", "freigabe": true, "reihenfolge": 10, "updated_at": "2026-05-01T09:15:00.000000Z" } ``` --- ### Dateiinhalt ersetzen ```http POST /api/files/{id}/content Authorization: Bearer Content-Type: multipart/form-data ``` Ersetzt nur den gespeicherten Inhalt einer Datei. Die Datensatz-ID, der Besitzer, `created_at` und alle Fachmetadaten bleiben erhalten. Aktualisiert werden `original_name`, `stored_path`, `mime_type`, `size` und `updated_at`. Nach erfolgreicher Aktualisierung wird der alte Inhalt geloescht. | Feld | Typ | Pflicht | Beschreibung | | ------ | ---- | ------- | -------------------------- | | `file` | File | ja | Neue Datei, maximal 200 MB | ```bash curl -X POST "https://repo.iab.de/api/files/018f1a2b-.../content" \ -H "Authorization: Bearer " \ -F "file=@daten-korrigiert.csv" ``` **Response `200`**: Vollstaendige Metadaten des bestehenden Datensatzes mit unveraenderter `id` und aktualisiertem `updated_at`. --- ### Datei löschen ``` DELETE /api/files/{id} Authorization: Bearer ``` Loescht Datei und Metadaten-Eintrag. Normale Benutzer koennen nur eigene Dateien loeschen (sonst `404`), Administratoren auch fremde Dateien. --- ## Oeffentliche Lese-API Die folgenden Endpunkte sind ohne Token erreichbar. Sie liefern ausschliesslich Datensaetze mit `freigabe=true`; ein Query-Parameter kann diese Einschraenkung nicht umgehen. | HTTP | Endpoint | Beschreibung | | ----- | --------------------------------- | ------------------------------------------ | | `GET` | `/api/public/files` | Freigegebene Metadaten paginiert auflisten | | `GET` | `/api/public/files/{id}` | Freigegebene Metadaten einzeln lesen | | `GET` | `/api/public/files/{id}/download` | Freigegebenen Dateiinhalt herunterladen | Die Liste unterstuetzt die Filter `path`, `aktiv_id`, `since` und `reprotyp` wie die geschuetzte Liste. Eine zukuenftige `sperrfrist` versteckt die Metadaten nicht, blockiert aber den oeffentlichen Download mit `403`. Unveroeffentlichte IDs liefern bei Einzelansicht und Download `404`. Die oeffentlichen JSON-Antworten enthalten `updated_at`, damit Clients ersetzte Inhalte erkennen koennen. Interne Felder wie `user_id`, `stored_path`, `erstellt_von` und `freigegeben_von` werden nicht ausgegeben. Antworten setzen `Cache-Control: public, max-age=0, must-revalidate`. ```bash curl "https://repo.iab.de/api/public/files?reprotyp=datensatz" curl -OJ "https://repo.iab.de/api/public/files/018f1a2b-.../download" ``` --- ## Erlaubte Dateitypen | Extension | MIME Type | | -------------- | --------------------------------------------------------------------------- | | `pdf` | `application/pdf` | | `txt` | `text/plain` | | `csv` | `text/csv` | | `jpg` / `jpeg` | `image/jpeg` | | `png` | `image/png` | | `gif` | `image/gif` | | `webp` | `image/webp` | | `doc` | `application/msword` | | `docx` | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | | `xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | | `pptx` | `application/vnd.openxmlformats-officedocument.presentationml.presentation` | | `zip` | `application/zip` | | `mp4` | `video/mp4` | | `wav` | `audio/x-wav`, `audio/wav`, `audio/wave` | | `ogg` | `audio/ogg` | | `mp3` | `audio/mpeg` | | `rdf` | `application/rdf+xml` | | `json` | `application/json` | Der MIME-Typ wird server-seitig anhand der Datei-Magic-Bytes geprüft, nicht anhand des Client-Headers. --- ## Limits | Parameter | Wert | | ------------------------ | ------ | | Max. Dateien pro Request | 25 | | Max. Dateigröße | 200 MB | --- ## Fehler | Code | Bedeutung | | ----- | ------------------------------------------------------------------------------------ | | `401` | Kein oder ungültiger Token | | `403` | Download fuer normalen/oeffentlichen Zugriff gesperrt | | `404` | Datei nicht gefunden oder gehört anderem Nutzer | | `413` | Request Entity Too Large (typisch nginx vor der API, z. B. client_max_body_size) | | `422` | Validierungsfehler (Dateityp, Größe, ungültiger Pfad, ungültige Update-Felder/Werte) | **Beispiel Validierungsfehler** ```json { "message": "The files.0 field must be a file of type: pdf, txt, ...", "errors": { "files.0": ["Unsupported file type."] } } ``` --- ## Speicherstruktur Dateien werden privat gespeichert (nicht öffentlich erreichbar) unter: ``` storage/app/private/uploads/{user_id}/{YYYY}/{MM}/{DD}/{hashname} ``` Der virtuelle Metadatenwert `path` beeinflusst den physischen Speicherpfad nicht. Der Dateiname wird als eindeutiger Hash gespeichert: ``` storage/app/private/uploads/1/2026/04/30/aB3xYz9kQ2mN4pR7.pdf ```