Support public software auto update URLs

This commit is contained in:
lwt
2026-06-09 10:36:45 +08:00
parent 20f1adf483
commit 8fa911390a
+38 -14
View File
@@ -249,6 +249,22 @@ function publicPlayer(player) {
}; };
} }
function getPublicOrigin(req) {
const forwardedProto = String(req.get("x-forwarded-proto") || "").split(",")[0].trim();
const proto = forwardedProto || req.protocol || "http";
const forwardedHost = String(req.get("x-forwarded-host") || "").split(",")[0].trim();
const host = forwardedHost || req.get("host") || "";
return host ? `${proto}://${host}` : "";
}
function absoluteUrl(req, value) {
const url = String(value || "").trim();
if (!url || /^https?:\/\//i.test(url)) return url;
if (!url.startsWith("/")) return url;
const origin = getPublicOrigin(req);
return origin ? `${origin}${url}` : url;
}
function getPlayerSession(req) { function getPlayerSession(req) {
const token = parseCookies(req)[playerSessionCookie]; const token = parseCookies(req)[playerSessionCookie];
if (!token) return null; if (!token) return null;
@@ -410,6 +426,10 @@ function getProtectedDownloadUrl(item) {
return resolveDownloadUrl(item) ? `/api/software/${encodeURIComponent(item.id)}/download` : ""; return resolveDownloadUrl(item) ? `/api/software/${encodeURIComponent(item.id)}/download` : "";
} }
function getUpdateDownloadUrl(item) {
return resolveDownloadUrl(item) ? `/api/software/${encodeURIComponent(item.id)}/update-download` : "";
}
function getReleaseNotes(item, manifest) { function getReleaseNotes(item, manifest) {
return manifest.release_notes || manifest.releaseNotes || manifest.changelog || item.changelog || ""; return manifest.release_notes || manifest.releaseNotes || manifest.changelog || item.changelog || "";
} }
@@ -453,11 +473,12 @@ function applyVersionManifest(item, sourceManifest, mode = "auto") {
return item; return item;
} }
function buildVersionManifest(item) { function buildVersionManifest(item, req) {
const sourceManifest = item.versionManifest && typeof item.versionManifest === "object" ? item.versionManifest : {}; const sourceManifest = item.versionManifest && typeof item.versionManifest === "object" ? item.versionManifest : {};
const latestVersion = getLatestVersion(item); const latestVersion = getLatestVersion(item);
const downloadUrl = getProtectedDownloadUrl(item); const downloadUrl = absoluteUrl(req, getUpdateDownloadUrl(item));
const versionManifestUrl = `/api/software/${encodeURIComponent(item.id)}/version.json`; const versionManifestUrl = `/api/software/${encodeURIComponent(item.id)}/version.json`;
const updateUrl = absoluteUrl(req, versionManifestUrl);
const releaseNotes = getReleaseNotes(item, sourceManifest); const releaseNotes = getReleaseNotes(item, sourceManifest);
const minimumVersion = sourceManifest.min_supported_version || sourceManifest.minSupportedVersion || sourceManifest.minimumVersion || sourceManifest.minVersion || ""; const minimumVersion = sourceManifest.min_supported_version || sourceManifest.minSupportedVersion || sourceManifest.minimumVersion || sourceManifest.minVersion || "";
@@ -473,8 +494,8 @@ function buildVersionManifest(item) {
size: sourceManifest.size || item.size || "", size: sourceManifest.size || item.size || "",
sizeBytes: sourceManifest.sizeBytes ?? item.sizeBytes ?? 0, sizeBytes: sourceManifest.sizeBytes ?? item.sizeBytes ?? 0,
sha256: sourceManifest.sha256 || item.sha256 || "", sha256: sourceManifest.sha256 || item.sha256 || "",
updateUrl: versionManifestUrl, updateUrl,
update_url: versionManifestUrl, update_url: updateUrl,
downloadUrl, downloadUrl,
download_url: downloadUrl, download_url: downloadUrl,
fullInstallerUrl: downloadUrl, fullInstallerUrl: downloadUrl,
@@ -485,13 +506,13 @@ function buildVersionManifest(item) {
min_supported_version: minimumVersion, min_supported_version: minimumVersion,
deltaUpdates: sourceManifest.deltaUpdates || sourceManifest.delta_updates || {}, deltaUpdates: sourceManifest.deltaUpdates || sourceManifest.delta_updates || {},
delta_updates: sourceManifest.delta_updates || sourceManifest.deltaUpdates || {}, delta_updates: sourceManifest.delta_updates || sourceManifest.deltaUpdates || {},
checkUpdateUrl: `/api/software/${encodeURIComponent(item.id)}/check-update`, checkUpdateUrl: absoluteUrl(req, `/api/software/${encodeURIComponent(item.id)}/check-update`),
versionManifestUrl, versionManifestUrl: updateUrl,
}; };
} }
function buildUpdateCheck(item, currentVersion) { function buildUpdateCheck(item, currentVersion, req) {
const manifest = buildVersionManifest(item); const manifest = buildVersionManifest(item, req);
const latestVersion = manifest.version; const latestVersion = manifest.version;
const comparison = compareVersions(currentVersion, latestVersion); const comparison = compareVersions(currentVersion, latestVersion);
const minimumVersion = manifest.min_supported_version || manifest.minSupportedVersion || manifest.minimumVersion || manifest.minVersion || ""; const minimumVersion = manifest.min_supported_version || manifest.minSupportedVersion || manifest.minimumVersion || manifest.minVersion || "";
@@ -697,7 +718,7 @@ app.get("/api/software/:id/latest", async (req, res, next) => {
res.status(404).json({ error: "Software not found" }); res.status(404).json({ error: "Software not found" });
return; return;
} }
const manifest = buildVersionManifest(item); const manifest = buildVersionManifest(item, req);
res.json({ res.json({
id: item.id, id: item.id,
name: item.name, name: item.name,
@@ -731,7 +752,7 @@ app.get("/api/software/:id/version.json", async (req, res, next) => {
res.status(404).json({ error: "Software not found" }); res.status(404).json({ error: "Software not found" });
return; return;
} }
res.json(buildVersionManifest(item)); res.json(buildVersionManifest(item, req));
} catch (error) { } catch (error) {
next(error); next(error);
} }
@@ -751,7 +772,7 @@ app.get("/api/software/:id/check-update", async (req, res, next) => {
res.status(404).json({ error: "Software not found" }); res.status(404).json({ error: "Software not found" });
return; return;
} }
res.json(buildUpdateCheck(item, currentVersion)); res.json(buildUpdateCheck(item, currentVersion, req));
} catch (error) { } catch (error) {
next(error); next(error);
} }
@@ -771,13 +792,13 @@ app.post("/api/software/:id/check-update", async (req, res, next) => {
res.status(404).json({ error: "Software not found" }); res.status(404).json({ error: "Software not found" });
return; return;
} }
res.json(buildUpdateCheck(item, currentVersion)); res.json(buildUpdateCheck(item, currentVersion, req));
} catch (error) { } catch (error) {
next(error); next(error);
} }
}); });
app.get("/api/software/:id/download", requirePlayer, async (req, res, next) => { async function sendSoftwareDownload(req, res, next) {
try { try {
const store = await readStore(); const store = await readStore();
const item = store.items.find((entry) => entry.id === req.params.id && entry.active !== false); const item = store.items.find((entry) => entry.id === req.params.id && entry.active !== false);
@@ -810,7 +831,10 @@ app.get("/api/software/:id/download", requirePlayer, async (req, res, next) => {
} catch (error) { } catch (error) {
next(error); next(error);
} }
}); }
app.get("/api/software/:id/update-download", sendSoftwareDownload);
app.get("/api/software/:id/download", requirePlayer, sendSoftwareDownload);
app.get("/api/admin/software", requirePlayer, requireAdmin, async (req, res, next) => { app.get("/api/admin/software", requirePlayer, requireAdmin, async (req, res, next) => {
try { try {