Skip to content

Commit 6cd6607

Browse files
committed
Switch app update source to GitHub Releases
Change app update flow to use GitHub Releases download URLs and simplify updater logic. README updated to show the new Releases URL format. main.js: replaced custom app update endpoints with a GitHub Releases template, removed HTTPS-based version discovery and related helpers (download/parsing/detection), simplified getAppUpdateUrl to build a direct Releases download link using app.getVersion(), adjusted platform extension mapping (default -> zip), and removed unused https import. IPC handlers now return/open the direct download URL only. preload.js / settings.*: removed exposure and UI elements that displayed remote latest-version details (getAppUpdateInfo, appVersionInfo, appUpdateAdvice) and updated settings.js to only show the computed download URL. This simplifies update behavior but removes automatic latest-version checks and the getAppUpdateInfo API; callers should use getAppUpdateUrl or rely on GitHub Releases tags.
1 parent 8b6e6e9 commit 6cd6607

5 files changed

Lines changed: 30 additions & 162 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
- 每次启动可检查脚本更新
1010
- 正式版更新源:`https://xmoj-bbs.me/XMOJ.user.js`
1111
- 预览版更新源:`https://dev.xmoj-bbs.me/XMOJ.user.js`
12-
- App 下载更新源:`https://app.xmoj-bbs.me/{system}/{version}.{ext}`
13-
- ext 会按系统自动适配:`Windows=exe, macOS=dmg, Linux=AppImage`
12+
- App 下载更新源:GitHub Releases `https://github.com/XMOJ-Script-dev/ELXMOJ/releases/download/v{version}/ELXMOJ-{version}.{ext}`
13+
- ext 按平台自动适配:Windows `.exe` / macOS `.dmg` / Linux `.AppImage`
1414
- 发现新版本时弹窗提示用户是否更新
1515
- 设置持久化(通道、启动检查、自动注入)
1616
- 提供启动自检和手动自检

src/main.js

Lines changed: 27 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const path = require("node:path");
2-
const https = require("node:https");
32
const { app, BrowserWindow, dialog, ipcMain, Menu, net, session, shell } = require("electron");
43

