Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7f0939279 | ||
|
|
cd08d72b2c | ||
|
|
2760253b81 | ||
|
|
b7fad0d874 | ||
|
|
f6fcc5096a | ||
|
|
32fc09f152 | ||
|
|
53aefae162 | ||
|
|
94cb1b9b62 | ||
|
|
b242e64678 |
+8
-2
@@ -2,13 +2,19 @@
|
|||||||
|
|
||||||
Alle nennenswerten Änderungen an AeroMC werden hier festgehalten.
|
Alle nennenswerten Änderungen an AeroMC werden hier festgehalten.
|
||||||
|
|
||||||
|
## [1.3.2] - 2026-11-09
|
||||||
|
|
||||||
|
### Neu
|
||||||
|
- CurseForge: Die Suche im Katalog funktioniert jetzt ohne eigenen API-Key. Mods und Modpacks lassen sich direkt aus CurseForge suchen, installieren und importieren, so wie bei Modrinth
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
- Die Hinweise in den Netzwerk-Einstellungen und im Importfenster zu CurseForge sind angepasst: Ein eigener Key ist nur noch optional
|
||||||
|
|
||||||
## [1.3.1] - 2026-11-02
|
## [1.3.1] - 2026-11-02
|
||||||
|
|
||||||
### Geändert
|
### Geändert
|
||||||
- CurseForge: Mods, die ihre Autoren nicht über Launcher freigeben, werden nicht mehr geladen. Beim Modpack-Import legt AeroMC stattdessen die Liste „GESPERRTE-MODS.txt“ mit Links zum Selbstladen in den Spielordner, bei einzelnen Mods erscheint eine Meldung mit dem Link
|
- CurseForge: Mods, die ihre Autoren nicht über Launcher freigeben, werden nicht mehr geladen. Beim Modpack-Import legt AeroMC stattdessen die Liste „GESPERRTE-MODS.txt“ mit Links zum Selbstladen in den Spielordner, bei einzelnen Mods erscheint eine Meldung mit dem Link
|
||||||
- Stream Deck: Das Plugin ist jetzt in Fassung 1.1.1.0 dabei. Bei eingeschaltetem „Stream Deck erlauben“ aktualisiert AeroMC es von selbst, danach die Stream-Deck-Software einmal neu starten
|
- Stream Deck: Das Plugin ist jetzt in Fassung 1.1.1.0 dabei. Bei eingeschaltetem „Stream Deck erlauben“ aktualisiert AeroMC es von selbst, danach die Stream-Deck-Software einmal neu starten
|
||||||
|
|
||||||
### Verbessert
|
|
||||||
- Kleinere Verbesserungen und Aktualisierungen im Hintergrund
|
- Kleinere Verbesserungen und Aktualisierungen im Hintergrund
|
||||||
|
|
||||||
## [1.3.0] - 2026-10-30
|
## [1.3.0] - 2026-10-30
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# AeroMC – Sicherheit
|
# AeroMC – Sicherheit
|
||||||
|
|
||||||
Überblick über die Sicherheitsmaßnahmen im AeroMC Launcher (Stand: Version 1.3.1).
|
Überblick über die Sicherheitsmaßnahmen im AeroMC Launcher (Stand: Version 1.3.2).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj": {
|
"C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"restore": {
|
"restore": {
|
||||||
"projectUniqueName": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
"projectUniqueName": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
||||||
"projectName": "AeroMC-Setup",
|
"projectName": "AeroMC-Setup",
|
||||||
|
|||||||
Binary file not shown.
@@ -14,11 +14,11 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright (c) M_Viper. Alle Rechte vorbehalten.")]
|
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright (c) M_Viper. Alle Rechte vorbehalten.")]
|
||||||
[assembly: System.Reflection.AssemblyDescriptionAttribute("Installiert AeroMC")]
|
[assembly: System.Reflection.AssemblyDescriptionAttribute("Installiert AeroMC")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.3.1.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.3.2.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.3.1")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.3.2")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("AeroMC Setup")]
|
[assembly: System.Reflection.AssemblyProductAttribute("AeroMC Setup")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("AeroMC Setup")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("AeroMC Setup")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.3.1.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.3.2.0")]
|
||||||
[assembly: System.Resources.NeutralResourcesLanguageAttribute("de-DE")]
|
[assembly: System.Resources.NeutralResourcesLanguageAttribute("de-DE")]
|
||||||
|
|
||||||
// Von der MSBuild WriteCodeFragment-Klasse generiert.
|
// Von der MSBuild WriteCodeFragment-Klasse generiert.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
580c54ad965e1465e6447eef31337065bb9284a8dba224aec55fdd4c0fb93d67
|
2821045dd08c55a3267e014e5026ac3588e68340b7d53441249b44302c5f3e33
|
||||||
|
|||||||
@@ -418,7 +418,7 @@
|
|||||||
"C:\\Users\\Viper\\.nuget\\packages\\": {}
|
"C:\\Users\\Viper\\.nuget\\packages\\": {}
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"restore": {
|
"restore": {
|
||||||
"projectUniqueName": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
"projectUniqueName": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
||||||
"projectName": "AeroMC-Setup",
|
"projectName": "AeroMC-Setup",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "SW1JzBEcPG8=",
|
"dgSpecHash": "dC0MHaIYOYg=",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
"projectFilePath": "C:\\Users\\Viper\\Desktop\\AeroMC-Launcher\\installer\\AeroMCSetup.csproj",
|
||||||
"expectedPackageFiles": [
|
"expectedPackageFiles": [
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xhayper/discord-rpc": "^1.3.4",
|
"@xhayper/discord-rpc": "^1.3.4",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "aeromc",
|
"name": "aeromc",
|
||||||
"productName": "AeroMC",
|
"productName": "AeroMC",
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
|
"description": "AeroMC – Instanz-basierter Minecraft-Launcher (à la MultiMC/Prism)",
|
||||||
"homepage": "https://m-viper.de",
|
"homepage": "https://m-viper.de",
|
||||||
"launcherWebsite": "https://aeromc.viper.ipv64.net",
|
"launcherWebsite": "https://aeromc.viper.ipv64.net",
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ auf dem Server, und der Launcher kennt nur die Adresse dieses Dienstes.
|
|||||||
| `GET /health` | Lebenszeichen, verrät nichts über den Schlüssel |
|
| `GET /health` | Lebenszeichen, verrät nichts über den Schlüssel |
|
||||||
| `POST /v1/mods/files` | Dateiliste auflösen, Rumpf `{ "fileIds": [...] }`, höchstens 100 je Anfrage |
|
| `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/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/<ID>/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
|
## Einrichten
|
||||||
|
|
||||||
@@ -52,14 +59,19 @@ pm2 save
|
|||||||
pm2 startup # einmalig, für den Start nach einem Neustart
|
pm2 startup # einmalig, für den Start nach einem Neustart
|
||||||
```
|
```
|
||||||
|
|
||||||
Nach jeder Änderung an der `.env`:
|
Nach jeder Änderung an der `.env` den Eintrag entfernen und neu aus der
|
||||||
|
`ecosystem.config.js` starten:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 restart aeromc-cf-proxy --update-env
|
pm2 delete aeromc-cf-proxy
|
||||||
|
pm2 start ecosystem.config.js
|
||||||
|
pm2 save
|
||||||
```
|
```
|
||||||
|
|
||||||
Ohne `--update-env` behält pm2 die alten Werte, und du suchst lange nach dem
|
Ein bloßes `pm2 restart aeromc-cf-proxy --update-env` reicht **nicht**: Die
|
||||||
Grund, warum der neue Schlüssel nicht greift.
|
`.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:
|
Nachsehen, ob er läuft:
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,49 @@ async function dateilisteAufloesen(req, res) {
|
|||||||
sendeJson(res, 200, await antwort.json());
|
sendeJson(res, 200, await antwort.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Katalog-Wege: Suche, Kategorien und Dateiliste einer Mod. Es wird nur durchgereicht,
|
||||||
|
* was der Launcher dafür braucht – jeder Weg hat eine feste Liste erlaubter Parameter,
|
||||||
|
* andere werden verworfen. gameId ist immer Minecraft (432), damit sich der Dienst nicht
|
||||||
|
* für andere Spiele missbrauchen lässt. Antworten werden NICHT zwischengespeichert
|
||||||
|
* (die CurseForge-Bedingungen verbieten es, Daten aus der API zu speichern).
|
||||||
|
*/
|
||||||
|
const MC_SPIEL = '432';
|
||||||
|
const ganz = (min, max) => (v) => /^\d{1,9}$/.test(v) && Number(v) >= min && Number(v) <= max;
|
||||||
|
const text = (maxLaenge) => (v) => v.length > 0 && v.length <= maxLaenge && !/[\u0000-\u001f]/.test(v);
|
||||||
|
const KATALOG_PARAMETER = {
|
||||||
|
suche: {
|
||||||
|
classId: ganz(0, 100000), categoryId: ganz(0, 100000),
|
||||||
|
searchFilter: text(100), gameVersion: text(32),
|
||||||
|
modLoaderType: ganz(0, 6), sortField: ganz(1, 8),
|
||||||
|
sortOrder: (v) => v === 'asc' || v === 'desc',
|
||||||
|
pageSize: ganz(1, 50), index: ganz(0, 10000),
|
||||||
|
},
|
||||||
|
kategorien: { classId: ganz(0, 100000) },
|
||||||
|
dateien: { gameVersion: text(32), modLoaderType: ganz(0, 6), pageSize: ganz(1, 50), index: ganz(0, 10000) },
|
||||||
|
};
|
||||||
|
const MAX_KATALOG_BYTES = 4 * 1024 * 1024;
|
||||||
|
|
||||||
|
async function katalogAnfrage(res, url, cfPfad, erlaubt) {
|
||||||
|
const ziel = new URL(CF_API + cfPfad);
|
||||||
|
if (erlaubt === KATALOG_PARAMETER.suche || erlaubt === KATALOG_PARAMETER.kategorien) ziel.searchParams.set('gameId', MC_SPIEL);
|
||||||
|
for (const [name, wert] of url.searchParams) {
|
||||||
|
const pruefer = erlaubt[name];
|
||||||
|
if (!pruefer || !pruefer(wert)) continue; // unbekannte oder ungültige Parameter einfach weglassen
|
||||||
|
ziel.searchParams.set(name, wert);
|
||||||
|
}
|
||||||
|
const antwort = await fetch(ziel, { headers: cfKopfzeilen(), signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS) });
|
||||||
|
if (!antwort.ok) {
|
||||||
|
// Schlüsselprobleme nicht im Wortlaut weiterreichen
|
||||||
|
const code = antwort.status === 401 || antwort.status === 403 ? 502 : antwort.status;
|
||||||
|
return sendeJson(res, code, { error: 'CurseForge antwortet mit HTTP ' + antwort.status });
|
||||||
|
}
|
||||||
|
const roh = await antwort.text();
|
||||||
|
if (roh.length > MAX_KATALOG_BYTES) return sendeJson(res, 502, { error: 'Antwort von CurseForge zu groß.' });
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(roh) });
|
||||||
|
res.end(roh);
|
||||||
|
}
|
||||||
|
|
||||||
async function dateiDurchreichen(req, res, url) {
|
async function dateiDurchreichen(req, res, url) {
|
||||||
const ziel = pruefeDownloadZiel(url.searchParams.get('url') || '');
|
const ziel = pruefeDownloadZiel(url.searchParams.get('url') || '');
|
||||||
if (!ziel) return sendeJson(res, 400, { error: 'Adresse fehlt oder zeigt nicht auf das CurseForge-CDN.' });
|
if (!ziel) return sendeJson(res, 400, { error: 'Adresse fehlt oder zeigt nicht auf das CurseForge-CDN.' });
|
||||||
@@ -386,6 +429,10 @@ const server = http.createServer(async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
if (req.method === 'POST' && url.pathname === '/v1/mods/files') return await dateilisteAufloesen(req, res);
|
if (req.method === 'POST' && url.pathname === '/v1/mods/files') return await dateilisteAufloesen(req, res);
|
||||||
if (req.method === 'GET' && url.pathname === '/v1/download') return await dateiDurchreichen(req, res, url);
|
if (req.method === 'GET' && url.pathname === '/v1/download') return await dateiDurchreichen(req, res, url);
|
||||||
|
if (req.method === 'GET' && url.pathname === '/v1/mods/search') return await katalogAnfrage(res, url, '/mods/search', KATALOG_PARAMETER.suche);
|
||||||
|
if (req.method === 'GET' && url.pathname === '/v1/categories') return await katalogAnfrage(res, url, '/categories', KATALOG_PARAMETER.kategorien);
|
||||||
|
const modDateien = req.method === 'GET' && /^\/v1\/mods\/(\d{1,9})\/files$/.exec(url.pathname);
|
||||||
|
if (modDateien) return await katalogAnfrage(res, url, '/mods/' + modDateien[1] + '/files', KATALOG_PARAMETER.dateien);
|
||||||
sendeJson(res, 404, { error: 'Unbekannter Weg.' });
|
sendeJson(res, 404, { error: 'Unbekannter Weg.' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Fehler]', err.message);
|
console.error('[Fehler]', err.message);
|
||||||
|
|||||||
+2
-2
@@ -561,7 +561,7 @@
|
|||||||
</section>
|
</section>
|
||||||
<section class="settings-card">
|
<section class="settings-card">
|
||||||
<h5>CurseForge</h5>
|
<h5>CurseForge</h5>
|
||||||
<small class="hint s-vorab">Nur für CurseForge-Modpacks und -Mods nötig. Modrinth funktioniert immer ohne.</small>
|
<small class="hint s-vorab">Für CurseForge ist normalerweise nichts nötig: AeroMC nutzt die Weiterleitung unten. Ein eigener Key ist nur für den Fall gedacht, dass du sie nicht nutzen willst. Modrinth funktioniert immer ohne.</small>
|
||||||
<div class="s-zeile s-voll">
|
<div class="s-zeile s-voll">
|
||||||
<div class="s-text"><b>Eigener API-Key</b><small>Von CurseForge for Studios (Profil → Schlüssel erstellen, beginnt mit cfc_pat_…) oder ein Organisationsschlüssel. Nach dem Eintragen speichern.</small></div>
|
<div class="s-text"><b>Eigener API-Key</b><small>Von CurseForge for Studios (Profil → Schlüssel erstellen, beginnt mit cfc_pat_…) oder ein Organisationsschlüssel. Nach dem Eintragen speichern.</small></div>
|
||||||
<div class="path-row">
|
<div class="path-row">
|
||||||
@@ -1629,7 +1629,7 @@
|
|||||||
|
|
||||||
<div id="import-pane-modpack" class="import-pane hidden">
|
<div id="import-pane-modpack" class="import-pane hidden">
|
||||||
<p class="import-copy">Modpack aus dem Katalog installieren oder als Datei importieren.</p>
|
<p class="import-copy">Modpack aus dem Katalog installieren oder als Datei importieren.</p>
|
||||||
<p class="hint">Katalog-Suche läuft über Modrinth oder CurseForge. Für CurseForge wird ein eigener API-Key in den Netzwerk-Einstellungen gebraucht (die Weiterleitung reicht nur für den Datei-Import). Datei-Import unterstützt zusätzlich CurseForge-ZIP (manifest.json).</p>
|
<p class="hint">Katalog-Suche läuft über Modrinth oder CurseForge. Datei-Import unterstützt zusätzlich CurseForge-ZIP (manifest.json).</p>
|
||||||
|
|
||||||
<div class="field-row" style="margin-top:12px;align-items:flex-end">
|
<div class="field-row" style="margin-top:12px;align-items:flex-end">
|
||||||
<label class="field" style="width:150px">
|
<label class="field" style="width:150px">
|
||||||
|
|||||||
@@ -4214,6 +4214,13 @@ ipcMain.handle('modpack:installFromCurseForge', async (_e, project) => {
|
|||||||
const gameVersion = (project && project.gameVersion) || '';
|
const gameVersion = (project && project.gameVersion) || '';
|
||||||
broadcast('launch:progress', { phase: 'modpack', detail: 'Modpack-Version wird ermittelt …' });
|
broadcast('launch:progress', { phase: 'modpack', detail: 'Modpack-Version wird ermittelt …' });
|
||||||
const file = await services.curseforgeResolveModpackFile(zugang, modId, gameVersion);
|
const file = await services.curseforgeResolveModpackFile(zugang, modId, gameVersion);
|
||||||
|
if (file && file.gesperrt) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
message: 'Der Autor erlaubt kein Laden dieses Modpacks über Launcher. Bitte von Hand laden und über „Importieren“ einlesen: '
|
||||||
|
+ services.cfProjektSeite(modId),
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!file || !file.url) {
|
if (!file || !file.url) {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
|
|||||||
+17
-14
@@ -586,8 +586,8 @@ function cfLoaderType(loader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CurseForge-Katalogsuche. Braucht direkten API-Key (modus === 'direkt').
|
* CurseForge-Katalogsuche – mit eigenem API-Key (modus 'direkt') oder über die
|
||||||
* Die Weiterleitung deckt absichtlich nur Dateiauflösung und Download ab.
|
* Weiterleitung (modus 'weiterleitung'); beide haben dieselben Pfade unter zugang.basis.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -653,13 +653,13 @@ async function curseforgeSearch(zugang, {
|
|||||||
query = '', gameVersion = '', loader = '', sort = 'popularity',
|
query = '', gameVersion = '', loader = '', sort = 'popularity',
|
||||||
limit = 30, offset = 0, art = 'mod',
|
limit = 30, offset = 0, art = 'mod',
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (!zugang || zugang.modus !== 'direkt') {
|
// Direkt (eigener Key) und über die Weiterleitung laufen über dieselben Pfade
|
||||||
|
if (!zugang) {
|
||||||
return {
|
return {
|
||||||
total: 0,
|
total: 0,
|
||||||
hits: [],
|
hits: [],
|
||||||
needKey: true,
|
needKey: true,
|
||||||
error: 'Für die CurseForge-Suche brauchst du einen eigenen API-Key in den Netzwerk-Einstellungen. '
|
error: 'Kein CurseForge-Zugang eingerichtet. Trage unter Optionen → Netzwerk die Adresse der Weiterleitung oder einen eigenen API-Key ein.',
|
||||||
+ 'Die Weiterleitung reicht für Downloads, nicht für den Katalog.',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!CF_CLASS[art]) {
|
if (!CF_CLASS[art]) {
|
||||||
@@ -668,7 +668,7 @@ async function curseforgeSearch(zugang, {
|
|||||||
return { total: 0, hits: [], error: 'CurseForge unterstützt diese Inhaltsart hier nicht – bitte Modrinth nutzen.' };
|
return { total: 0, hits: [], error: 'CurseForge unterstützt diese Inhaltsart hier nicht – bitte Modrinth nutzen.' };
|
||||||
}
|
}
|
||||||
const classId = CF_CLASS[art];
|
const classId = CF_CLASS[art];
|
||||||
const url = new URL(CURSEFORGE + '/mods/search');
|
const url = new URL(zugang.basis + '/mods/search');
|
||||||
url.searchParams.set('gameId', '432');
|
url.searchParams.set('gameId', '432');
|
||||||
url.searchParams.set('classId', String(classId));
|
url.searchParams.set('classId', String(classId));
|
||||||
if (query) url.searchParams.set('searchFilter', query);
|
if (query) url.searchParams.set('searchFilter', query);
|
||||||
@@ -696,13 +696,16 @@ async function curseforgeSearch(zugang, {
|
|||||||
return {
|
return {
|
||||||
total: 0,
|
total: 0,
|
||||||
hits: [],
|
hits: [],
|
||||||
needKey: true,
|
needKey: zugang.modus === 'direkt',
|
||||||
error: 'CurseForge lehnt den API-Key ab (HTTP ' + res.status + '). '
|
error: zugang.modus === 'direkt'
|
||||||
+ 'Bitte unter Optionen → Netzwerk einen gültigen Schlüssel von console.curseforge.com eintragen. '
|
? 'CurseForge lehnt den API-Key ab (HTTP ' + res.status + '). '
|
||||||
+ 'Das Proxy-Token ist kein API-Key.'
|
+ 'Bitte unter Optionen → Netzwerk einen gültigen Schlüssel von console.curseforge.com eintragen. '
|
||||||
+ (detail ? ' (' + detail + ')' : ''),
|
+ 'Das Proxy-Token ist kein API-Key.'
|
||||||
|
+ (detail ? ' (' + detail + ')' : '')
|
||||||
|
: 'Der CurseForge-Server weist die Anfrage zurück (HTTP ' + res.status + ').',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (res.status === 429) throw new Error('Zu viele Anfragen – bitte einen Moment warten.');
|
||||||
throw new Error('CurseForge-Suche fehlgeschlagen: HTTP ' + res.status + (detail ? ' – ' + detail : ''));
|
throw new Error('CurseForge-Suche fehlgeschlagen: HTTP ' + res.status + (detail ? ' – ' + detail : ''));
|
||||||
}
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@@ -750,12 +753,12 @@ function cfFileMatches(file, gameVersion, loader, art) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function curseforgePickFile(zugang, modId, gameVersion, loader, art) {
|
async function curseforgePickFile(zugang, modId, gameVersion, loader, art) {
|
||||||
if (!zugang || zugang.modus !== 'direkt') {
|
if (!zugang) {
|
||||||
const err = new Error('CurseForge-Dateiauswahl braucht einen eigenen API-Key.');
|
const err = new Error('Kein CurseForge-Zugang eingerichtet.');
|
||||||
err.code = 'need-key';
|
err.code = 'need-key';
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const url = new URL(CURSEFORGE + '/mods/' + modId + '/files');
|
const url = new URL(zugang.basis + '/mods/' + modId + '/files');
|
||||||
url.searchParams.set('pageSize', '50');
|
url.searchParams.set('pageSize', '50');
|
||||||
url.searchParams.set('index', '0');
|
url.searchParams.set('index', '0');
|
||||||
if (gameVersion) url.searchParams.set('gameVersion', gameVersion);
|
if (gameVersion) url.searchParams.set('gameVersion', gameVersion);
|
||||||
|
|||||||
Reference in New Issue
Block a user