# Die API von d4rk_media Diese Seite ist für den Fall geschrieben, dass jemand ein Skript anbindet und etwas nicht tut, was es soll. Sie nennt deshalb nicht nur die Endpunkte, sondern auch **was schiefgehen kann und wie die Antwort dann aussieht**. Der Dienst läuft unter `https://fivemanage.d4rkst3r.de`. Dateien liegen darunter unter `/f/`. --- ## Der Grundsatz: der Pfad ist der Schlüssel `vehicles/adder.webp` bleibt `vehicles/adder.webp`. Die öffentliche Adresse entsteht daraus, indem der Ursprung davorgesetzt wird: ``` vehicles/adder.webp → https://fivemanage.d4rkst3r.de/f/vehicles/adder.webp ``` Deshalb kann die Vorlage in einem Handy-Skript schlicht `{model}.webp` lauten. **Die Adressen stehen nirgends in der Datenbank** — sie werden bei jeder Antwort neu gebaut. Ein Umzug auf einen anderen Namen ist eine geänderte Variable, kein Datenbankeingriff. ### Erlaubte Pfade Jeder Teil zwischen den Schrägstrichen muss mit einem **Buchstaben oder einer Ziffer** beginnen; danach sind zusätzlich Punkt, Strich und Unterstrich erlaubt. Höchstens 8 Ebenen, höchstens 200 Zeichen. Damit sind `..`, versteckte Dateien und alles mit Backslash von vornherein draußen. Was nicht passt, wird **abgelehnt und nicht zurechtgebogen** — sonst läge eine Datei unter einem anderen Namen als dem, den der Aufrufer erwartet, und niemand merkte es, bis das Bild fehlt. > Ein führender Unterstrich fällt durch. Das ist kein erfundener Fall: das > Fotostudio legt seine Leerbilder als `_plate` ab. --- ## Anmeldung: Token Alle Skript-Wege brauchen einen Token im Kopf: ``` Authorization: Bearer d4rk_… ``` Tokens werden im Dashboard angelegt. Der Klartext wird **genau einmal** gezeigt — danach steht nur noch sein Hash in der Datenbank, und auch der Dienst kann ihn nicht mehr herausgeben. Ein Token kann zwei Fesseln haben: | Fessel | Wirkung | |---|---| | Präfix | darf nur unterhalb dieses Pfades schreiben, z. B. `vehicles/` | | darf löschen | ohne dieses Recht antwortet `DELETE` mit 403 | --- ## POST /api/upload Nimmt die Datei in einer von **drei Rumpfformen** entgegen: 1. `multipart/form-data` mit dem Feld `file` 2. Base64-Text, wenn `X-Encoding: base64` gesetzt ist 3. rohe Bytes, sonst ### Kopfzeilen | Kopf | Bedeutung | |---|---| | `X-Path` | Wohin die Datei kommt. **Ohne ihn** wird aus dem SHA-256 ein Pfad gebaut — dann ist die Adresse nicht mehr vorhersagbar. | | `X-Encoding` | `base64`, wenn der Rumpf Base64-Text ist. Ein Data-URL-Vorspann (`data:image/webp;base64,`) wird abgeschnitten. | | `X-Overwrite` | `false` verweigert das Überschreiben und antwortet mit 409. Standard ist überschreiben. | | `X-Mime` | Überschreibt die Art. Sonst entscheidet die Endung. | ### Antwort ```json { "url": "https://fivemanage.d4rkst3r.de/f/vehicles/adder.webp", "path": "vehicles/adder.webp", "size": 132760, "sha256": "36cb4ddf…", "mime": "image/webp", "replaced": false } ``` ### Was schiefgehen kann | Antwort | Grund | |---|---| | 401 | Token fehlt oder ist unbekannt | | 403 | Der Token darf dort nicht schreiben (Präfix-Fessel) | | 400 | Pfad unzulässig, Rumpf leer, kaputtes Base64 | | 409 | `X-Overwrite: false` und der Pfad ist belegt | | 413 | größer als `MAX_UPLOAD_MB` (Standard 64) | Jede Fehlerantwort ist JSON mit einem Feld `error` und einem **Satz auf Deutsch**. Ein nackter 500 ohne Text ist genau der Fehler, wegen dem dieser Dienst gebaut wurde. --- ## Aus einer FiveM-Resource ```lua local TOKEN = 'd4rk_…' -- im Dashboard anlegen local function hochladen(pfad, bytes, cb) PerformHttpRequest('https://fivemanage.d4rkst3r.de/api/upload', function(status, body) if status ~= 200 then return cb(nil, body) end cb(json.decode(body).url) end, 'POST', B64.encode(bytes), { ['Authorization'] = 'Bearer ' .. TOKEN, ['X-Path'] = pfad, ['X-Encoding'] = 'base64', ['Content-Type'] = 'text/plain; charset=utf-8', }) end ``` **Warum Base64 und nicht die rohen Bytes.** `PerformHttpRequest` nimmt den Rumpf als Lua-Zeichenkette, und ob Nullbytes darin unverändert ankommen, hat hier niemand nachgemessen — ein PNG besteht zu gutem Teil daraus. Base64 kostet ein Drittel mehr auf der Leitung und nimmt die Frage aus dem Weg. Nachgemessen ist: Base64 kommt mit demselben SHA-256 an wie die rohen Bytes. **Warum vom Server und nicht aus der NUI.** Die NUI läuft unter `https`, und CEF hebt jede `http`-Adresse selbst dorthin an. Ein Upload aus dem Fenster heraus wäre entweder Mixed Content oder ginge ins Leere. --- ## GET /api/exists/<pfad> Für „nur fehlende" in einem Serienlauf: eine winzige Antwort gegen 130 KB Upload. ```json { "exists": true, "path": "vehicles/adder.webp", "url": "…", "size": 132760, "sha256": "…", "updated_at": 1786466462488 } ``` > **Drei Zustände, nicht zwei.** Wer einen Netzfehler als „nicht da" durchgehen > lässt, lädt bei einem Husten alles ein zweites Mal hoch. Ein Aufrufer sollte > zwischen `exists: false` und „ich weiß es nicht" unterscheiden. --- ## DELETE /api/media/<pfad> Braucht einen Token mit dem Recht **darf löschen**. ```json { "deleted": "vehicles/adder.webp" } ``` Löschen ist endgültig: die Datei ist von der Platte weg, und es gibt keinen Papierkorb. Die tägliche Sicherung hilft nur bis zum letzten Lauf. --- ## Grenzen | | | |---|---| | Größte Datei | 64 MB (`MAX_UPLOAD_MB`) | | Pfadlänge | 200 Zeichen, höchstens 8 Ebenen | | Anmeldeversuche | 5 frei, danach Sperre ab 30 s (verdoppelt bis 15 min) | | Sitzung | 30 Tage | --- ## Der Zwischenspeicher des Proxys Vor dem Dienst steht Nginx Proxy Manager. Dessen `assets.conf` greift über eine Regel auf **jede URL, die auf `.webp`, `.png`, `.js`, `.css` … endet** — und legt die Antwort für 30 Minuten weg, dabei ignoriert sie unsere eigenen Kopfzeilen. Das hatte zwei sichtbare Folgen, beide nachgemessen und beide behoben: - Ein **überschriebenes Bild** blieb bis zu 30 Minuten alt, obwohl Upload und Datenbank „fertig" sagten. - `/api/exists/…webp` wurde mit Antwort zwischengespeichert und danach **auch ohne Token** ausgeliefert. Behoben durch drei Blöcke in der *Custom Nginx Configuration* des Proxy-Hosts: ```nginx location ^~ /api/ { proxy_cache off; include conf.d/include/proxy.conf; } location ^~ /f/ { proxy_cache off; include conf.d/include/proxy.conf; } location ^~ /t/ { proxy_cache off; include conf.d/include/proxy.conf; } ``` Das `^~` ist der ganze Trick: eine gewöhnliche Präfix-Regel **verliert** gegen die Regex aus `assets.conf`. In einem Wegwerf-Nginx nachgemessen. --- ## Export Im Dashboard unter **API** — oder direkt, mit gültiger Sitzung: ``` /api/dash/export?format=json alles, was der Dienst weiß /api/dash/export?format=csv Semikolon und CRLF, für Excel /api/dash/export?format=lua fertige Tabelle Name → Adresse /api/dash/export?format=lua&folder=vehicles ``` Die Lua-Fassung liest man in einer Resource als `Urls.adder` aus. --- *Diese Seite wird aus `docs/` im Repo erzeugt — Änderungen hier gehen beim nächsten Lauf verloren.*