54
const {
@@ -43,9 +42,7 @@ let lastCheckResult = null;
4342
const LOCAL_SCRIPT_PATH = path.join(__dirname, "..", "XMOJ.user.js");
4443
const XMOJ_HOME = "https://www.xmoj.tech";
4544
const USER_SCRIPT_DEBUG_MODE_KEY = "UserScript-Setting-DebugMode";
46-
const APP_UPDATE_URL_TEMPLATE = "https://app.xmoj-bbs.me/{system}/{version}.{ext}";
47-
const APP_UPDATE_BASE_URL = "https://app.xmoj-bbs.me";
48-
const APP_RELEASES_API_URL = "https://api.github.com/repos/XMOJ-Script-dev/ELXMOJ/releases/latest";
45+
const APP_UPDATE_URL_TEMPLATE = "https://github.com/XMOJ-Script-dev/ELXMOJ/releases/download/v{version}/ELXMOJ-{version}.{ext}";
4946
const PRELOAD_PATH = path.join(__dirname, "preload.js");
5047
const APP_ICON_PATH = path.join(
5148
__dirname,
@@ -66,134 +63,36 @@ function getPlatformPackageExtension() {
6663
if (process.platform === "win32") return "exe";
6764
if (process.platform === "darwin") return "dmg";
6865
if (process.platform === "linux") return "AppImage";
69-
return "bin";
66+
return "zip";
7067
}
7168

72-
function buildAppUpdateUrl(version) {
73-
const normalizedVersion = String(version || app.getVersion()).trim();
74-
const ext = getPlatformPackageExtension();
75-
return APP_UPDATE_URL_TEMPLATE
76-
.replace("{system}", getUpdateSystemName())
77-
.replace("{version}", normalizedVersion)
78-
.replace("{ext}", ext);
79-
}
80-
81-
function getAppUpdateUrl() {
82-
return buildAppUpdateUrl(app.getVersion());
83-
}
84-
85-
function downloadTextWithHeaders(url, headers = {}) {
86-
return new Promise((resolve, reject) => {
87-
const req = https.get(
88-
url,
89-
{
90-
timeout: 15000,
91-
headers
92-
},
93-
(res) => {
94-
if (res.statusCode !== 200) {
95-
reject(new Error(`HTTP ${res.statusCode} while downloading ${url}`));
96-
res.resume();
97-
return;
98-
}
99-
100-
const chunks = [];
101-
res.on("data", (chunk) => chunks.push(chunk));
102-
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
103-
}
104-
);
105-
106-
req.on("timeout", () => {
107-
req.destroy(new Error(`Timeout while downloading ${url}`));
108-
});
109-
req.on("error", reject);
110-
});
111-
}
112-
113-
function parseVersionFromLatestYml(ymlText) {
114-
const match = String(ymlText || "").match(/^\s*version\s*:\s*["']?([^"'\s]+)["']?/m);
115-
return match ? String(match[1]).trim() : "";
116-
}
117-
118-
function normalizeReleaseVersionTag(value) {
119-
const raw = String(value || "").trim();
120-
return raw.replace(/^v/i, "");
121-
}
69+
function getVersionSuffix(version) {
70+
const raw = String(version || "").trim();
71+
const semverMatch = raw.match(/^\d+\.\d+\.\d+(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
72+
const prerelease = semverMatch?.[1] || "";
12273

123-
async function detectLatestVersionFromAppSource() {
124-
const system = getUpdateSystemName();
125-
const latestJsonUrl = `${APP_UPDATE_BASE_URL}/${system}/latest.json`;
126-
const latestYmlUrl = `${APP_UPDATE_BASE_URL}/${system}/latest.yml`;
127-
128-
try {
129-
const raw = await downloadText(latestJsonUrl);
130-
const parsed = JSON.parse(raw);
131-
const version = normalizeReleaseVersionTag(parsed?.version || parsed?.latest || parsed?.tag || "");
132-
if (version) {
133-
return { ok: true, version, source: latestJsonUrl };
134-
}
135-
} catch {
136-
// fallback to yml below
137-
}
138-
139-
try {
140-
const raw = await downloadText(latestYmlUrl);
141-
const version = normalizeReleaseVersionTag(parseVersionFromLatestYml(raw));
142-
if (version) {
143-
return { ok: true, version, source: latestYmlUrl };
144-
}
145-
} catch {
146-
// fallback to GitHub release API below
74+
if (!prerelease) {
75+
return "release";
14776
}
14877

149-
return { ok: false, version: "", source: "" };
150-
}
78+
const normalized = prerelease.toLowerCase();
79+
if (normalized.startsWith("alpha")) return "alpha";
80+
if (normalized.startsWith("beta")) return "beta";
81+
if (normalized.startsWith("rc")) return "rc";
82+
if (normalized.startsWith("dev")) return "dev";
83+
if (normalized.startsWith("nightly")) return "nightly";
84+
if (normalized.startsWith("canary")) return "canary";
15185

152-
async function detectLatestVersionFromGitHub() {
153-
try {
154-
const raw = await downloadTextWithHeaders(APP_RELEASES_API_URL, {
155-
"User-Agent": "ELXMOJ-App-Updater"
156-
});
157-
const parsed = JSON.parse(raw);
158-
const version = normalizeReleaseVersionTag(parsed?.tag_name || parsed?.name || "");
159-
if (!version) {
160-
return { ok: false, version: "", source: APP_RELEASES_API_URL };
161-
}
162-
return { ok: true, version, source: APP_RELEASES_API_URL };
163-
} catch {
164-
return { ok: false, version: "", source: APP_RELEASES_API_URL };
165-
}
86+
// Fallback to prerelease tag itself when it is a custom identifier.
87+
return normalized.replace(/[^a-z0-9.-]/g, "") || "preview";
16688
}
16789

168-
async function getAppUpdateInfo() {
169-
const currentVersion = app.getVersion();
170-
let latest = await detectLatestVersionFromAppSource();
171-
172-
if (!latest.ok) {
173-
latest = await detectLatestVersionFromGitHub();
174-
}
175-
176-
if (!latest.ok || !latest.version) {
177-
return {
178-
ok: false,
179-
currentVersion,
180-
latestVersion: "",
181-
hasUpdate: false,
182-
downloadUrl: getAppUpdateUrl(),
183-
source: "",
184-
message: "未能获取最新版本。可在发布流程里生成并上传 latest.json 或 latest.yml(包含 version 字段),也可继续维护 GitHub Release 的最新 tag。"
185-
};
186-
}
187-
188-
return {
189-
ok: true,
190-
currentVersion,
191-
latestVersion: latest.version,
192-
hasUpdate: isNewerVersion(currentVersion, latest.version),
193-
downloadUrl: buildAppUpdateUrl(latest.version),
194-
source: latest.source,
195-
message: ""
196-
};
90+
function getAppUpdateUrl() {
91+
const version = app.getVersion();
92+
const ext = getPlatformPackageExtension();
93+
return APP_UPDATE_URL_TEMPLATE
94+
.replace("{version}", version)
95+
.replace("{ext}", ext);
19796
}
19897

19998
function getDebugModeFromChannel(channel) {
@@ -587,9 +486,8 @@ function createMainMenu() {
587486
submenu: [
588487
{
589488
label: "下载最新版本",
590-
click: async () => {
591-
const info = await getAppUpdateInfo();
592-
shell.openExternal(info.downloadUrl || getAppUpdateUrl()).catch(() => {
489+
click: () => {
490+
shell.openExternal(getAppUpdateUrl()).catch(() => {
593491
// Ignore failures to open update page
594492
});
595493
}
@@ -953,23 +851,14 @@ function registerIpcHandlers() {
953851
if (!isTrustedIpcSender(event)) {
954852
throw new Error("Unauthorized IPC sender");
955853
}
956-
const info = await getAppUpdateInfo();
957-
return info.downloadUrl || getAppUpdateUrl();
958-
});
959-
960-
ipcMain.handle("elxmoj:get-app-update-info", async (event) => {
961-
if (!isTrustedIpcSender(event)) {
962-
throw new Error("Unauthorized IPC sender");
963-
}
964-
return getAppUpdateInfo();
854+
return getAppUpdateUrl();
965855
});
966856

967857
ipcMain.handle("elxmoj:open-app-update-page", async (event) => {
968858
if (!isTrustedIpcSender(event)) {
969859
throw new Error("Unauthorized IPC sender");
970860
}
971-
const info = await getAppUpdateInfo();
972-
const url = info.downloadUrl || getAppUpdateUrl();
861+
const url = getAppUpdateUrl();
973862
await shell.openExternal(url);
974863
return { ok: true, url };
975864
});

src/preload.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,6 @@ if (isTrustedPreloadContext()) {
10001000
checkUpdate: () => ipcRenderer.invoke("elxmoj:check-update"),
10011001
runSelfCheck: () => ipcRenderer.invoke("elxmoj:run-self-check"),
10021002
getLastSelfCheck: () => ipcRenderer.invoke("elxmoj:get-last-self-check"),
1003-
getAppUpdateInfo: () => ipcRenderer.invoke("elxmoj:get-app-update-info"),
10041003
getAppUpdateUrl: () => ipcRenderer.invoke("elxmoj:get-app-update-url"),
10051004
openAppUpdatePage: () => ipcRenderer.invoke("elxmoj:open-app-update-page")
10061005
});

src/settings.html

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,7 @@ <h1>ELXMOJ 设置</h1>
190190
<div class="row">
191191
<button id="btnAppUpdate" class="btn-soft">下载最新 App</button>
192192
</div>
193-
<div id="appVersionInfo" class="hint">App 版本信息: 加载中...</div>
194193
<div id="appUpdateUrl" class="hint">下载地址: 加载中...</div>
195-
<div id="appUpdateAdvice" class="hint"></div>
196194
<div id="status" class="status">等待操作...</div>
197195
</div>
198196

src/settings.js

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,11 @@ async function openAppUpdatePage() {
5555

5656
async function showAppUpdateUrl() {
5757
try {
58-
const info = await window.ELXMOJ.getAppUpdateInfo();
59-
const url = info?.downloadUrl || (await window.ELXMOJ.getAppUpdateUrl());
60-
61-
const versionInfo = document.getElementById("appVersionInfo");
62-
if (versionInfo) {
63-
if (info?.ok) {
64-
versionInfo.textContent = `App 版本: 当前 ${info.currentVersion},最新 ${info.latestVersion}${info.hasUpdate ? "可更新" : "已是最新"}`;
65-
} else {
66-
versionInfo.textContent = `App 版本: 当前 ${info?.currentVersion || "未知"},最新版本获取失败`;
67-
}
68-
}
69-
58+
const url = await window.ELXMOJ.getAppUpdateUrl();
7059
const hint = document.getElementById("appUpdateUrl");
7160
if (hint) {
7261
hint.textContent = `下载地址: ${url}`;
7362
}
74-
75-
const advice = document.getElementById("appUpdateAdvice");
76-
if (advice) {
77-
advice.textContent = info?.ok
78-
? ""
79-
: `无法自动获取最新版本时,请提供 latest.json 或 latest.yml(含 version 字段)到更新源目录,或维护 GitHub Release 最新 tag。${info?.message ? ` 详情: ${info.message}` : ""}`;
80-
}
8163
} catch {
8264
// Ignore optional hint failures
8365
}

0 commit comments

Comments
 (0)