feat: 日志模块 + netRequest 封装 + PROJECT.md 更新
日志模块(文件持久化 + 事件总线联动 + 脱敏)
新增:
- shared/types/logging.ts — LogLevel / LogCategory / LogEntry 类型
- electron/main/logger.ts — 主进程日志核心(JSON Lines / 每日轮转 / 7天清理)
- electron/main/log-ipc.ts — 渲染进程日志 IPC 通道接收
- src/utils/logger.ts — 渲染进程日志器(IPC 转发 / DEV 控制台 / 事件总线钩子)
- shared/utils/sanitize.ts — 脱敏工具(ID哈希 / 邮箱 / 手机 / 名称 / Token)
修改:
- electron/main.ts — initLogger + flushLogger + uncaughtException
- src/main.tsx — renderer 全局错误捕获
- src/utils/event-bus.ts — setEventLogListener 钩子,emit 自动审计
- src/services/request.ts — 拦截器结构化错误分级记录
- electron/main/updater.ts — 14处 console → logger 迁移
- src/components/AppProvider.tsx — 登录/登出/自动登录 auth 事件日志
- shared/constants/ipc-channels.ts — 新增 LOG_MESSAGE 通道
netRequest 封装(Electron net.request 的 axios 风格 API)
新增:
- electron/main/net-request.ts — netRequest.get/post/put/del 命名空间
修改:
- electron/main/updater.ts — fetchUpdateInfo 改用 netRequest(22→12行)
文档更新
- PROJECT.md — 目录结构 / 状态管理 / 日志系统 / IPC 章节同步现状
- TODO.md — 增量更新改造 + 日志模块待办
- UpdateA.md — API blockmap 字段 / 数据库字段说明
This commit is contained in:
@@ -25,6 +25,8 @@ import { unlink } from 'node:fs/promises';
|
||||
|
||||
import type { UpdateCheckResult } from '../../shared/types';
|
||||
import { APP_VERSION } from '../../shared/constants/version';
|
||||
import { logger } from './logger';
|
||||
import { netRequest } from './net-request';
|
||||
|
||||
// ---------- IPC 通道(对渲染进程暴露,与旧版兼容)----------
|
||||
|
||||
@@ -59,7 +61,7 @@ export function initUpdater(mainWindow: BrowserWindow): void {
|
||||
updateWindow = mainWindow;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('[Updater] 开发模式,跳过自动检查更新。API:', getApiBaseUrl());
|
||||
logger.info('updater', '开发模式,跳过自动检查更新', { apiUrl: getApiBaseUrl() });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,43 +76,30 @@ export function initUpdater(mainWindow: BrowserWindow): void {
|
||||
// ============================================================
|
||||
|
||||
async function fetchUpdateInfo(): Promise<UpdateCheckResult | null> {
|
||||
const platform = process.platform; // 'win32' | 'darwin' | 'linux'
|
||||
const edition = process.env.EDITION || 'personal';
|
||||
const apiUrl = `${getApiBaseUrl()}/api/v1/update/check?platform=${platform}&version=${APP_VERSION}&edition=${edition}`;
|
||||
const { data } = await netRequest.get<{ code: number; data?: UpdateCheckResult } & UpdateCheckResult>(
|
||||
'/api/v1/update/check',
|
||||
{
|
||||
baseURL: getApiBaseUrl(),
|
||||
params: {
|
||||
platform: process.platform,
|
||||
version: APP_VERSION,
|
||||
edition: process.env.EDITION || 'personal',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
console.log('[Updater] 请求版本检查 API:', apiUrl);
|
||||
// 兼容两种 API 响应格式:
|
||||
// 格式 A: { code: 0, data: UpdateCheckResult }
|
||||
// 格式 B: UpdateCheckResult 直接返回
|
||||
const result: UpdateCheckResult = (data as { data?: UpdateCheckResult }).data ?? (data as UpdateCheckResult);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = net.request({ method: 'GET', url: apiUrl });
|
||||
if (!result.hasUpdate) {
|
||||
logger.info('updater', '当前已是最新版本');
|
||||
return null;
|
||||
}
|
||||
|
||||
req.on('response', (response) => {
|
||||
let body = '';
|
||||
response.on('data', (chunk) => (body += chunk.toString()));
|
||||
response.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(body);
|
||||
|
||||
// 兼容两种 API 响应格式:
|
||||
// 格式 A: { code: 0, data: UpdateCheckResult }
|
||||
// 格式 B: UpdateCheckResult 直接返回
|
||||
const data: UpdateCheckResult = json.data ?? json;
|
||||
|
||||
if (!data.hasUpdate) {
|
||||
console.log('[Updater] 当前已是最新版本');
|
||||
return resolve(null);
|
||||
}
|
||||
|
||||
console.log('[Updater] 发现新版本:', data.latestVersion);
|
||||
resolve(data);
|
||||
} catch (err) {
|
||||
reject(new Error(`API 响应解析失败: ${(err as Error).message}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (err) => reject(new Error(`API 请求失败: ${err.message}`)));
|
||||
req.end();
|
||||
});
|
||||
logger.info('updater', '发现新版本', { latestVersion: result.latestVersion });
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -122,8 +111,7 @@ async function downloadInstaller(info: UpdateCheckResult): Promise<string> {
|
||||
process.platform === 'win32' ? '.exe' : process.platform === 'darwin' ? '.dmg' : '.AppImage';
|
||||
const tempPath = join(tmpdir(), `HeiXiu-${info.latestVersion}-update${ext}`);
|
||||
|
||||
console.log('[Updater] 开始下载:', info.downloadUrl);
|
||||
console.log('[Updater] 临时文件:', tempPath);
|
||||
logger.info('updater', '开始下载', { downloadUrl: info.downloadUrl, tempPath });
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = net.request({ method: 'GET', url: info.downloadUrl });
|
||||
@@ -174,7 +162,7 @@ async function downloadInstaller(info: UpdateCheckResult): Promise<string> {
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[Updater] 下载完成,SHA512 校验通过');
|
||||
logger.info('updater', '下载完成,SHA512 校验通过');
|
||||
pendingInstallerPath = tempPath;
|
||||
resolve(tempPath);
|
||||
});
|
||||
@@ -203,12 +191,12 @@ async function downloadInstaller(info: UpdateCheckResult): Promise<string> {
|
||||
|
||||
export async function checkForUpdates(): Promise<void> {
|
||||
if (updateChecked) {
|
||||
console.log('[Updater] 已检查过更新,跳过');
|
||||
logger.debug('updater', '已检查过更新,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('[Updater] 开发模式,跳过更新检查');
|
||||
logger.debug('updater', '开发模式,跳过更新检查');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,7 +223,7 @@ export async function checkForUpdates(): Promise<void> {
|
||||
// 下载完成 → 通知渲染进程
|
||||
updateWindow?.webContents.send(UPDATE_CHANNELS.UPDATE_DOWNLOADED);
|
||||
} catch (error) {
|
||||
console.error('[Updater] 更新流程失败:', (error as Error).message);
|
||||
logger.error('updater', '更新流程失败', error as Error, { apiUrl: getApiBaseUrl() });
|
||||
updateWindow?.webContents.send(UPDATE_CHANNELS.UPDATE_ERROR, {
|
||||
message: (error as Error).message || '更新检查失败',
|
||||
});
|
||||
@@ -263,7 +251,7 @@ export async function checkForUpdatesManual(): Promise<void> {
|
||||
await downloadInstaller(info);
|
||||
updateWindow?.webContents.send(UPDATE_CHANNELS.UPDATE_DOWNLOADED);
|
||||
} catch (error) {
|
||||
console.error('[Updater] 手动更新失败:', (error as Error).message);
|
||||
logger.error('updater', '手动更新失败', error as Error);
|
||||
updateWindow?.webContents.send(UPDATE_CHANNELS.UPDATE_ERROR, {
|
||||
message: (error as Error).message || '检查更新失败',
|
||||
});
|
||||
@@ -272,11 +260,11 @@ export async function checkForUpdatesManual(): Promise<void> {
|
||||
|
||||
export function installUpdateNow(): void {
|
||||
if (!pendingInstallerPath) {
|
||||
console.error('[Updater] 没有待安装的更新文件');
|
||||
logger.warn('updater', '没有待安装的更新文件');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Updater] 开始安装更新:', pendingInstallerPath);
|
||||
logger.info('updater', '开始安装更新', { installerPath: pendingInstallerPath });
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// Windows: NSIS 安装器,/S 静默安装
|
||||
@@ -292,7 +280,7 @@ export function installUpdateNow(): void {
|
||||
});
|
||||
} else {
|
||||
// Linux: AppImage,chmod +x 后执行(通常需要手动替换)
|
||||
console.log('[Updater] Linux 更新请手动替换 AppImage 文件');
|
||||
logger.warn('updater', 'Linux 更新需手动替换 AppImage 文件');
|
||||
}
|
||||
|
||||
// 退出应用,让安装器接管
|
||||
|
||||
Reference in New Issue
Block a user