启动器:接入 Gitea Releases 自动检查更新 + 更新日志页

- server.py: 新增 /api/update/check、/api/update/changelog 接口,
  用标准库 urllib 请求自托管仓库 Lingbao/90CNServer 的 Releases,
  带 5 分钟缓存与 8 秒超时;本地版本号写死 v2026.11.04
- shell.js: 版本卡片改为动态读取远程最新 tag,发现新版本绿色可点击
  跳转;新增 loadChangelog 渲染发布记录
- index.html/shell.css: 更新日志页改为动态容器,补充卡片样式
This commit is contained in:
hanfuit committed 2026-11-04 19:16:05 +08:00
1 parent d31a34e9aa
commit f2debaf9bf
4 files changed
+181 -3

No files matched your search

+73 -2
View File
@@ -25,6 +25,7 @@
if (page === 'companion') loadCompanion();
if (page === 'servertool') loadServerTool();
if (page === 'settings') loadSettings();
if (page === 'changelog') loadChangelog();
if (page === 'gm') {
// 进入 GM:GM 覆盖整个窗口(隐藏顶部导航/面包屑/底部状态栏),并铺满工作区
document.body.classList.add('gm-fullscreen');
@@ -165,12 +166,11 @@
$('statApiPort').textContent = location.port || '19527';
$('shellBottomPort').textContent = running ? '18111 / 7001' : '—';
$('statState').textContent = s.control_exists && s.client_dir_exists ? '已就绪' : '不完整';
$('statVersion').textContent = 'v2026.11.03';
$('statRemote').textContent = '当前版本';
$('statClient').textContent = s.client_dir_exists ? (running ? '运行中' : '已就绪') : '缺失';
$('shellClock').textContent = '检查于 ' + new Date().toLocaleTimeString('zh-CN', {hour12:false});
$('statResource').textContent = s.admin ? '管理员' : '普通权限';
loadAccountLabel();
loadUpdateStatus();
} catch (e) { /* ignore */ }
}
@@ -189,6 +189,77 @@
} catch (e) { /* ignore */ }
}
// 检查更新:填充「版本」卡片里的「本地 / 远程最新 / 状态」
async function loadUpdateStatus() {
const remoteEl = $('statRemote');
const versionEl = $('statVersion');
try {
const r = await api('/api/update/check');
if (versionEl) versionEl.textContent = r.current || '—';
if (!remoteEl) return;
if (r.error) {
remoteEl.textContent = '检查失败';
remoteEl.className = '';
remoteEl.title = '无法连接更新服务器:' + r.error;
} else if (r.has_update && r.latest) {
remoteEl.textContent = r.latest;
remoteEl.className = 'green';
remoteEl.title = '发现新版本,点击查看';
remoteEl.style.cursor = 'pointer';
remoteEl.onclick = () => { window.open(r.latest_url, '_blank'); };
} else if (r.latest) {
remoteEl.textContent = r.latest;
remoteEl.className = 'green';
remoteEl.title = '已是最新版本';
} else {
remoteEl.textContent = '暂无发布';
remoteEl.className = '';
}
} catch (e) {
if (remoteEl) { remoteEl.textContent = '检查失败'; remoteEl.className = ''; }
}
}
// 更新日志页面:拉取所有 Release 的 changelog
async function loadChangelog() {
const box = $('changelogBody');
if (!box) return;
box.innerHTML = '<div class="empty-box">正在获取更新日志…</div>';
try {
const r = await api('/api/update/changelog');
if (!r.ok) {
box.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(r.error || '未知错误') + '</div>';
return;
}
const rels = r.releases || [];
if (!rels.length) {
box.innerHTML = '<div class="empty-box">暂无发布记录。<br>在 Gitea 仓库「发布 Release」并填写更新说明后,这里会自动显示。</div>';
return;
}
box.innerHTML = rels.map(rel => {
const badge = rel.prerelease ? '<span class="gm-tag gm-tag-gray">预发布</span>' : '';
const body = (rel.body || '').replace(/\r?\n/g, '<br>');
const assets = (rel.assets || []).map(a =>
`<a class="changelog-asset" href="${escapeHtml(a.download_url)}" target="_blank">⬇ ${escapeHtml(a.name)} (${fmt(a.size)} B)</a>`
).join('');
const when = rel.published_at ? rel.published_at.replace('T', ' ').slice(0, 16) : '';
return `<div class="changelog-item">
<div class="changelog-head">
<span class="changelog-tag">${escapeHtml(rel.tag || rel.name || '')}</span>
<span class="changelog-name">${escapeHtml(rel.name || '')}</span>
${badge}
<span class="changelog-time">${escapeHtml(when)}</span>
</div>
<div class="changelog-body">${body}</div>
${assets ? '<div class="changelog-assets">' + assets + '</div>' : ''}
<a class="changelog-link" href="${escapeHtml(rel.html_url)}" target="_blank">在浏览器中查看 ↗</a>
</div>`;
}).join('');
} catch (e) {
box.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(e && e.message ? e.message : e) + '</div>';
}
}
$('shellAccountSelect').addEventListener('change', async () => {
const username = $('shellAccountSelect').value;
try {