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