启动器:接入 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:
1 parent
d31a34e9aa
commit
f2debaf9bf
4 files changed
+181
-3
No files matched your search
+73
-2
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user