|
@@ -1,12 +1,15 @@
|
|
// main.js (ESM 版本)
|
|
// main.js (ESM 版本)
|
|
-import { app, BrowserWindow, Menu, ipcMain } from 'electron';
|
|
|
|
|
|
+import { app, BrowserWindow, Menu, ipcMain, shell } from 'electron';
|
|
import { fileURLToPath } from 'url';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
import { dirname, join } from 'path';
|
|
|
|
+import { exec } from 'child_process';
|
|
|
|
+import { promisify } from 'util';
|
|
import { writeLog } from './src/log/log-writer.js';
|
|
import { writeLog } from './src/log/log-writer.js';
|
|
|
|
|
|
// -------------- 构造 ESM 版 __dirname --------------
|
|
// -------------- 构造 ESM 版 __dirname --------------
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const __dirname = dirname(__filename);
|
|
|
|
+const execAsync = promisify(exec);
|
|
|
|
|
|
// -------------- 创建窗口 --------------
|
|
// -------------- 创建窗口 --------------
|
|
function createWindow() {
|
|
function createWindow() {
|
|
@@ -17,8 +20,9 @@ function createWindow() {
|
|
frame: !isMac,
|
|
frame: !isMac,
|
|
titleBarStyle: isMac ? 'hiddenInset' : 'default',
|
|
titleBarStyle: isMac ? 'hiddenInset' : 'default',
|
|
webPreferences: {
|
|
webPreferences: {
|
|
- nodeIntegration: true,
|
|
|
|
- contextIsolation: false,
|
|
|
|
|
|
+ nodeIntegration: false,
|
|
|
|
+ contextIsolation: true,
|
|
|
|
+ preload: join(__dirname, 'preload.js'),
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
@@ -50,7 +54,106 @@ app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+// -------------- 系统操作函数 --------------
|
|
|
|
+const getSystemCommands = () => {
|
|
|
|
+ const platform = process.platform;
|
|
|
|
+
|
|
|
|
+ switch (platform) {
|
|
|
|
+ case 'win32': // Windows
|
|
|
|
+ return {
|
|
|
|
+ logout: 'shutdown /l',
|
|
|
|
+ shutdown: 'shutdown /s /t 0'
|
|
|
|
+ };
|
|
|
|
+ case 'darwin': // macOS
|
|
|
|
+ return {
|
|
|
|
+ logout: 'sudo launchctl bootout gui/$(id -u)',
|
|
|
|
+ shutdown: 'sudo shutdown -h now'
|
|
|
|
+ };
|
|
|
|
+ case 'linux': // Linux
|
|
|
|
+ return {
|
|
|
|
+ logout: 'loginctl terminate-user $USER',
|
|
|
|
+ shutdown: 'systemctl poweroff'
|
|
|
|
+ };
|
|
|
|
+ default:
|
|
|
|
+ return {
|
|
|
|
+ logout: null,
|
|
|
|
+ shutdown: null
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const executeSystemCommand = async (command, requiresAdmin = false) => {
|
|
|
|
+ try {
|
|
|
|
+ if (!command) {
|
|
|
|
+ throw new Error('当前平台不支持该操作');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Windows平台的权限检查
|
|
|
|
+ if (process.platform === 'win32' && requiresAdmin) {
|
|
|
|
+ // 检查是否以管理员身份运行
|
|
|
|
+ try {
|
|
|
|
+ await execAsync('net session', { timeout: 5000 });
|
|
|
|
+ } catch (adminCheckError) {
|
|
|
|
+ writeLog('warn', '操作可能需要管理员权限');
|
|
|
|
+ return {
|
|
|
|
+ success: false,
|
|
|
|
+ error: '该操作需要管理员权限,请以管理员身份运行程序后重试',
|
|
|
|
+ requiresAdmin: true
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const { stdout, stderr } = await execAsync(command, { timeout: 10000 });
|
|
|
|
+ writeLog('info', `系统命令执行成功: ${command}`);
|
|
|
|
+
|
|
|
|
+ if (stderr && stderr.trim()) {
|
|
|
|
+ writeLog('warn', `系统命令有警告信息: ${stderr}`);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return { success: true, stdout, stderr };
|
|
|
|
+ } catch (error) {
|
|
|
|
+ let errorMessage = error.message;
|
|
|
|
+
|
|
|
|
+ // 特定错误类型的处理
|
|
|
|
+ if (error.code === 'EACCES') {
|
|
|
|
+ errorMessage = '权限不足,请以管理员身份运行程序';
|
|
|
|
+ } else if (error.code === 'ENOENT') {
|
|
|
|
+ errorMessage = '系统命令不存在或路径错误';
|
|
|
|
+ } else if (error.code === 'ETIMEDOUT') {
|
|
|
|
+ errorMessage = '操作超时,请重试';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ writeLog('error', `系统命令执行失败: ${command}, 错误: ${errorMessage}`);
|
|
|
|
+ return { success: false, error: errorMessage };
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
// -------------- IPC --------------
|
|
// -------------- IPC --------------
|
|
ipcMain.handle('write-log', async (_, level, msg) => {
|
|
ipcMain.handle('write-log', async (_, level, msg) => {
|
|
writeLog(level, msg);
|
|
writeLog(level, msg);
|
|
-});
|
|
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// 退出应用
|
|
|
|
+ipcMain.handle('exit-close', async () => {
|
|
|
|
+ writeLog('info', '用户选择关闭应用程序');
|
|
|
|
+ app.quit();
|
|
|
|
+ return { success: true };
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// 注销用户
|
|
|
|
+ipcMain.handle('exit-logout', async () => {
|
|
|
|
+ writeLog('info', '用户选择注销系统');
|
|
|
|
+ const commands = getSystemCommands();
|
|
|
|
+ // 注销通常需要管理员权限(Windows平台)
|
|
|
|
+ const requiresAdmin = process.platform === 'win32';
|
|
|
|
+ return await executeSystemCommand(commands.logout, requiresAdmin);
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// 关机
|
|
|
|
+ipcMain.handle('exit-shutdown', async () => {
|
|
|
|
+ writeLog('info', '用户选择关机');
|
|
|
|
+ const commands = getSystemCommands();
|
|
|
|
+ // 关机通常需要管理员权限
|
|
|
|
+ const requiresAdmin = true;
|
|
|
|
+ return await executeSystemCommand(commands.shutdown, requiresAdmin);
|
|
|
|
+});
|