feat: 新增自动更新版本引导流程及相关配置调整

- 新增完整的upvp更新引导页面套件,涵盖版本展示、版本检查、测试协议确认与完成页流程
- 新增appVersion配置字段用于记录当前应用版本,调整配置文件存储路径与旧配置迁移逻辑
- 更新路由配置与全局状态管理以支持更新引导流程
- 更新自动更新计划文档与示例配置文件
This commit is contained in:
2026-08-31 03:04:23 +08:00
parent fa44d41d44
commit b3da357d57
16 changed files with 391 additions and 28 deletions
+18 -4
View File
@@ -9,12 +9,13 @@ const CURRENT_VERSION = 1;
/**
* 配置文件路径:
* - 打包后 → 系统用户数据目录(userData),避免安装到 Program Files 等只读目录时写入失败
* - 打包后 → 安装目录(可执行文件旁)。默认 per-user 安装(%LOCALAPPDATA%\Programs)可写;
* 若当初选择 per-machine 安装(Program Files)会无写权限,属已知限制
* - 开发模式 → 项目根目录(与旧行为一致,方便调试)
*/
export function configPath(): string {
if (app.isPackaged) {
return path.join(app.getPath('userData'), CONFIG_FILE);
return path.join(path.dirname(app.getPath('exe')), CONFIG_FILE);
}
return path.join(__dirname, '..', CONFIG_FILE);
}
@@ -130,7 +131,10 @@ export interface UpdateConfig {
}
export interface AppConfig {
/** 配置结构版本(迁移用) */
version: number;
/** 当前应用版本号(app.getVersion(),每次启动刷新;配置文件自述用) */
appVersion: string;
oobe: boolean;
app: AppInfoConfig;
theme: ThemeConfig;
@@ -148,6 +152,7 @@ export interface AppConfig {
const DEFAULTS: AppConfig = {
version: CURRENT_VERSION,
appVersion: '',
oobe: true,
app: { language: 'zh-CN' },
theme: { darkMode: 'auto', parallax: true },
@@ -220,12 +225,19 @@ export function loadConfig(): AppConfig {
}
}
export function saveConfig(config: AppConfig): void {
/**
* 保存配置(稀疏:仅写与默认值不同的部分)。
* - force=true:全量写入(首次启动使用,保证配置文件在安装目录可见)
* - 稀疏结果为空时:保留现有文件不删(重置由 config:reset / factoryReset 显式删除)
*/
export function saveConfig(config: AppConfig, force = false): void {
const filePath = configPath();
const sparse = diffValue(config, DEFAULTS) as Record<string, unknown> | undefined;
if (!sparse || Object.keys(sparse).length === 0) {
try { fs.unlinkSync(filePath); } catch {}
if (force) {
fs.writeFileSync(filePath, yaml.dump(config, { lineWidth: -1 }), 'utf-8');
}
return;
}
@@ -256,6 +268,8 @@ function mergeDeep<T>(base: T, patch: unknown): T {
export function getConfig(): AppConfig {
if (!current) {
current = loadConfig();
// 注意:不在此处刷新 appVersion —— 升级后保持旧版本号,
// 由渲染端 upvp(版本更新引导)流程完成后写入新版本号
}
return current;
}
+14 -19
View File
@@ -17,7 +17,6 @@ import { registerJavaHandlers } from './handlers/java';
import { registerUpdateHandlers } from './handlers/update';
import { updateService } from './updater';
import { saveConfig, configExists, getConfig, flushConfig, configPath } from './config';
import { authPath } from './auth';
const { app } = electron;
@@ -32,25 +31,20 @@ const win: { mainWindow: electron.BrowserWindow | null; splashWindow: electron.B
splashWindow: null,
};
// 迁移旧版「可执行文件旁」存储 → userData(仅打包模式)。
// 复制而非移动,避免破坏用户已有文件;userData 已有目标文件则跳过。
// 迁移旧版 userData 存储 → 安装目录(仅打包模式;配置已改回存安装目录)。
// 复制而非移动,避免破坏用户已有文件;安装目录已有目标文件则跳过。
function migrateLegacyFiles(): void {
if (!app.isPackaged) return;
const exeDir = path.dirname(app.getPath('exe'));
const pairs: { name: string; dest: string }[] = [
{ name: 'Koring.yml', dest: configPath() },
{ name: 'koring-auth.json', dest: authPath() },
];
for (const { name, dest } of pairs) {
if (fs.existsSync(dest)) continue;
const src = path.join(exeDir, name);
if (!fs.existsSync(src)) continue;
try {
fs.copyFileSync(src, dest);
console.log(`[migrate] copied ${name}${dest}`);
} catch (e) {
console.error(`[migrate] failed to copy ${name}:`, e);
}
const dest = path.join(exeDir, 'Koring.yml');
if (fs.existsSync(dest)) return;
const src = path.join(app.getPath('userData'), 'Koring.yml');
if (!fs.existsSync(src)) return;
try {
fs.copyFileSync(src, dest);
console.log(`[migrate] copied Koring.yml ${src}${dest}`);
} catch (e) {
console.error(`[migrate] failed to copy Koring.yml:`, e);
}
}
@@ -73,7 +67,8 @@ function runStartupChecks(): { isFirstLaunch: boolean; config: ReturnType<typeof
// 3. Load (or create) configgetConfig 会缓存到主进程内存,成为唯一权威)
const config = getConfig();
if (isFirstLaunch) {
saveConfig(config);
// 首次启动:全量写入,确保配置文件在安装目录可见(稀疏保存下全默认不落盘)
saveConfig(config, true);
}
return { isFirstLaunch, config };
@@ -175,7 +170,7 @@ function registerAllHandlers() {
app.whenReady().then(() => {
registerAllHandlers();
// Migrate legacy exe-dir config/auth to userData before anything reads them
// Migrate legacy userData config to install dir before anything reads them
migrateLegacyFiles();
// Run startup checks before creating windows