新增: 应用内版本更新与自动升级全链路
后端 update.rs: app_check_update(拉 latest.json+ed25519 签名校验) / app_download_install_update / prefs 读写 / mark_checked / mark_notified 五命令, lib.rs 注册;tauri.conf updater 段(endpoints=app.1216.top/download/devflow/latest.json + pubkey + createUpdaterArtifacts) + tauri-plugin-updater 依赖 + capabilities 权限。 前端 UpdateBadge.vue(顶栏徽章+下拉) / useUpdateCheck.ts(检查/下载/安装状态机) / AboutPanel.vue(设置页关于面板) + AppLayout 接线 + SettingsNav/searchIndex 索引 + i18n zh/en settings 键。 运维 scripts/publish-release.ps1(发版) + swap-install.ps1(换装校验) + 设计文档 版本更新与自动升级-设计-2026-10-09.md。 验收: cargo check -p devflow EXIT 0 + vue-tsc EXIT 0 + build EXIT 0。
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
// 应用内升级 — 自动检查调度(B2/L1)
|
||||
//
|
||||
// 设计:docs/02-架构设计/专项设计/版本更新与自动升级-设计-2026-10-09.md §四 L1
|
||||
//
|
||||
// 策略(C4 定稿):启动后延迟 3s 首查 → 此后按 interval_minutes(默认 15)周期查。
|
||||
// 去重:同一版本号只提示一次(KV df-update-notified-version),「稍后」后不再重复弹。
|
||||
// 只提示不自动装(安装是高风险动作,必须人点头)。
|
||||
//
|
||||
// 实现要点:
|
||||
// - 单例调度:模块级 timer,防多组件重复启动。
|
||||
// - 节流判定:读 KV last_check + interval,未到间隔跳过(防每次启动都拉)。
|
||||
// - 更新可用时广播 window 事件 'app-update-available',TopBar 徽章/AboutPanel 消费。
|
||||
// - 停用:auto_check=false 时不启动。
|
||||
|
||||
import { settingsApi } from '@/api'
|
||||
|
||||
/** 更新可用事件载荷 */
|
||||
export interface UpdateAvailablePayload {
|
||||
version: string
|
||||
notes: string | null
|
||||
date: string | null
|
||||
}
|
||||
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
let started = false
|
||||
|
||||
/** 首查延迟(ms)—— 设计 C4:启动后 3 秒 */
|
||||
const FIRST_CHECK_DELAY_MS = 3000
|
||||
|
||||
/**
|
||||
* 执行一次自动检查(含节流判定)。
|
||||
* - auto_check=false → 跳过
|
||||
* - 距上次检查不足 interval → 跳过
|
||||
* - 有新版且未提示过该版本 → 广播 app-update-available
|
||||
*/
|
||||
async function runAutoCheck(): Promise<void> {
|
||||
try {
|
||||
const prefs = await settingsApi.getUpdatePrefs()
|
||||
if (!prefs.auto_check) return
|
||||
|
||||
// 节流:距上次检查不足 interval 则跳过
|
||||
if (prefs.last_check) {
|
||||
const last = Date.parse(prefs.last_check)
|
||||
if (!Number.isNaN(last)) {
|
||||
const elapsedMin = (Date.now() - last) / 60000
|
||||
if (elapsedMin < prefs.interval_minutes) return
|
||||
}
|
||||
}
|
||||
|
||||
const r = await settingsApi.checkUpdate()
|
||||
const now = new Date().toISOString()
|
||||
void settingsApi.markUpdateChecked(now)
|
||||
|
||||
if (r.has_update && r.version) {
|
||||
// 同版本只提示一次
|
||||
if (prefs.notified_version === r.version) return
|
||||
void settingsApi.markUpdateNotified(r.version)
|
||||
const payload: UpdateAvailablePayload = {
|
||||
version: r.version,
|
||||
notes: r.notes,
|
||||
date: r.date,
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('app-update-available', { detail: payload }))
|
||||
}
|
||||
} catch {
|
||||
// 网络失败静默:自动检查不打扰用户(手动检查才报错)
|
||||
}
|
||||
}
|
||||
|
||||
/** 调度下一次检查(按 interval_minutes) */
|
||||
async function scheduleNext(): Promise<void> {
|
||||
let intervalMin = 15
|
||||
try {
|
||||
const prefs = await settingsApi.getUpdatePrefs()
|
||||
intervalMin = prefs.interval_minutes
|
||||
} catch {
|
||||
/* 用默认 15 */
|
||||
}
|
||||
timer = setTimeout(async () => {
|
||||
await runAutoCheck()
|
||||
await scheduleNext()
|
||||
}, Math.max(1, intervalMin) * 60_000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动自动检查调度(幂等:重复调用只启动一次)。
|
||||
* 在 App.vue onMounted 调用。
|
||||
*/
|
||||
export function startUpdateAutoCheck(): void {
|
||||
if (started) return
|
||||
started = true
|
||||
timer = setTimeout(async () => {
|
||||
await runAutoCheck()
|
||||
await scheduleNext()
|
||||
}, FIRST_CHECK_DELAY_MS)
|
||||
}
|
||||
|
||||
/** 停止调度(卸载/测试用) */
|
||||
export function stopUpdateAutoCheck(): void {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
timer = null
|
||||
}
|
||||
started = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 偏好变更后重置调度(用户在关于页开关自动检查/改间隔后调用)。
|
||||
* 关 → 停;开 → 立即重排。
|
||||
*/
|
||||
export async function resetUpdateAutoCheck(): Promise<void> {
|
||||
stopUpdateAutoCheck()
|
||||
try {
|
||||
const prefs = await settingsApi.getUpdatePrefs()
|
||||
if (prefs.auto_check) startUpdateAutoCheck()
|
||||
} catch {
|
||||
/* 静默 */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user