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:
2026-06-04 11:03:25 +08:00
parent 337234443a
commit ae733016bb
21 changed files with 1245 additions and 154 deletions

View File

@@ -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: AppImagechmod +x 后执行(通常需要手动替换)
console.log('[Updater] Linux 更新手动替换 AppImage 文件');
logger.warn('updater', 'Linux 更新手动替换 AppImage 文件');
}
// 退出应用,让安装器接管