portal 設定頁加版本卡:顯示目前版本+落後紅點+一鍵更新(帶 email,t154 免辨識碼)

leo 08-02:「不顯示的話用戶不知道要不要更新」「發現落後就按一下開啟 install
直接帶它的 email 和辨識碼」。
比法=自己的 bundle_version(cypher /health)vs 最新版(安裝器 /api/latest),
semver 逐段數字比(避免 1.4.10 < 1.4.9 的字串比錯誤);
舊格式版本(日期+sha)一律判為落後,舊實例才會被正確提示更新。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-08-02 19:23:12 +08:00
parent 1e89be1ea0
commit e570714472
+85
View File
@@ -329,6 +329,22 @@
<div class="view page narrow" id="v-settings">
<div class="pagehead"><span class="t">設定</span></div>
<div class="setcol">
<!-- 🆕 2026-08-02(leo:「不顯示的話用戶不知道要不要更新」+「發現落後就按一下開啟 install」):
版本卡放設定頁第一張——用戶想「我是不是舊的」時第一眼就看到。
落後時直接給一顆帶 email 的更新按鈕(安裝器 t154 對既有實例免辨識碼),
不要求用戶理解版本號、找網址、翻辨識碼。 -->
<div class="panel" id="st-ver-panel">
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
<div style="min-width:0;flex:1">
<div style="font-size:17px;font-weight:600;display:flex;align-items:center;gap:8px">
版本
<span id="st-ver-dot" style="display:none;width:9px;height:9px;border-radius:50%;background:#C2492E" title="有新版本"></span>
</div>
<div id="st-ver-line" style="margin-top:4px;font-size:14px;line-height:1.65;color:rgba(var(--ink-rgb),.55)">查詢中…</div>
</div>
<button class="btn" id="st-ver-update" style="display:none">立即更新</button>
</div>
</div>
<div class="panel">
<div style="font-size:17px;font-weight:600;margin-bottom:10px">我的帳號</div>
<div id="st-me"><div class="muted">載入中…</div></div>
@@ -1736,11 +1752,80 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
if (ev.target && ev.target.id === 'ad-otp-close') $('ad-otp').innerHTML = '';
});
// ── 版本檢查(2026-08-02)──────────────────────────────────────────────
// leo 的兩個要求:
// ②「不顯示的話用戶不知道要不要更新」→ 一定要有目前版本
// ③「白癡化,發現落後就按一下開啟 install 直接帶它的 email 和辨識碼」
// → 落後才出現按鈕,且自動帶 email;辨識碼不必帶(安裝器 t154 對既有實例免碼)
//
// 比法:自己的版本(cypher /health 的 bundle_versionvs 最新版(安裝器 /api/latest)。
// 兩邊都是 semver(例 1.4.2),用數字逐段比,不用字串比('1.4.10' < '1.4.9' 會出錯)。
var INSTALLER_ORIGIN = 'https://install.arcrun.dev';
function cmpSemver(a, b) {
var x = String(a || '').split('.').map(Number);
var y = String(b || '').split('.').map(Number);
for (var i = 0; i < 3; i++) {
var d = (x[i] || 0) - (y[i] || 0);
if (d) return d < 0 ? -1 : 1;
}
return 0;
}
function loadVersion() {
var line = $('st-ver-line');
var dot = $('st-ver-dot');
var btn = $('st-ver-update');
if (!line) return;
var mineP = fetch(window.ARCRUN_API_BASE + '/health', { cache: 'no-store' })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (j) { return (j && j.bundle_version) || ''; })
.catch(function () { return ''; });
var latestP = fetch(INSTALLER_ORIGIN + '/api/latest')
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (j) { return (j && j.release) || ''; })
.catch(function () { return ''; });
Promise.all([mineP, latestP]).then(function (res) {
var mine = res[0], latest = res[1];
if (!mine) { line.textContent = '無法讀取目前版本(知識庫服務可能正在啟動)'; return; }
// 舊實例的 bundle_version 是舊格式(2026-07-31+8e83589),比不了 semver。
// 這種情況一律當成「落後」——因為新版才會寫 semver 進來。
var mineIsSemver = /^\d+\.\d+\.\d+$/.test(mine);
if (!latest) {
line.textContent = '目前版本 ' + mine + '(暫時查不到最新版,稍後再試)';
return;
}
var behind = !mineIsSemver || cmpSemver(mine, latest) < 0;
if (!behind) {
line.innerHTML = '目前版本 <strong>' + esc(mine) + '</strong> 已是最新版';
dot.style.display = 'none';
btn.style.display = 'none';
return;
}
line.innerHTML = '目前版本 <strong>' + esc(mineIsSemver ? mine : '較舊版本') + '</strong>'
+ ' → 最新版 <strong>' + esc(latest) + '</strong><br>'
+ '<span style="color:#C2492E">有新版可以更新。按「立即更新」會開啟安裝頁,用你原本的 Cloudflare 帳號重新安裝一次即可,知識庫內容不會動到。</span>';
dot.style.display = 'inline-block';
btn.style.display = '';
btn.onclick = function () {
// 帶 email 讓安裝頁預填;既有實例更新免辨識碼(安裝器 t154)
var email = (S.profile && S.profile.email) || '';
var url = INSTALLER_ORIGIN + '/' + (email ? '?email=' + encodeURIComponent(email) : '');
window.open(url, '_blank', 'noopener');
};
});
}
// ── 設定 ──
function loadSettings() {
var p = S.profile;
if (!p) { $('st-me').innerHTML = '<div class="muted">載入中…</div>'; return; }
if (window._loadAiConfig) window._loadAiConfig();
loadVersion();
var libs = p.libraries || [];
var libHtml = libs.indexOf('*') >= 0
? '<span class="tag green">全部知識庫</span>'