Die API-Anleitung
+218
@@ -0,0 +1,218 @@
|
|||||||
|
# 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.
|
||||||
Reference in New Issue
Block a user