# AeroMC – CurseForge-Weiterleitung Kleiner Dienst, der den CurseForge-API-Schlüssel hält und die Anfragen stellvertretend für den Launcher stellt. ## Wozu das Ganze CurseForge erlaubt es nicht, den API-Schlüssel weiterzugeben. In einer verteilten `.exe` wäre er für jeden auslesbar – das Entpacken einer Electron-Anwendung dauert keine Minute. Deshalb bleibt der Schlüssel hier auf dem Server, und der Launcher kennt nur die Adresse dieses Dienstes. ## Wege | Weg | Zweck | |---|---| | `GET /health` | Lebenszeichen, verrät nichts über den Schlüssel | | `POST /v1/mods/files` | Dateiliste auflösen, Rumpf `{ "fileIds": [...] }`, höchstens 100 je Anfrage | | `GET /v1/download?url=…` | Mod-Datei durchreichen; das CurseForge-CDN verlangt seit Juli 2026 ebenfalls einen Schlüssel | | `GET /v1/mods/search` | Katalogsuche. Erlaubt sind nur `classId`, `categoryId`, `searchFilter`, `gameVersion`, `modLoaderType`, `sortField`, `sortOrder`, `pageSize` (1–50) und `index`; `gameId` ist immer Minecraft (432) | | `GET /v1/categories` | Kategorien (nur `classId`, `gameId` ist immer 432) | | `GET /v1/mods//files` | Dateien einer Mod (`gameVersion`, `modLoaderType`, `pageSize`, `index`) | Die Katalog-Wege geben ungültige oder unbekannte Parameter nicht weiter, sondern lassen sie weg. Antworten von CurseForge werden nicht zwischengespeichert. Alles andere (z. B. `/v1/games`) gibt es nicht und liefert `404`. ## Einrichten ```bash cp .env.example .env # CF_API_KEY eintragen ``` ### Ohne Docker Braucht nur Node 18 oder neuer, keine Pakete. ```bash node index.js ``` Dauerhaft über systemd: `aeromc-cf-proxy.service` nach `/etc/systemd/system/` kopieren, Pfade und Benutzer darin anpassen, dann ```bash systemctl daemon-reload systemctl enable --now aeromc-cf-proxy ``` ### Mit pm2 `ecosystem.config.js` liegt bei und liest die `.env` selbst ein – pm2 tut das von sich aus nämlich nicht. ```bash pm2 start ecosystem.config.js pm2 save pm2 startup # einmalig, für den Start nach einem Neustart ``` Nach jeder Änderung an der `.env` den Eintrag entfernen und neu aus der `ecosystem.config.js` starten: ```bash pm2 delete aeromc-cf-proxy pm2 start ecosystem.config.js pm2 save ``` Ein bloßes `pm2 restart aeromc-cf-proxy --update-env` reicht **nicht**: Die `.env` wird nur gelesen, wenn pm2 die `ecosystem.config.js` lädt. Beim Restart behält pm2 die Werte vom ersten Start, und du suchst lange nach dem Grund, warum der neue Schlüssel nicht greift. Nachsehen, ob er läuft: ```bash pm2 logs aeromc-cf-proxy --lines 20 ``` **Nicht auf Cluster-Betrieb umstellen.** Die Konfiguration steht bewusst auf `exec_mode: 'fork'` mit einem Prozess. Die Ratenbegrenzung zählt im Arbeitsspeicher – bei vier Arbeitern hätte jeder seine eigene Zählung, und die Grenze wäre in Wahrheit viermal so hoch wie eingestellt. ### Mit Docker ```bash docker compose up -d ``` ## Launcher einstellen In den Launcher-Einstellungen unter **Netzwerk → CurseForge** die Adresse eintragen, zum Beispiel `https://cf.example.de`. Das Feld „Eigener API-Key" bleibt leer – wer dort einen Schlüssel einträgt, geht direkt zu CurseForge und nutzt diesen Dienst nicht. Soll die Adresse für alle Nutzer voreingestellt sein, trage sie in `src/services.js` bei `STANDARD_WEITERLEITUNG` ein. Die Adresse ist kein Geheimnis, nur der Schlüssel ist eines. ## Absicherung Der Dienst steht offen im Netz. Eingebaut ist: - **Host-Liste** – durchgereicht wird nur an `edge.forgecdn.net`, `mediafilez.forgecdn.net` und `media.forgecdn.net`. Ohne diese Prüfung könnte jemand den Dienst Adressen in deinem Heimnetz abrufen lassen und die Antwort mitlesen. - **Umleitungen** werden selbst verfolgt und bei jedem Sprung erneut gegen die Host-Liste geprüft, höchstens vier Sprünge. - **Ratenbegrenzung** je Absender, einstellbar über `RATE_LIMIT_PER_MIN`. - **Größengrenze** für durchgereichte Dateien über `MAX_DOWNLOAD_MB`. Sie gilt auch dann, wenn CurseForge keine Dateigröße angibt: Es wird mitgezählt und bei Überschreitung abgebrochen. - **Parallelgrenze** über `MAX_PARALLEL_DOWNLOADS` (Standard 8). Weitere Downloads bekommen `503` mit `Retry-After`. - **Zeitlimit** von 30 Sekunden für Antworten von CurseForge. Bricht der Launcher mitten im Download ab, wird auch die Verbindung zu CurseForge geschlossen. - **Fehlversuche** beim Token zählen zur Ratenbegrenzung, ein Raten ist also nicht unbegrenzt schnell möglich. - **`/selftest`** läuft höchstens alle 30 Sekunden wirklich (fünf Anfragen an CurseForge), dazwischen kommt das letzte Ergebnis mit `"zwischengespeichert": true`. Das Token steckt im Launcher; ohne diese Bremse ließe sich damit dein CurseForge-Kontingent aufbrauchen. - **Token** über `CF_PROXY_TOKEN` – Pflicht: ohne Token startet der Dienst nicht (bewusst offen nur mit `CF_PROXY_OFFEN=1`, z. B. im abgeschotteten Netz). Zum Token eine ehrliche Einordnung: Es steckt dann auch im Launcher und ist dort auslesbar. Es hält Gelegenheitsnutzer ab, keine entschlossenen. Die Ratenbegrenzung ist der wirksamere Schutz für dein Kontingent. ### Reverse-Proxy: `TRUST_PROXY` richtig setzen `TRUST_PROXY` ist keine Ja/Nein-Angabe, sondern die **Anzahl der Stufen** vor dem Dienst: `0` ohne Proxy, `1` mit Nginx Proxy Manager, `2` mit Cloudflare davor. Der Grund: nginx **ergänzt** `X-Forwarded-For`, statt es zu ersetzen. Schickt ein Aufrufer selbst `X-Forwarded-For: 1.2.3.4`, steht hinterher `1.2.3.4, ` darin. Deshalb wird von rechts gezählt – die eigenen Proxy-Stufen sind die einzigen Einträge, die niemand von außen setzen kann. Zu hoch angesetzt, greift der Dienst auf einen Eintrag zu, den der Aufrufer selbst erfunden hat: Dann kann sich jeder mit jeder Anfrage eine neue Adresse ausdenken und die Ratenbegrenzung ins Leere laufen lassen. Im Zweifel `0`. ### Nginx Proxy Manager Im Proxy Host eintragen: | Feld | Wert | |---|---| | Scheme | `http` | | Forward Hostname / IP | Containername oder LAN-Adresse des Dienstes | | Forward Port | `8420` | | Block Common Exploits | an | | Websockets Support | nicht nötig | Im Reiter **SSL**: Let's-Encrypt-Zertifikat anfordern, „Force SSL" und HTTP/2 aktivieren. Ohne HTTPS ginge das Token im Klartext über die Leitung. Im Reiter **Advanced** empfiehlt sich für die durchgereichten Mod-Dateien: ```nginx proxy_buffering off; proxy_read_timeout 300s; ``` Ohne `proxy_buffering off` schreibt nginx große Dateien erst vollständig in eine temporäre Datei, bevor der Launcher das erste Byte sieht – bei mehreren hundert MB fühlt sich das wie ein Hänger an. Läuft der Nginx Proxy Manager selbst in Docker, gehören beide ins gleiche Docker-Netz und der Port muss gar nicht nach außen gebunden werden: In der `docker-compose.yml` dann den `ports`-Block weglassen. Sonst wenigstens auf `127.0.0.1:8420:8420` einschränken, damit der Dienst nicht an der Absicherung des Proxys vorbei erreichbar ist. ## Was der Dienst nicht tut Er speichert nichts, protokolliert keine Adressen der Nutzer und gibt Schlüsselfehler von CurseForge nicht im Wortlaut nach außen weiter.