feat: add trusted mirror update source

Version checks and downloads previously depended on GitHub Release. About & Updates now offers GitHub or a validated mirror source, keeps the selector beneath the startup-check switch, and disables it when startup checks are off.

The website resolves platform downloads from a bounded OSS release index with a GitHub fallback. Tagged releases publish and verify immutable OSS assets through OIDC before switching the latest-version index; deployment requires the configured Alibaba Cloud environment variables and role.

Release note: “关于与更新”新增 GitHub 与镜像节点选择,启动检查、手动检查和下载页使用同一可信来源;官网下载也可按系统、架构和安装包类型直接选择。
This commit is contained in:
mesalogo
2026-08-17 17:20:24 +08:00
parent ce15e7021c
commit dc8f86ff3e
32 changed files with 1680 additions and 66 deletions
+132
View File
@@ -12,6 +12,137 @@
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)");
const finePointer = window.matchMedia("(hover: hover) and (pointer: fine)");
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
const releaseManifestUrl =
"https://goodbuddy.oss-cn-hangzhou.aliyuncs.com/releases/latest.json";
const releaseFallbackUrl =
"https://github.com/mesalogo/goodbuddy/releases/latest";
const releaseStatus = document.querySelector("[data-release-status]");
const downloadCards = [
...document.querySelectorAll("[data-download-card]"),
];
const platformNames = {
windows: "Windows",
macos: "macOS",
linux: "Linux",
};
const formatNames = {
nsis: "安装版",
portable: "便携版",
dmg: "DMG",
zip: "ZIP",
AppImage: "AppImage",
deb: "DEB",
};
const formatFileSize = (bytes) => {
const megabytes = bytes / (1024 * 1024);
return `${megabytes >= 100 ? megabytes.toFixed(0) : megabytes.toFixed(1)} MB`;
};
const isTrustedReleaseUrl = (value) => {
try {
const url = new URL(value);
return (
url.protocol === "https:" &&
url.hostname === "goodbuddy.oss-cn-hangzhou.aliyuncs.com" &&
url.pathname.startsWith("/releases/")
);
} catch {
return false;
}
};
const configureDownloads = (release) => {
if (
release?.formatVersion !== 1 ||
release?.productName !== "GoodBuddy" ||
typeof release?.version !== "string" ||
!release?.targets
) {
throw new Error("发布索引格式无效");
}
const updateCard = (card) => {
const platform = card.dataset.downloadCard;
const archSelect = card.querySelector("[data-download-arch]");
const formatSelect = card.querySelector("[data-download-format]");
const link = card.closest(".download-card")?.querySelector("[data-release-link]");
const meta = card.closest(".download-card")?.querySelector("[data-download-meta]");
if (
!platform ||
!(archSelect instanceof HTMLSelectElement) ||
!(formatSelect instanceof HTMLSelectElement) ||
!(link instanceof HTMLAnchorElement) ||
!(meta instanceof HTMLElement)
) {
return;
}
const target = release.targets[`${platform}-${archSelect.value}`];
const file = target?.files?.[formatSelect.value];
if (
!file ||
typeof file.name !== "string" ||
!Number.isSafeInteger(file.size) ||
file.size < 1 ||
!isTrustedReleaseUrl(file.url)
) {
link.href = releaseFallbackUrl;
link.textContent =
`前往 GitHub 下载 ${platformNames[platform] ?? platform}`;
meta.textContent = "当前选项暂不可用,请在 GitHub Release 中选择文件。";
return;
}
link.href = file.url;
const platformName = platformNames[platform] ?? platform;
const archName =
platform === "macos" && archSelect.value === "arm64"
? "Apple 芯片"
: archSelect.value === "arm64"
? "ARM64"
: "x64";
const formatName = formatNames[formatSelect.value] ?? formatSelect.value;
link.textContent = `下载 ${platformName} ${archName} ${formatName}`;
meta.textContent =
`GoodBuddy ${release.version} · ${formatFileSize(file.size)} · ` +
`${archSelect.options[archSelect.selectedIndex]?.text ?? archSelect.value}`;
};
for (const card of downloadCards) {
const selects = card.querySelectorAll("select");
for (const select of selects) {
select.addEventListener("change", () => updateCard(card));
}
updateCard(card);
}
if (releaseStatus instanceof HTMLElement) {
releaseStatus.textContent =
`官方下载源已就绪:GoodBuddy ${release.version}` +
"请选择处理器和安装包类型。";
releaseStatus.classList.add("is-ready");
}
};
const loadRelease = async () => {
try {
const response = await fetch(releaseManifestUrl, {
cache: "no-store",
credentials: "omit",
});
if (!response.ok) {
throw new Error(`发布索引请求失败:${response.status}`);
}
configureDownloads(await response.json());
} catch {
if (releaseStatus instanceof HTMLElement) {
releaseStatus.textContent =
"官方下载源暂不可用,下载按钮已切换到 GitHub Release。";
releaseStatus.classList.add("is-fallback");
}
}
};
const getSavedTheme = () => {
try {
@@ -51,6 +182,7 @@
applyTheme(getSavedTheme() ?? (systemTheme.matches ? "dark" : "light"));
setHeaderState();
void loadRelease();
themeToggle?.addEventListener("click", () => {
applyTheme(root.dataset.theme === "dark" ? "light" : "dark", true);