feat: der Dienst steht oeffentlich, und die Pfadmeldung sagt jetzt die Regel

Der Stack laeuft unter https://fivemanage.d4rkst3r.de, hinter NPM mit
Let's-Encrypt-Zertifikat, Container auf Port 9101.

DER NAME IST EINE UMKEHR, und sie steht ausfuehrlich im ROADMAP: hier stand,
dass fivecdn und fivemanage wegfallen, weil wir kein Fivemanage betreiben. Das
Argument gilt weiter -- es ist nur teurer als das, was dagegen steht. Der Name
war bereits fertig verkabelt: DNS da, NPM-Host da, Zertifikat gueltig bis
09.11.2026, Ziel host.docker.internal:9101, und dieser Port wurde frei, als die
Lite-Container gingen. Unser Stack auf 9101 kostet damit null Klicks. Der
Wechsel auf media.d4rkst3r.de bleibt billig, weil keine Adresse in der
Datenbank steht.

Ueber die oeffentliche Adresse durchgemessen: Anmeldung durch echtes HTTPS
(der Fall, den curl bisher nicht bezeugen konnte), Token mit Praefix-Fessel,
403 mit Text fuer anderswo/ und fuers Loeschen, Upload und Abruf Byte fuer
Byte identisch.

Und ein Befund aus dem Fotostudio-Anschluss: die Pfadmeldung war irrefuehrend.
SEGMENT verlangt als ERSTES Zeichen einen Buchstaben oder eine Ziffer, die
Meldung sprach aber nur von "nicht mit Punkt beginnend" -- ein fuehrender
Unterstrich faellt ebenso durch. Kein erfundener Fall: das Fotostudio legt
seine Leerbilder als "_plate" ab. Jetzt nennt die Meldung die Regel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 17:08:36 +02:00
co-authored by Claude Opus 5
parent 7bfd3458a1
commit 5f7f17ce7e
2 changed files with 91 additions and 17 deletions
+83 -15
View File
@@ -27,19 +27,33 @@ kein zweiter Dienst, kein Passwort dazwischen, und eine Sicherung ist ein
`cp -a` über ein Volume. Wenn daraus je Millionen Zeilen werden, wird
`server/src/db.ts` ausgetauscht — die Aufrufer merken davon nichts.
**Ein Name.** `fivecdn.d4rkst3r.de` und `fivemanage.d4rkst3r.de` fallen weg —
wir betreiben kein Fivemanage, also tragen unsere Adressen auch nicht dessen
Namen. Dashboard und API liegen an der Wurzel, die Dateien unter `/f/`:
**Ein Name.** Dashboard und API liegen an der Wurzel, die Dateien unter `/f/`:
```
https://media.d4rkst3r.de/ Dashboard und API
https://media.d4rkst3r.de/f/vehicles/adder.webp
https://fivemanage.d4rkst3r.de/ Dashboard und API
https://fivemanage.d4rkst3r.de/f/vehicles/adder.webp
```
Ein DNS-Eintrag, ein Host im Proxy. Aufgegeben ist damit eine Eigenschaft, die
zwei Namen mitbrächten: die öffentliche Adresse trägt jetzt auch das
Anmeldeformular.
> **Und der Name ist `fivemanage`, obwohl hier lange das Gegenteil stand.**
> Der ursprüngliche Satz war: `fivecdn` und `fivemanage` fallen weg, wir
> betreiben kein Fivemanage und tragen deshalb nicht dessen Namen. Das Argument
> stimmt immer noch — es ist nur billiger als das, was dagegen steht, und das
> ist nachgemessen: der Name **war schon fertig verkabelt**. DNS-Eintrag da,
> Host in NPM da, Let's-Encrypt-Zertifikat gültig bis 09.11.2026, und er zeigte
> auf `host.docker.internal:9101` — einen Port, der frei wurde, als die
> Lite-Container gelöscht wurden. Unser Stack auf 9101 heißt: **kein
> DNS-Eintrag, kein Klick in NPM, kein neues Zertifikat.**
>
> Der Preis ist ein Name, der nach einem fremden Produkt klingt. Der Wechsel
> bleibt billig — die Adressen stehen nirgends in der Datenbank, sie werden bei
> jeder Antwort aus `PUBLIC_URL` gebaut. Ein späterer Umzug auf
> `media.d4rkst3r.de` ist ein DNS-Eintrag, ein NPM-Host und eine geänderte
> Variable.
Zwei Namen kann der Dienst weiterhin — `FILES_HOST` setzen, dann gibt es unter
diesem Namen ausschließlich Dateien und die URLs kommen ohne `/f/` aus. Beides
ist gemessen. Der Wechsel ist billig: die Adressen stehen **nirgends in der
@@ -241,19 +255,73 @@ Nachmittag gekostet.
> anderen Rechner, sondern nur der **öffentliche Auftritt**: DNS-Eintrag für
> `media.d4rkst3r.de`, ein Host in NPM mit TLS, ein echtes Passwort.
Host-Port bleibt **9102**. Gemessen am 11.08.2026 belegt: 80, 81, 443, 2224,
3000, 3080, 3478, 8000, 8080, **8443**, 8090, 9000, 9443, 11000 — 8443 fehlte
in der alten Liste. **9100 und 9101 sind frei**, seit die Lite-Container weg
sind; wir bleiben trotzdem auf 9102, weil die Zahl in dieser Datei, im Compose
und in der Erinnerung schon zusammengehört.
Gemessen am 11.08.2026 belegt: 80, 81, 443, 2224, 3000, 3080, 3478, 8000,
8080, **8443**, 8090, 9000, 9443, 11000 — 8443 fehlte in der alten Liste.
**Der Host-Port ist 9101**, nicht die 9102 aus dem Compose-Standard: darauf
zeigt der fertige NPM-Host (siehe oben). Gesetzt wird er über `HOST_PORT` in
der `.env`, der Standard im Compose bleibt 9102.
### ✅ Fertig — der Dienst steht öffentlich
Seit dem 11.08.2026 läuft er unter **`https://fivemanage.d4rkst3r.de`**, hinter
NPM mit Let's-Encrypt-Zertifikat, Container auf 9101. Die Werte stehen in einer
`.env` neben dem Compose (gitignored); dieselben Werte sind in Portainer die
Stack-Variablen.
Durchgemessen über die öffentliche Adresse, nicht über localhost:
- Anmeldung durch echtes HTTPS — **das ist der Fall, den `curl` bisher nicht
bezeugen konnte**: `Secure` am Cookie stört jetzt nicht mehr, weil die
Verbindung wirklich TLS ist. 200, Cookie da, `/auth/me` 200.
- Token mit Präfix-Fessel `vehicles/` und ohne Löschrecht angelegt. Upload nach
`anderswo/`**403 mit Text**, Löschen → **403 mit Text**.
- Upload und Abruf über TLS und Proxy: **Byte für Byte identisch.**
> **Der 502, der keiner war.** Der erste Aufruf gab 502 in 78 ms. Die
> naheliegende Erklärung — „ein Moment, gleich geht's" — war falsch: der
> NPM-Host zeigte in dem Moment auf 9102, unser Container liegt auf 9101, also
> sofortige Ablehnung. Kein Timeout, keine DNS-Frage, kein Zufall. Ein 502
> binnen Millisekunden ist immer ein *Connection refused*, und dann stimmt eine
> Portnummer nicht.
### ⬜ Als Nächstes
**3 · `server/upload.lua` im Fotostudio.** `Upload.put(pfad, bytes) → url`.
Token in `config.upload.lua`, server-only, gitignored, mit committetem
`.example` — nie im NUI, dort hat es jeder Spieler im Speicher. An den
Serienlauf hängen, damit freigestellte Bilder nach der Freigabe sofort
hochgehen.
**3 · `server/upload.lua` im Fotostudio — geschrieben, im Spiel noch nicht
gelaufen.** Liegt in `d4rk_photostudio` (eigenes Repo, `D:\FXServer\txData\…`):
```
server/upload.lua Upload.send(pfad, bytes, cb) und Upload.put(pfad, bytes) → url
config.upload.example.lua committet
config.upload.lua gitignored, enthaelt den Token
```
Hängt an `d4rk_photostudio:saveKeyed` — also genau an der Freigabe: das Bild
geht **erst auf die Platte, dann in den Dienst**, und ein Fehlschlag bricht
nichts ab, sondern landet im Protokoll des Laufs. Ein Lauf über 900 Fahrzeuge,
der an einem Netzhuster stirbt, wäre der teuerste denkbare Fehler.
Gemessen ist alles, was sich ohne FiveM messen lässt — und das ist mehr, als es
zunächst schien: `upload.lua` wurde in einer **echten Lua-5.3-VM** (fengari,
über Node) mit gefälschten FiveM-Funktionen ausgeführt. 30 Prüfungen, keine
durchgefallen: fehlende, kaputte und unvollständige `config.upload.lua`;
Adresse, Methode und alle vier Kopfzeilen; der Fehlertext des Dienstes statt
einer nackten Zahl; Status 0; und dass die Frist den Rückruf nicht ein zweites
Mal auslöst. Der Rumpf, den die VM erzeugt hat, ging danach **unverändert an
den echten Dienst** — hochgeladen, abgerufen, Byte für Byte identisch.
**Was offen bleibt, bis es einmal im Spiel lief:** ob `PerformHttpRequest`,
`SetTimeout` und `promise` sich in FiveM so verhalten wie die Fälschungen, und
ob der Event-Handler den Rückruf sauber erreicht. Dafür fehlt genau ein
Handgriff, und er braucht die Serverkonsole:
```
refresh
ensure d4rk_photostudio (bzw. restart d4rk_photostudio)
```
Beim Start sagt die Resource in einer Zeile, ob der Dienst antwortet. Danach
`/studio shot <modell>` im Spiel — das Bild muss unter
`https://fivemanage.d4rkst3r.de/f/vehicles/<modell>.webp` stehen.
Nach diesem Schritt ist das erreicht, worum es ursprünglich ging.
+8 -2
View File
@@ -41,10 +41,16 @@ export function checkPath(raw: string): string {
for (const segment of segments) {
if (!SEGMENT.test(segment)) {
// Die Meldung nennt die Regel so, wie sie ist. Vorher stand hier
// "nicht mit Punkt beginnend" — das ist nur die Haelfte: das erste
// Zeichen muss ein Buchstabe oder eine Ziffer sein, ein fuehrender
// Unterstrich faellt also ebenso durch. Aufgefallen an
// "_probe.webp", und das ist kein erfundener Fall: das Fotostudio
// legt seine Leerbilder als "_plate" ab.
throw new PathError(
`"${segment}" ist als Pfadteil nicht erlaubt ` +
'(Buchstaben, Ziffern, Punkt, Strich, Unterstrich; ' +
'nicht mit Punkt beginnend)',
'(erstes Zeichen: Buchstabe oder Ziffer; danach auch ' +
'Punkt, Strich, Unterstrich)',
)
}
}