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:
+132
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user