Upload via GUI (194 Dateien)

This commit is contained in:
2026-10-07 07:32:02 +00:00
parent 7e7a2021ed
commit 3ac5a6c14d
20 changed files with 2196 additions and 489 deletions
+47 -8
View File
@@ -299,6 +299,7 @@ async function modrinthResolveFile({ projectId, gameVersion, loader, art = 'mod'
versionNumber: v.version_number,
filename: file.filename,
url: file.url,
hashes: mrHashes(file),
sha1: file.hashes && file.hashes.sha1,
size: file.size,
dependencies: v.dependencies || [],
@@ -324,6 +325,7 @@ async function modrinthResolveFile({ projectId, gameVersion, loader, art = 'mod'
versionNumber: v.version_number,
filename: file.filename,
url: file.url,
hashes: mrHashes(file),
sha1: file.hashes && file.hashes.sha1,
size: file.size,
dependencies: v.dependencies || [],
@@ -350,23 +352,58 @@ async function modrinthResolveModpack({ projectId, gameVersion = '' } = {}) {
name: v.name || v.version_number,
filename: file.filename,
url: file.url,
hashes: mrHashes(file),
size: file.size || 0,
gameVersions: v.game_versions || [],
loaders: v.loaders || [],
};
}
async function downloadBuffer(url, extraHeaders) {
const res = await fetch(url, { headers: Object.assign({ 'User-Agent': UA }, extraHeaders || {}) });
if (!res.ok) throw new Error('Download fehlgeschlagen: HTTP ' + res.status);
return Buffer.from(await res.arrayBuffer());
/*
* Prüfsumme eines Downloads kontrollieren. erwartet: { sha512?, sha1? } – die
* Werte stammen aus der Modrinth- bzw. CurseForge-API. Ohne Angabe wird nichts
* geprüft (z. B. Dateien, zu denen die Quelle keine Prüfsumme nennt).
*/
function pruefeDownload(buf, erwartet, name) {
if (!erwartet) return;
for (const algo of ['sha512', 'sha256', 'sha1']) {
const soll = erwartet[algo] && String(erwartet[algo]).toLowerCase();
if (!soll) continue;
const ist = require('crypto').createHash(algo).update(buf).digest('hex');
if (ist !== soll) {
throw new Error(`Prüfsumme von ${name || 'Download'} stimmt nicht – Datei verworfen (beschädigt oder manipuliert).`);
}
return;
}
}
// extraHeaders: z. B. der CurseForge-API-Key, den das CDN seit Juli 2026 verlangt
async function downloadFile(url, destPath, extraHeaders) {
// CurseForge liefert Prüfsummen als Liste: algo 1 = SHA-1, 2 = MD5
function cfHashes(datei) {
const sha1 = ((datei && datei.hashes) || []).find((h) => h.algo === 1 && h.value);
return sha1 ? { sha1: sha1.value } : null;
}
// Modrinth: { sha512, sha1 }
function mrHashes(datei) {
const h = (datei && datei.hashes) || {};
return h.sha512 || h.sha1 ? { sha512: h.sha512, sha1: h.sha1 } : null;
}
async function downloadBuffer(url, extraHeaders, erwartet) {
const res = await fetch(url, { headers: Object.assign({ 'User-Agent': UA }, extraHeaders || {}) });
if (!res.ok) throw new Error('Download fehlgeschlagen: HTTP ' + res.status);
const buf = Buffer.from(await res.arrayBuffer());
pruefeDownload(buf, erwartet, decodeURIComponent(String(url).split('/').pop() || ''));
return buf;
}
// extraHeaders: z. B. der CurseForge-API-Key, den das CDN seit Juli 2026 verlangt
// erwartet: Prüfsummen (siehe pruefeDownload) – geschrieben wird nur, was passt
async function downloadFile(url, destPath, extraHeaders, erwartet) {
const res = await fetch(url, { headers: Object.assign({ 'User-Agent': UA }, extraHeaders || {}) });
if (!res.ok) throw new Error('Download fehlgeschlagen: HTTP ' + res.status);
const buf = Buffer.from(await res.arrayBuffer());
pruefeDownload(buf, erwartet, path.basename(destPath));
fs.mkdirSync(path.dirname(destPath), { recursive: true });
fs.writeFileSync(destPath, buf);
return buf.length;
@@ -383,7 +420,7 @@ async function modrinthInstall({ projectId, title, gameVersion, loader, modsDir,
: `Keine passende Version für ${gameVersion} gefunden.` };
}
const dest = path.join(modsDir, file.filename);
const bytes = await downloadFile(file.url, dest);
const bytes = await downloadFile(file.url, dest, null, file.hashes);
return {
ok: true,
dependencies: file.dependencies || [],
@@ -738,6 +775,7 @@ async function curseforgePickFile(zugang, modId, gameVersion, loader, art) {
fileId: match.id,
filename: name,
url: dl,
hashes: cfHashes(match),
versionNumber: match.displayName || match.fileName || String(match.id),
dependencies: match.dependencies || [],
};
@@ -768,7 +806,7 @@ async function curseforgeInstall({ zugang, projectId, title, gameVersion, loader
// Download: direkt mit Key oder über Weiterleitung
const url = zugang.downloadAdresse ? zugang.downloadAdresse(file.url) : file.url;
const headers = Object.assign({}, zugang.downloadKopfzeilen || {});
const bytes = await downloadFile(url, dest, headers);
const bytes = await downloadFile(url, dest, headers, file.hashes);
// Pflicht-Abhängigkeiten (relationType 3 = RequiredDependency)
const depIds = (file.dependencies || [])
@@ -826,6 +864,7 @@ async function curseforgeResolveFiles(zugang, files, onProgress) {
map.set(Number(d.id), {
fileName: d.fileName,
url: zugang.downloadAdresse(roh),
hashes: cfHashes(d),
kopfzeilen: zugang.downloadKopfzeilen,
});
}