|
|
@@ -3,7 +3,6 @@ import { fileURLToPath } from 'url';
|
|
|
import path from 'path';
|
|
|
import { exec } from 'child_process';
|
|
|
import { promisify } from 'util';
|
|
|
-import os from 'os';
|
|
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
|
|
@@ -11,23 +10,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
const __dirname = path.dirname(__filename);
|
|
|
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
|
|
|
|
|
|
-/**
|
|
|
- * 设置内容安全策略 (Content Security Policy, CSP)
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 为 Electron 应用设置 CSP 规则,防止 XSS 攻击
|
|
|
- * - 开发环境:允许 localhost 连接,便于开发调试
|
|
|
- * - 生产环境:严格限制资源加载,提高安全性
|
|
|
- *
|
|
|
- * CSP 规则说明:
|
|
|
- * - default-src 'self': 默认只允许同源资源
|
|
|
- * - script-src: 控制脚本加载源
|
|
|
- * - style-src: 控制样式表加载源
|
|
|
- * - connect-src: 控制网络请求源(如 fetch, WebSocket)
|
|
|
- * - img-src: 控制图片加载源
|
|
|
- * - font-src: 控制字体加载源
|
|
|
- * - worker-src: 控制 Web Worker 加载源
|
|
|
- */
|
|
|
+// 设置内容安全策略(CSP),防止 XSS 攻击
|
|
|
function setContentSecurityPolicy() {
|
|
|
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
|
|
const csp = isDev
|
|
|
@@ -41,25 +24,7 @@ function setContentSecurityPolicy() {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * 创建主窗口
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 创建 Electron 应用的主窗口
|
|
|
- * - 配置窗口大小和 Web 安全设置
|
|
|
- * - 根据环境加载不同的内容源
|
|
|
- *
|
|
|
- * 窗口配置:
|
|
|
- * - 宽度: 1200px
|
|
|
- * - 高度: 800px
|
|
|
- * - preload: 预加载脚本路径(用于安全地暴露 Node.js API)
|
|
|
- * - nodeIntegration: false(禁用 Node.js 集成,提高安全性)
|
|
|
- * - contextIsolation: true(启用上下文隔离,防止 XSS 攻击)
|
|
|
- *
|
|
|
- * 加载逻辑:
|
|
|
- * - 开发环境: 加载 Vite 开发服务器 (http://localhost:5173) 并打开开发者工具
|
|
|
- * - 生产环境: 加载构建后的静态文件 (dist/index.html)
|
|
|
- */
|
|
|
+// 创建主窗口,根据环境加载不同内容源
|
|
|
function createWindow() {
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
width: 1200,
|
|
|
@@ -79,126 +44,7 @@ function createWindow() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * 获取本机局域网 IP 地址
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 遍历所有网络接口,查找第一个非内部的 IPv4 地址
|
|
|
- * - 用于确定局域网网段,以便扫描同网段的设备
|
|
|
- *
|
|
|
- * 查找逻辑:
|
|
|
- * - 遍历所有网络接口(如以太网、Wi-Fi 等)
|
|
|
- * - 过滤条件:IPv4 地址 && 非内部地址(非 127.0.0.1)
|
|
|
- * - 返回找到的第一个符合条件的 IP 地址
|
|
|
- *
|
|
|
- * @returns {string} 本机局域网 IP 地址,如果未找到则返回默认值 '192.168.1.1'
|
|
|
- *
|
|
|
- * @example
|
|
|
- * // 如果本机 IP 是 192.168.2.35,则返回 '192.168.2.35'
|
|
|
- * const localIP = getLocalIP(); // '192.168.2.35'
|
|
|
- */
|
|
|
-function getLocalIP() {
|
|
|
- const interfaces = os.networkInterfaces();
|
|
|
- for (const name of Object.keys(interfaces)) {
|
|
|
- for (const iface of interfaces[name]) {
|
|
|
- if (iface.family === 'IPv4' && !iface.internal) {
|
|
|
- return iface.address;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return '192.168.1.1';
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 扫描局域网内端口 5555 的 ADB 设备
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 获取本机 IP 地址,确定局域网网段(如 192.168.2.x)
|
|
|
- * - 扫描该网段内所有可能的 IP 地址(1-254)
|
|
|
- * - 对每个 IP 尝试连接 ADB 端口 5555
|
|
|
- * - 使用并行扫描提高效率,每批同时扫描 50 个 IP
|
|
|
- *
|
|
|
- * 扫描策略:
|
|
|
- * - 根据本机 IP 确定网段(如 192.168.2.35 → 网段 192.168.2.x)
|
|
|
- * - 扫描范围:192.168.2.1 到 192.168.2.254
|
|
|
- * - 每批处理 50 个 IP,避免同时发起过多连接
|
|
|
- * - 每个连接超时时间:300ms(快速失败,提高扫描速度)
|
|
|
- * - 忽略连接失败的错误(大部分 IP 不会有设备)
|
|
|
- *
|
|
|
- * 性能优化:
|
|
|
- * - 使用 Promise.all 并行执行,大幅提升扫描速度
|
|
|
- * - 分批处理避免系统资源耗尽
|
|
|
- * - 扫描完成后等待 500ms,确保所有连接已建立
|
|
|
- *
|
|
|
- * @returns {Promise<void>} 扫描完成(不返回结果,结果通过 getADBDevices 获取)
|
|
|
- *
|
|
|
- * @example
|
|
|
- * // 如果本机 IP 是 192.168.2.35
|
|
|
- * // 会扫描 192.168.2.1:5555 到 192.168.2.254:5555
|
|
|
- * await scanADBDevices();
|
|
|
- */
|
|
|
-async function scanADBDevices() {
|
|
|
- const localIP = getLocalIP();
|
|
|
- const ipParts = localIP.split('.');
|
|
|
- const baseIP = `${ipParts[0]}.${ipParts[1]}.${ipParts[2]}`;
|
|
|
-
|
|
|
- // 并行扫描,每批 50 个 IP
|
|
|
- const batchSize = 100;
|
|
|
- const promises = [];
|
|
|
-
|
|
|
- for (let i = 1; i <= 254; i += batchSize) {
|
|
|
- const batch = [];
|
|
|
- for (let j = i; j < i + batchSize && j <= 254; j++) {
|
|
|
- const ip = `${baseIP}.${j}`;
|
|
|
- batch.push(
|
|
|
- execAsync(`adb connect ${ip}:5555`, { timeout: 300 })
|
|
|
- .catch(() => {}) // 忽略连接失败
|
|
|
- );
|
|
|
- }
|
|
|
- promises.push(Promise.all(batch));
|
|
|
- }
|
|
|
-
|
|
|
- await Promise.all(promises);
|
|
|
- // 等待连接建立
|
|
|
- await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 获取已连接的 ADB 设备列表
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 执行 `adb devices` 命令获取当前所有已连接的设备
|
|
|
- * - 解析命令输出,提取设备 ID 和状态
|
|
|
- * - 只返回状态为 'device' 的设备(已授权且可用的设备)
|
|
|
- *
|
|
|
- * 命令输出格式:
|
|
|
- * ```
|
|
|
- * List of devices attached
|
|
|
- * 192.168.2.5:5555 device
|
|
|
- * 3764756281ZZZZZ device
|
|
|
- * ```
|
|
|
- *
|
|
|
- * 解析逻辑:
|
|
|
- * - 跳过第一行标题("List of devices attached")
|
|
|
- * - 每行按空白字符分割,提取设备 ID 和状态
|
|
|
- * - 只保留状态为 'device' 的设备(排除 'offline', 'unauthorized' 等)
|
|
|
- *
|
|
|
- * 返回数据格式:
|
|
|
- * ```javascript
|
|
|
- * [
|
|
|
- * { id: '192.168.2.5:5555', status: 'device' },
|
|
|
- * { id: '3764756281ZZZZZ', status: 'device' }
|
|
|
- * ]
|
|
|
- * ```
|
|
|
- *
|
|
|
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
|
|
|
- * - id: 设备标识符(IP:端口 或 USB 序列号)
|
|
|
- * - status: 设备状态(通常为 'device')
|
|
|
- *
|
|
|
- * @example
|
|
|
- * const devices = await getADBDevices();
|
|
|
- * // [{ id: '192.168.2.5:5555', status: 'device' }]
|
|
|
- */
|
|
|
+// 获取已连接的 ADB 设备列表
|
|
|
async function getADBDevices() {
|
|
|
try {
|
|
|
const { stdout } = await execAsync('adb devices');
|
|
|
@@ -221,72 +67,12 @@ async function getADBDevices() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * IPC 处理程序:扫描 ADB 设备
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 接收渲染进程的扫描请求
|
|
|
- * - 先执行扫描操作(scanADBDevices)
|
|
|
- * - 然后获取设备列表(getADBDevices)
|
|
|
- * - 返回扫描后的设备列表
|
|
|
- *
|
|
|
- * 调用方式(从渲染进程):
|
|
|
- * ```javascript
|
|
|
- * const devices = await window.electronAPI.scanADBDevices();
|
|
|
- * ```
|
|
|
- *
|
|
|
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
|
|
|
- */
|
|
|
-ipcMain.handle('scan-adb-devices', async () => {
|
|
|
- try {
|
|
|
- await scanADBDevices();
|
|
|
- return await getADBDevices();
|
|
|
- } catch (error) {
|
|
|
- console.error('扫描设备失败:', error);
|
|
|
- return [];
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-/**
|
|
|
- * IPC 处理程序:获取 ADB 设备列表
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 接收渲染进程的获取设备列表请求
|
|
|
- * - 直接返回当前已连接的设备列表(不执行扫描)
|
|
|
- * - 用于刷新设备列表,查看当前连接状态
|
|
|
- *
|
|
|
- * 调用方式(从渲染进程):
|
|
|
- * ```javascript
|
|
|
- * const devices = await window.electronAPI.getADBDevices();
|
|
|
- * ```
|
|
|
- *
|
|
|
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
|
|
|
- */
|
|
|
+// IPC 处理程序:获取 ADB 设备列表
|
|
|
ipcMain.handle('get-adb-devices', async () => {
|
|
|
return await getADBDevices();
|
|
|
});
|
|
|
|
|
|
-/**
|
|
|
- * IPC 处理程序:连接 ADB 设备
|
|
|
- *
|
|
|
- * 功能说明:
|
|
|
- * - 接收渲染进程的连接设备请求
|
|
|
- * - 执行 `adb connect` 命令连接指定设备
|
|
|
- * - 返回连接结果(成功或失败)
|
|
|
- *
|
|
|
- * 调用方式(从渲染进程):
|
|
|
- * ```javascript
|
|
|
- * const result = await window.electronAPI.connectADBDevice('192.168.2.5:5555');
|
|
|
- * // { success: true } 或 { success: false, error: '错误信息' }
|
|
|
- * ```
|
|
|
- *
|
|
|
- * @param {Electron.IpcMainInvokeEvent} event - IPC 事件对象
|
|
|
- * @param {string} ipPort - 设备 IP 地址和端口(格式:'192.168.2.5:5555')
|
|
|
- *
|
|
|
- * @returns {Promise<{success: boolean, error?: string}>} 连接结果
|
|
|
- * - success: true 表示连接成功,false 表示连接失败
|
|
|
- * - error: 连接失败时的错误信息(仅在失败时存在)
|
|
|
- */
|
|
|
+// IPC 处理程序:连接 ADB 设备
|
|
|
ipcMain.handle('connect-adb-device', async (event, ipPort) => {
|
|
|
try {
|
|
|
await execAsync(`adb connect ${ipPort}`);
|
|
|
@@ -297,15 +83,7 @@ ipcMain.handle('connect-adb-device', async (event, ipPort) => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/**
|
|
|
- * 应用启动逻辑
|
|
|
- *
|
|
|
- * 当 Electron 应用准备就绪时:
|
|
|
- * 1. 设置内容安全策略(CSP)
|
|
|
- * 2. 创建主窗口
|
|
|
- * 3. 监听 'activate' 事件(macOS 特有,当应用被激活时)
|
|
|
- * - 如果没有窗口,则创建新窗口
|
|
|
- */
|
|
|
+// 应用启动逻辑:设置 CSP、创建窗口、监听激活事件
|
|
|
app.whenReady().then(() => {
|
|
|
setContentSecurityPolicy();
|
|
|
createWindow();
|
|
|
@@ -317,15 +95,7 @@ app.whenReady().then(() => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-/**
|
|
|
- * 应用关闭逻辑
|
|
|
- *
|
|
|
- * 当所有窗口都关闭时:
|
|
|
- * - macOS: 不退出应用(保持应用在 Dock 中运行)
|
|
|
- * - Windows/Linux: 退出应用
|
|
|
- *
|
|
|
- * 这是 Electron 的标准行为,符合各平台的应用生命周期管理
|
|
|
- */
|
|
|
+// 应用关闭逻辑:macOS 保持运行,其他平台退出
|
|
|
app.on('window-all-closed', () => {
|
|
|
if (process.platform !== 'darwin') {
|
|
|
app.quit();
|