日志模块(文件持久化 + 事件总线联动 + 脱敏)
新增:
- 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 字段 / 数据库字段说明
163 lines
5.0 KiB
TypeScript
163 lines
5.0 KiB
TypeScript
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
|
|
import { createRequire } from 'node:module';
|
|
import { fileURLToPath } from 'node:url';
|
|
import path from 'node:path';
|
|
|
|
import { getPlatform, isMacOS } from './main/utils/platform';
|
|
import { getWindowIconPath } from './main/utils/logo';
|
|
import { buildWindowTitle, parseEdition } from '../shared/constants/app';
|
|
import { initUpdater, registerUpdateIpcHandlers } from './main/updater';
|
|
import { setupAppMenu } from './main/menu';
|
|
import { initLogger, flushLogger, logger } from './main/logger';
|
|
import { registerLogIpcHandlers } from './main/log-ipc';
|
|
import { BIDIRECTIONAL } from '../shared/constants/ipc-channels';
|
|
|
|
createRequire(import.meta.url);
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
process.env.APP_ROOT = path.join(__dirname, '..');
|
|
|
|
export const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL'];
|
|
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron');
|
|
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist');
|
|
|
|
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
|
|
? path.join(process.env.APP_ROOT, 'public')
|
|
: RENDERER_DIST;
|
|
|
|
// ---------- 平台 & 版本信息 ----------
|
|
const currentPlatform = getPlatform();
|
|
const currentEdition = parseEdition(process.env.EDITION);
|
|
const WINDOW_TITLE = buildWindowTitle(currentPlatform, currentEdition);
|
|
|
|
// ============================================================
|
|
// 全局未捕获异常(主进程兜底记录)
|
|
// ============================================================
|
|
|
|
process.on('uncaughtException', (error) => {
|
|
try {
|
|
logger.error('app', 'Uncaught exception (main process)', error);
|
|
} catch {
|
|
/* logger 自身异常 — 最后防线 */
|
|
}
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason) => {
|
|
try {
|
|
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
logger.error('app', 'Unhandled rejection (main process)', error);
|
|
} catch {
|
|
/* logger 自身异常 — 最后防线 */
|
|
}
|
|
});
|
|
|
|
// ============================================================
|
|
// 窗口引用
|
|
// ============================================================
|
|
|
|
let mainWindow: BrowserWindow | null = null;
|
|
|
|
// ============================================================
|
|
// 主窗口
|
|
// ============================================================
|
|
|
|
function createMainWindow(): BrowserWindow {
|
|
const iconPath = getWindowIconPath(app.getAppPath());
|
|
|
|
mainWindow = new BrowserWindow({
|
|
title: WINDOW_TITLE,
|
|
icon: iconPath,
|
|
width: 1280,
|
|
height: 800,
|
|
minWidth: 960,
|
|
minHeight: 600,
|
|
show: false,
|
|
...(isMacOS() ? { titleBarStyle: 'hiddenInset' as const } : {}),
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.mjs'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
},
|
|
});
|
|
|
|
mainWindow.setTitle(WINDOW_TITLE);
|
|
|
|
if (VITE_DEV_SERVER_URL) {
|
|
mainWindow.webContents.openDevTools();
|
|
}
|
|
|
|
// macOS 全屏事件
|
|
if (isMacOS()) {
|
|
mainWindow.on('enter-full-screen', () =>
|
|
mainWindow?.webContents.send('window-fullscreen-changed', true),
|
|
);
|
|
mainWindow.on('leave-full-screen', () =>
|
|
mainWindow?.webContents.send('window-fullscreen-changed', false),
|
|
);
|
|
}
|
|
|
|
mainWindow.webContents.on('did-finish-load', () => {
|
|
mainWindow?.webContents.send('main-process-message', new Date().toLocaleString());
|
|
mainWindow?.webContents.send('platform-info', {
|
|
platform: currentPlatform,
|
|
edition: currentEdition,
|
|
});
|
|
});
|
|
|
|
// ready-to-show 后显示,避免白屏闪烁
|
|
mainWindow.once('ready-to-show', () => {
|
|
mainWindow?.show();
|
|
});
|
|
|
|
if (VITE_DEV_SERVER_URL) {
|
|
mainWindow.loadURL(VITE_DEV_SERVER_URL);
|
|
} else {
|
|
mainWindow.loadFile(path.join(RENDERER_DIST, 'index.html'));
|
|
}
|
|
|
|
return mainWindow;
|
|
}
|
|
|
|
// ============================================================
|
|
// 应用生命周期
|
|
// ============================================================
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (!isMacOS()) {
|
|
app.quit();
|
|
}
|
|
});
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createMainWindow();
|
|
}
|
|
});
|
|
|
|
app.on('before-quit', () => {
|
|
flushLogger();
|
|
});
|
|
|
|
app.whenReady().then(() => {
|
|
initLogger();
|
|
registerLogIpcHandlers();
|
|
setupAppMenu();
|
|
app.setName(WINDOW_TITLE);
|
|
registerUpdateIpcHandlers();
|
|
|
|
// 文件对话框 IPC 处理(供渲染进程选择文件夹)
|
|
ipcMain.handle(BIDIRECTIONAL.FILE_DIALOG, async (_event, options: Electron.OpenDialogOptions) => {
|
|
if (!mainWindow) return { canceled: true, filePaths: [] };
|
|
return dialog.showOpenDialog(mainWindow, {
|
|
title: options?.title || '选择文件夹',
|
|
defaultPath: options?.defaultPath || app.getPath('home'),
|
|
properties: options?.properties || ['openDirectory'],
|
|
});
|
|
});
|
|
|
|
// 始终打开主窗口
|
|
// 登录/注册由渲染进程内的 Modal 弹层处理,不再新开窗口
|
|
const win = createMainWindow();
|
|
initUpdater(win);
|
|
});
|