diff --git a/frontend/.gitignore b/frontend/.gitignore index 774344d..bc2a630 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +dist-electron diff --git a/frontend/dist-electron/main.js b/frontend/dist-electron/main.js deleted file mode 100644 index 64ce4ae..0000000 --- a/frontend/dist-electron/main.js +++ /dev/null @@ -1,294 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -const electron_1 = require("electron"); -const path = __importStar(require("path")); -const fs = __importStar(require("fs")); -const child_process_1 = require("child_process"); -const environmentService_1 = require("./services/environmentService"); -const downloadService_1 = require("./services/downloadService"); -const pythonService_1 = require("./services/pythonService"); -const gitService_1 = require("./services/gitService"); -// 检查是否以管理员权限运行 -function isRunningAsAdmin() { - try { - // 在Windows上,尝试写入系统目录来检查管理员权限 - if (process.platform === 'win32') { - const testPath = path.join(process.env.WINDIR || 'C:\\Windows', 'temp', 'admin-test.tmp'); - try { - fs.writeFileSync(testPath, 'test'); - fs.unlinkSync(testPath); - return true; - } - catch { - return false; - } - } - return true; // 非Windows系统暂时返回true - } - catch { - return false; - } -} -// 重新以管理员权限启动应用 -function restartAsAdmin() { - if (process.platform === 'win32') { - const exePath = process.execPath; - const args = process.argv.slice(1); - // 使用PowerShell以管理员权限启动 - (0, child_process_1.spawn)('powershell', [ - '-Command', - `Start-Process -FilePath "${exePath}" -ArgumentList "${args.join(' ')}" -Verb RunAs`, - ], { - detached: true, - stdio: 'ignore', - }); - electron_1.app.quit(); - } -} -let mainWindow = null; -function createWindow() { - mainWindow = new electron_1.BrowserWindow({ - width: 1600, - height: 900, - minWidth: 800, - minHeight: 600, - icon: path.join(__dirname, '../src/assets/AUTO_MAA.ico'), - webPreferences: { - preload: path.join(__dirname, 'preload.js'), - nodeIntegration: false, - contextIsolation: true, - }, - autoHideMenuBar: true, - }); - mainWindow.setMenuBarVisibility(false); - const devServer = process.env.VITE_DEV_SERVER_URL; - if (devServer) { - mainWindow.loadURL(devServer); - } - else { - const indexHtmlPath = path.join(electron_1.app.getAppPath(), 'dist', 'index.html'); - mainWindow.loadFile(indexHtmlPath); - } - mainWindow.on('closed', () => { - mainWindow = null; - }); - // 设置各个服务的主窗口引用 - if (mainWindow) { - (0, downloadService_1.setMainWindow)(mainWindow); - (0, pythonService_1.setMainWindow)(mainWindow); - (0, gitService_1.setMainWindow)(mainWindow); - } -} -// IPC处理函数 -electron_1.ipcMain.handle('open-dev-tools', () => { - if (mainWindow) { - mainWindow.webContents.openDevTools({ mode: 'undocked' }); - } -}); -electron_1.ipcMain.handle('select-folder', async () => { - if (!mainWindow) - return null; - const result = await electron_1.dialog.showOpenDialog(mainWindow, { - properties: ['openDirectory'], - title: '选择文件夹', - }); - return result.canceled ? null : result.filePaths[0]; -}); -electron_1.ipcMain.handle('select-file', async (event, filters = []) => { - if (!mainWindow) - return null; - const result = await electron_1.dialog.showOpenDialog(mainWindow, { - properties: ['openFile'], - title: '选择文件', - filters: filters.length > 0 ? filters : [{ name: '所有文件', extensions: ['*'] }], - }); - return result.canceled ? null : result.filePaths[0]; -}); -// 环境检查 -electron_1.ipcMain.handle('check-environment', async () => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, environmentService_1.checkEnvironment)(appRoot); -}); -// Python相关 -electron_1.ipcMain.handle('download-python', async (event, mirror = 'tsinghua') => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, pythonService_1.downloadPython)(appRoot, mirror); -}); -electron_1.ipcMain.handle('install-pip', async () => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, pythonService_1.installPipPackage)(appRoot); -}); -electron_1.ipcMain.handle('install-dependencies', async (event, mirror = 'tsinghua') => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, pythonService_1.installDependencies)(appRoot, mirror); -}); -electron_1.ipcMain.handle('start-backend', async () => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, pythonService_1.startBackend)(appRoot); -}); -// Git相关 -electron_1.ipcMain.handle('download-git', async () => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, gitService_1.downloadGit)(appRoot); -}); -electron_1.ipcMain.handle('clone-backend', async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, gitService_1.cloneBackend)(appRoot, repoUrl); -}); -electron_1.ipcMain.handle('update-backend', async (event, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') => { - const appRoot = (0, environmentService_1.getAppRoot)(); - return (0, gitService_1.cloneBackend)(appRoot, repoUrl); // 使用相同的逻辑,会自动判断是pull还是clone -}); -// 配置文件操作 -electron_1.ipcMain.handle('save-config', async (event, config) => { - try { - const appRoot = (0, environmentService_1.getAppRoot)(); - const configDir = path.join(appRoot, 'config'); - const configPath = path.join(configDir, 'frontend_config.json'); - // 确保config目录存在 - if (!fs.existsSync(configDir)) { - fs.mkdirSync(configDir, { recursive: true }); - } - fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8'); - console.log(`配置已保存到: ${configPath}`); - } - catch (error) { - console.error('保存配置文件失败:', error); - throw error; - } -}); -electron_1.ipcMain.handle('load-config', async () => { - try { - const appRoot = (0, environmentService_1.getAppRoot)(); - const configPath = path.join(appRoot, 'config', 'frontend_config.json'); - if (fs.existsSync(configPath)) { - const config = fs.readFileSync(configPath, 'utf8'); - console.log(`从文件加载配置: ${configPath}`); - return JSON.parse(config); - } - return null; - } - catch (error) { - console.error('加载配置文件失败:', error); - return null; - } -}); -electron_1.ipcMain.handle('reset-config', async () => { - try { - const appRoot = (0, environmentService_1.getAppRoot)(); - const configPath = path.join(appRoot, 'config', 'frontend_config.json'); - if (fs.existsSync(configPath)) { - fs.unlinkSync(configPath); - console.log(`配置文件已删除: ${configPath}`); - } - } - catch (error) { - console.error('重置配置文件失败:', error); - throw error; - } -}); -// 日志文件操作 -electron_1.ipcMain.handle('save-logs-to-file', async (event, logs) => { - try { - const appRoot = (0, environmentService_1.getAppRoot)(); - const logsDir = path.join(appRoot, 'logs'); - // 确保logs目录存在 - if (!fs.existsSync(logsDir)) { - fs.mkdirSync(logsDir, { recursive: true }); - } - const logFilePath = path.join(logsDir, 'app.log'); - fs.writeFileSync(logFilePath, logs, 'utf8'); - console.log(`日志已保存到: ${logFilePath}`); - } - catch (error) { - console.error('保存日志文件失败:', error); - throw error; - } -}); -electron_1.ipcMain.handle('load-logs-from-file', async () => { - try { - const appRoot = (0, environmentService_1.getAppRoot)(); - const logFilePath = path.join(appRoot, 'logs', 'app.log'); - if (fs.existsSync(logFilePath)) { - const logs = fs.readFileSync(logFilePath, 'utf8'); - console.log(`从文件加载日志: ${logFilePath}`); - return logs; - } - return null; - } - catch (error) { - console.error('加载日志文件失败:', error); - return null; - } -}); -// 管理员权限相关 -electron_1.ipcMain.handle('check-admin', () => { - return isRunningAsAdmin(); -}); -electron_1.ipcMain.handle('restart-as-admin', () => { - restartAsAdmin(); -}); -// 应用生命周期 -// 保证应用单例运行 -const gotTheLock = electron_1.app.requestSingleInstanceLock(); -if (!gotTheLock) { - electron_1.app.quit(); - process.exit(0); -} -electron_1.app.on('second-instance', () => { - if (mainWindow) { - if (mainWindow.isMinimized()) - mainWindow.restore(); - mainWindow.focus(); - } -}); -electron_1.app.whenReady().then(() => { - // 检查管理员权限 - if (!isRunningAsAdmin()) { - console.log('应用未以管理员权限运行'); - // 在生产环境中,可以选择是否强制要求管理员权限 - // 这里先创建窗口,让用户选择是否重新启动 - } - createWindow(); -}); -electron_1.app.on('window-all-closed', () => { - if (process.platform !== 'darwin') - electron_1.app.quit(); -}); -electron_1.app.on('activate', () => { - if (mainWindow === null) - createWindow(); -}); diff --git a/frontend/dist-electron/preload.js b/frontend/dist-electron/preload.js deleted file mode 100644 index 2b84432..0000000 --- a/frontend/dist-electron/preload.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const electron_1 = require("electron"); -window.addEventListener('DOMContentLoaded', () => { - console.log('Preload loaded'); -}); -// 暴露安全的 API 给渲染进程 -electron_1.contextBridge.exposeInMainWorld('electronAPI', { - openDevTools: () => electron_1.ipcRenderer.invoke('open-dev-tools'), - selectFolder: () => electron_1.ipcRenderer.invoke('select-folder'), - selectFile: (filters) => electron_1.ipcRenderer.invoke('select-file', filters), - // 初始化相关API - checkEnvironment: () => electron_1.ipcRenderer.invoke('check-environment'), - downloadPython: (mirror) => electron_1.ipcRenderer.invoke('download-python', mirror), - installPip: () => electron_1.ipcRenderer.invoke('install-pip'), - downloadGit: () => electron_1.ipcRenderer.invoke('download-git'), - installDependencies: (mirror) => electron_1.ipcRenderer.invoke('install-dependencies', mirror), - cloneBackend: (repoUrl) => electron_1.ipcRenderer.invoke('clone-backend', repoUrl), - updateBackend: (repoUrl) => electron_1.ipcRenderer.invoke('update-backend', repoUrl), - startBackend: () => electron_1.ipcRenderer.invoke('start-backend'), - // 管理员权限相关 - checkAdmin: () => electron_1.ipcRenderer.invoke('check-admin'), - restartAsAdmin: () => electron_1.ipcRenderer.invoke('restart-as-admin'), - // 配置文件操作 - saveConfig: (config) => electron_1.ipcRenderer.invoke('save-config', config), - loadConfig: () => electron_1.ipcRenderer.invoke('load-config'), - resetConfig: () => electron_1.ipcRenderer.invoke('reset-config'), - // 日志文件操作 - saveLogsToFile: (logs) => electron_1.ipcRenderer.invoke('save-logs-to-file', logs), - loadLogsFromFile: () => electron_1.ipcRenderer.invoke('load-logs-from-file'), - // 监听下载进度 - onDownloadProgress: (callback) => { - electron_1.ipcRenderer.on('download-progress', (_, progress) => callback(progress)); - }, - removeDownloadProgressListener: () => { - electron_1.ipcRenderer.removeAllListeners('download-progress'); - }, -}); diff --git a/frontend/dist-electron/services/downloadService.js b/frontend/dist-electron/services/downloadService.js deleted file mode 100644 index 3f5628b..0000000 --- a/frontend/dist-electron/services/downloadService.js +++ /dev/null @@ -1,86 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setMainWindow = setMainWindow; -exports.downloadFile = downloadFile; -const https = __importStar(require("https")); -const fs = __importStar(require("fs")); -const http = __importStar(require("http")); -let mainWindow = null; -function setMainWindow(window) { - mainWindow = window; -} -function downloadFile(url, outputPath) { - return new Promise((resolve, reject) => { - console.log(`开始下载文件: ${url}`); - console.log(`保存路径: ${outputPath}`); - const file = fs.createWriteStream(outputPath); - // 创建HTTP客户端,兼容https和http - const client = url.startsWith('https') ? https : http; - client - .get(url, response => { - const totalSize = parseInt(response.headers['content-length'] || '0', 10); - let downloadedSize = 0; - console.log(`文件大小: ${totalSize} bytes`); - response.pipe(file); - response.on('data', chunk => { - downloadedSize += chunk.length; - const progress = totalSize ? Math.round((downloadedSize / totalSize) * 100) : 0; - console.log(`下载进度: ${progress}% (${downloadedSize}/${totalSize})`); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - progress, - status: 'downloading', - message: `下载中... ${progress}%`, - }); - } - }); - file.on('finish', () => { - file.close(); - console.log(`文件下载完成: ${outputPath}`); - resolve(); - }); - file.on('error', err => { - console.error(`文件写入错误: ${err.message}`); - fs.unlink(outputPath, () => { }); // 删除不完整的文件 - reject(err); - }); - }) - .on('error', err => { - console.error(`下载错误: ${err.message}`); - reject(err); - }); - }); -} diff --git a/frontend/dist-electron/services/environmentService.js b/frontend/dist-electron/services/environmentService.js deleted file mode 100644 index fb26df5..0000000 --- a/frontend/dist-electron/services/environmentService.js +++ /dev/null @@ -1,65 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getAppRoot = getAppRoot; -exports.checkEnvironment = checkEnvironment; -const path = __importStar(require("path")); -const fs = __importStar(require("fs")); -const electron_1 = require("electron"); -// 获取应用根目录 -function getAppRoot() { - return process.env.NODE_ENV === 'development' ? process.cwd() : path.dirname(electron_1.app.getPath('exe')); -} -// 检查环境 -function checkEnvironment(appRoot) { - const environmentPath = path.join(appRoot, 'environment'); - const pythonPath = path.join(environmentPath, 'python'); - const gitPath = path.join(environmentPath, 'git'); - const backendPath = path.join(appRoot, 'backend'); - const requirementsPath = path.join(backendPath, 'requirements.txt'); - const pythonExists = fs.existsSync(pythonPath); - const gitExists = fs.existsSync(gitPath); - const backendExists = fs.existsSync(backendPath); - // 检查依赖是否已安装(简单检查是否存在site-packages目录) - const sitePackagesPath = path.join(pythonPath, 'Lib', 'site-packages'); - const dependenciesInstalled = fs.existsSync(sitePackagesPath) && fs.readdirSync(sitePackagesPath).length > 10; - return { - pythonExists, - gitExists, - backendExists, - dependenciesInstalled, - isInitialized: pythonExists && gitExists && backendExists && dependenciesInstalled, - }; -} diff --git a/frontend/dist-electron/services/gitService.js b/frontend/dist-electron/services/gitService.js deleted file mode 100644 index 5a60096..0000000 --- a/frontend/dist-electron/services/gitService.js +++ /dev/null @@ -1,321 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setMainWindow = setMainWindow; -exports.downloadGit = downloadGit; -exports.cloneBackend = cloneBackend; -const path = __importStar(require("path")); -const fs = __importStar(require("fs")); -const child_process_1 = require("child_process"); -const adm_zip_1 = __importDefault(require("adm-zip")); -const downloadService_1 = require("./downloadService"); -let mainWindow = null; -function setMainWindow(window) { - mainWindow = window; -} -const gitDownloadUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/git.zip'; -// 递归复制目录,包括文件和隐藏文件 -function copyDirSync(src, dest) { - if (!fs.existsSync(dest)) { - fs.mkdirSync(dest, { recursive: true }); - } - const entries = fs.readdirSync(src, { withFileTypes: true }); - for (const entry of entries) { - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, entry.name); - if (entry.isDirectory()) { - copyDirSync(srcPath, destPath); - } - else { - // 直接覆盖写,不需要先删除 - fs.copyFileSync(srcPath, destPath); - } - } -} -// 获取Git环境变量配置 -function getGitEnvironment(appRoot) { - const gitDir = path.join(appRoot, 'environment', 'git'); - const binPath = path.join(gitDir, 'bin'); - const mingw64BinPath = path.join(gitDir, 'mingw64', 'bin'); - const gitCorePath = path.join(gitDir, 'mingw64', 'libexec', 'git-core'); - return { - ...process.env, - // 修复remote-https问题的关键:确保所有Git相关路径都在PATH中 - PATH: `${binPath};${mingw64BinPath};${gitCorePath};${process.env.PATH}`, - GIT_EXEC_PATH: gitCorePath, - GIT_TEMPLATE_DIR: path.join(gitDir, 'mingw64', 'share', 'git-core', 'templates'), - HOME: process.env.USERPROFILE || process.env.HOME, - // // SSL证书路径 - // GIT_SSL_CAINFO: path.join(gitDir, 'mingw64', 'ssl', 'certs', 'ca-bundle.crt'), - // 禁用系统Git配置 - GIT_CONFIG_NOSYSTEM: '1', - // 禁用交互式认证 - GIT_TERMINAL_PROMPT: '0', - GIT_ASKPASS: '', - // // 修复remote-https问题的关键环境变量 - // CURL_CA_BUNDLE: path.join(gitDir, 'mingw64', 'ssl', 'certs', 'ca-bundle.crt'), - // 确保Git能找到所有必要的程序 - GIT_HTTP_LOW_SPEED_LIMIT: '0', - GIT_HTTP_LOW_SPEED_TIME: '0', - }; -} -// 检查是否为Git仓库 -function isGitRepository(dirPath) { - const gitDir = path.join(dirPath, '.git'); - return fs.existsSync(gitDir); -} -// 下载Git -async function downloadGit(appRoot) { - try { - const environmentPath = path.join(appRoot, 'environment'); - const gitPath = path.join(environmentPath, 'git'); - if (!fs.existsSync(environmentPath)) { - fs.mkdirSync(environmentPath, { recursive: true }); - } - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'git', - progress: 0, - status: 'downloading', - message: '开始下载Git...', - }); - } - // 使用自定义Git压缩包 - const zipPath = path.join(environmentPath, 'git.zip'); - await (0, downloadService_1.downloadFile)(gitDownloadUrl, zipPath); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'git', - progress: 100, - status: 'extracting', - message: '正在解压Git...', - }); - } - // 解压Git到临时目录,然后移动到正确位置 - console.log(`开始解压Git到: ${gitPath}`); - // 创建临时解压目录 - const tempExtractPath = path.join(environmentPath, 'git_temp'); - if (!fs.existsSync(tempExtractPath)) { - fs.mkdirSync(tempExtractPath, { recursive: true }); - console.log(`创建临时解压目录: ${tempExtractPath}`); - } - // 解压到临时目录 - const zip = new adm_zip_1.default(zipPath); - zip.extractAllTo(tempExtractPath, true); - console.log(`Git解压到临时目录: ${tempExtractPath}`); - // 检查解压后的目录结构 - const tempContents = fs.readdirSync(tempExtractPath); - console.log(`临时目录内容:`, tempContents); - // 如果解压后有git子目录,则从git子目录移动内容 - let sourceDir = tempExtractPath; - if (tempContents.length === 1 && tempContents[0] === 'git') { - sourceDir = path.join(tempExtractPath, 'git'); - console.log(`检测到git子目录,使用源目录: ${sourceDir}`); - } - // 确保目标Git目录存在 - if (!fs.existsSync(gitPath)) { - fs.mkdirSync(gitPath, { recursive: true }); - console.log(`创建Git目录: ${gitPath}`); - } - // 移动文件到最终目录 - const sourceContents = fs.readdirSync(sourceDir); - for (const item of sourceContents) { - const sourcePath = path.join(sourceDir, item); - const targetPath = path.join(gitPath, item); - // 如果目标已存在,先删除 - if (fs.existsSync(targetPath)) { - if (fs.statSync(targetPath).isDirectory()) { - fs.rmSync(targetPath, { recursive: true, force: true }); - } - else { - fs.unlinkSync(targetPath); - } - } - // 移动文件或目录 - fs.renameSync(sourcePath, targetPath); - console.log(`移动: ${sourcePath} -> ${targetPath}`); - } - // 清理临时目录 - fs.rmSync(tempExtractPath, { recursive: true, force: true }); - console.log(`清理临时目录: ${tempExtractPath}`); - console.log(`Git解压完成到: ${gitPath}`); - // 删除zip文件 - fs.unlinkSync(zipPath); - console.log(`删除临时文件: ${zipPath}`); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'git', - progress: 100, - status: 'completed', - message: 'Git安装完成', - }); - } - return { success: true }; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'git', - progress: 0, - status: 'error', - message: `Git下载失败: ${errorMessage}`, - }); - } - return { success: false, error: errorMessage }; - } -} -// 克隆后端代码(替换原有核心逻辑) -async function cloneBackend(appRoot, repoUrl = 'https://github.com/DLmaster361/AUTO_MAA.git') { - try { - const backendPath = appRoot; - const gitPath = path.join(appRoot, 'environment', 'git', 'bin', 'git.exe'); - if (!fs.existsSync(gitPath)) - throw new Error(`Git可执行文件不存在: ${gitPath}`); - const gitEnv = getGitEnvironment(appRoot); - // 检查 git 是否可用 - await new Promise((resolve, reject) => { - const proc = (0, child_process_1.spawn)(gitPath, ['--version'], { env: gitEnv }); - proc.on('close', code => (code === 0 ? resolve() : reject(new Error('git 无法正常运行')))); - proc.on('error', reject); - }); - // ==== 下面是关键逻辑 ==== - if (isGitRepository(backendPath)) { - // 已是 git 仓库,直接 pull - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'backend', - progress: 0, - status: 'downloading', - message: '正在更新后端代码...', - }); - } - await new Promise((resolve, reject) => { - const proc = (0, child_process_1.spawn)(gitPath, ['pull'], { stdio: 'pipe', env: gitEnv, cwd: backendPath }); - proc.stdout?.on('data', d => console.log('git pull:', d.toString())); - proc.stderr?.on('data', d => console.log('git pull err:', d.toString())); - proc.on('close', code => code === 0 ? resolve() : reject(new Error(`git pull失败,退出码: ${code}`))); - proc.on('error', reject); - }); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'backend', - progress: 100, - status: 'completed', - message: '后端代码更新完成', - }); - } - } - else { - // 不是 git 仓库,clone 到 tmp,再拷贝出来 - const tmpDir = path.join(appRoot, 'git_tmp'); - if (fs.existsSync(tmpDir)) - fs.rmSync(tmpDir, { recursive: true, force: true }); - fs.mkdirSync(tmpDir, { recursive: true }); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'backend', - progress: 0, - status: 'downloading', - message: '正在克隆后端代码...', - }); - } - await new Promise((resolve, reject) => { - const proc = (0, child_process_1.spawn)(gitPath, [ - 'clone', - '--progress', - '--verbose', - '--single-branch', - '--depth', - '1', - '--branch', - 'feature/refactor', - repoUrl, - tmpDir, - ], { - stdio: 'pipe', - env: gitEnv, - cwd: appRoot, - }); - proc.stdout?.on('data', d => console.log('git clone:', d.toString())); - proc.stderr?.on('data', d => console.log('git clone err:', d.toString())); - proc.on('close', code => code === 0 ? resolve() : reject(new Error(`git clone失败,退出码: ${code}`))); - proc.on('error', reject); - }); - // 复制所有文件到 backendPath(appRoot),包含 .git - const tmpFiles = fs.readdirSync(tmpDir); - for (const file of tmpFiles) { - const src = path.join(tmpDir, file); - const dst = path.join(backendPath, file); - if (fs.existsSync(dst)) { - if (fs.statSync(dst).isDirectory()) - fs.rmSync(dst, { recursive: true, force: true }); - else - fs.unlinkSync(dst); - } - if (fs.statSync(src).isDirectory()) - copyDirSync(src, dst); - else - fs.copyFileSync(src, dst); - } - fs.rmSync(tmpDir, { recursive: true, force: true }); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'backend', - progress: 100, - status: 'completed', - message: '后端代码克隆完成', - }); - } - } - return { success: true }; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - console.error('获取后端代码失败:', errorMessage); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'backend', - progress: 0, - status: 'error', - message: `后端代码获取失败: ${errorMessage}`, - }); - } - return { success: false, error: errorMessage }; - } -} diff --git a/frontend/dist-electron/services/pythonService.js b/frontend/dist-electron/services/pythonService.js deleted file mode 100644 index 05a64c1..0000000 --- a/frontend/dist-electron/services/pythonService.js +++ /dev/null @@ -1,489 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setMainWindow = setMainWindow; -exports.downloadPython = downloadPython; -exports.installDependencies = installDependencies; -exports.installPipPackage = installPipPackage; -exports.startBackend = startBackend; -const path = __importStar(require("path")); -const fs = __importStar(require("fs")); -const child_process_1 = require("child_process"); -const adm_zip_1 = __importDefault(require("adm-zip")); -const downloadService_1 = require("./downloadService"); -let mainWindow = null; -function setMainWindow(window) { - mainWindow = window; -} -// Python镜像源URL映射 -const pythonMirrorUrls = { - official: 'https://www.python.org/ftp/python/3.12.0/python-3.12.0-embed-amd64.zip', - tsinghua: 'https://mirrors.tuna.tsinghua.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip', - ustc: 'https://mirrors.ustc.edu.cn/python/3.12.0/python-3.12.0-embed-amd64.zip', - huawei: 'https://mirrors.huaweicloud.com/repository/toolkit/python/3.12.0/python-3.12.0-embed-amd64.zip', - aliyun: 'https://mirrors.aliyun.com/python-release/windows/python-3.12.0-embed-amd64.zip', -}; -// 检查pip是否已安装 -function isPipInstalled(pythonPath) { - const scriptsPath = path.join(pythonPath, 'Scripts'); - const pipExePath = path.join(scriptsPath, 'pip.exe'); - const pip3ExePath = path.join(scriptsPath, 'pip3.exe'); - console.log(`检查pip安装状态:`); - console.log(`Scripts目录: ${scriptsPath}`); - console.log(`pip.exe路径: ${pipExePath}`); - console.log(`pip3.exe路径: ${pip3ExePath}`); - const scriptsExists = fs.existsSync(scriptsPath); - const pipExists = fs.existsSync(pipExePath); - const pip3Exists = fs.existsSync(pip3ExePath); - console.log(`Scripts目录存在: ${scriptsExists}`); - console.log(`pip.exe存在: ${pipExists}`); - console.log(`pip3.exe存在: ${pip3Exists}`); - return scriptsExists && (pipExists || pip3Exists); -} -// 安装pip -async function installPip(pythonPath, appRoot) { - console.log('开始检查pip安装状态...'); - const pythonExe = path.join(pythonPath, 'python.exe'); - // 检查Python可执行文件是否存在 - if (!fs.existsSync(pythonExe)) { - throw new Error(`Python可执行文件不存在: ${pythonExe}`); - } - // 检查pip是否已安装 - if (isPipInstalled(pythonPath)) { - console.log('pip已经安装,跳过安装步骤'); - console.log('检测到pip.exe文件存在,认为pip安装成功'); - console.log('pip检查完成'); - return; - } - console.log('pip未安装,开始安装...'); - const getPipPath = path.join(pythonPath, 'get-pip.py'); - const getPipUrl = 'http://221.236.27.82:10197/d/AUTO_MAA/get-pip.py'; - console.log(`Python可执行文件路径: ${pythonExe}`); - console.log(`get-pip.py下载URL: ${getPipUrl}`); - console.log(`get-pip.py保存路径: ${getPipPath}`); - // 下载get-pip.py - console.log('开始下载get-pip.py...'); - try { - await (0, downloadService_1.downloadFile)(getPipUrl, getPipPath); - console.log('get-pip.py下载完成'); - // 检查下载的文件大小 - const stats = fs.statSync(getPipPath); - console.log(`get-pip.py文件大小: ${stats.size} bytes`); - if (stats.size < 10000) { - // 如果文件小于10KB,可能是无效文件 - throw new Error(`get-pip.py文件大小异常: ${stats.size} bytes,可能下载失败`); - } - } - catch (error) { - console.error('下载get-pip.py失败:', error); - throw new Error(`下载get-pip.py失败: ${error}`); - } - // 执行pip安装 - await new Promise((resolve, reject) => { - console.log('执行pip安装命令...'); - const process = (0, child_process_1.spawn)(pythonExe, [getPipPath], { - cwd: pythonPath, - stdio: 'pipe', - }); - process.stdout?.on('data', data => { - const output = data.toString(); - console.log('pip安装输出:', output); - }); - process.stderr?.on('data', data => { - const errorOutput = data.toString(); - console.log('pip安装错误输出:', errorOutput); - }); - process.on('close', code => { - console.log(`pip安装完成,退出码: ${code}`); - if (code === 0) { - console.log('pip安装成功'); - resolve(); - } - else { - reject(new Error(`pip安装失败,退出码: ${code}`)); - } - }); - process.on('error', error => { - console.error('pip安装进程错误:', error); - reject(error); - }); - }); - // 验证pip是否安装成功 - console.log('验证pip安装...'); - await new Promise((resolve, reject) => { - const verifyProcess = (0, child_process_1.spawn)(pythonExe, ['-m', 'pip', '--version'], { - cwd: pythonPath, - stdio: 'pipe', - }); - verifyProcess.stdout?.on('data', data => { - const output = data.toString(); - console.log('pip版本信息:', output); - }); - verifyProcess.stderr?.on('data', data => { - const errorOutput = data.toString(); - console.log('pip版本检查错误:', errorOutput); - }); - verifyProcess.on('close', code => { - if (code === 0) { - console.log('pip验证成功'); - resolve(); - } - else { - reject(new Error(`pip验证失败,退出码: ${code}`)); - } - }); - verifyProcess.on('error', error => { - console.error('pip验证进程错误:', error); - reject(error); - }); - }); - // 清理临时文件 - console.log('清理临时文件...'); - try { - if (fs.existsSync(getPipPath)) { - fs.unlinkSync(getPipPath); - console.log('get-pip.py临时文件已删除'); - } - } - catch (error) { - console.warn('清理get-pip.py文件时出错:', error); - } - console.log('pip安装和验证完成'); -} -// 下载Python -async function downloadPython(appRoot, mirror = 'ustc') { - try { - const environmentPath = path.join(appRoot, 'environment'); - const pythonPath = path.join(environmentPath, 'python'); - // 确保environment目录存在 - if (!fs.existsSync(environmentPath)) { - fs.mkdirSync(environmentPath, { recursive: true }); - } - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'python', - progress: 0, - status: 'downloading', - message: '开始下载Python...', - }); - } - // 根据选择的镜像源获取下载链接 - const pythonUrl = pythonMirrorUrls[mirror] || pythonMirrorUrls.ustc; - const zipPath = path.join(environmentPath, 'python.zip'); - await (0, downloadService_1.downloadFile)(pythonUrl, zipPath); - // 检查下载的Python文件大小 - const stats = fs.statSync(zipPath); - console.log(`Python压缩包大小: ${stats.size} bytes (${(stats.size / 1024 / 1024).toFixed(2)} MB)`); - // Python 3.12.0嵌入式版本应该大约30MB,如果小于5MB可能是无效文件 - if (stats.size < 5 * 1024 * 1024) { - // 5MB - fs.unlinkSync(zipPath); // 删除无效文件 - throw new Error(`Python下载文件大小异常: ${stats.size} bytes (${(stats.size / 1024).toFixed(2)} KB)。可能是对应镜像站不可用。请选择任意一个其他镜像源进行下载!`); - } - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'python', - progress: 100, - status: 'extracting', - message: '正在解压Python...', - }); - } - // 解压Python到指定目录 - console.log(`开始解压Python到: ${pythonPath}`); - // 确保Python目录存在 - if (!fs.existsSync(pythonPath)) { - fs.mkdirSync(pythonPath, { recursive: true }); - console.log(`创建Python目录: ${pythonPath}`); - } - const zip = new adm_zip_1.default(zipPath); - zip.extractAllTo(pythonPath, true); - console.log(`Python解压完成到: ${pythonPath}`); - // 删除zip文件 - fs.unlinkSync(zipPath); - console.log(`删除临时文件: ${zipPath}`); - // 启用 site-packages 支持 - const pthFile = path.join(pythonPath, 'python312._pth'); - if (fs.existsSync(pthFile)) { - let content = fs.readFileSync(pthFile, 'utf-8'); - content = content.replace(/^#import site/m, 'import site'); - fs.writeFileSync(pthFile, content, 'utf-8'); - console.log('已启用 site-packages 支持'); - } - // 安装pip - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'python', - progress: 80, - status: 'installing', - message: '正在安装pip...', - }); - } - await installPip(pythonPath, appRoot); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'python', - progress: 100, - status: 'completed', - message: 'Python和pip安装完成', - }); - } - return { success: true }; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'python', - progress: 0, - status: 'error', - message: `Python下载失败: ${errorMessage}`, - }); - } - return { success: false, error: errorMessage }; - } -} -// pip镜像源URL映射 -const pipMirrorUrls = { - official: 'https://pypi.org/simple/', - tsinghua: 'https://pypi.tuna.tsinghua.edu.cn/simple/', - ustc: 'https://pypi.mirrors.ustc.edu.cn/simple/', - aliyun: 'https://mirrors.aliyun.com/pypi/simple/', - douban: 'https://pypi.douban.com/simple/', -}; -// 安装Python依赖 -async function installDependencies(appRoot, mirror = 'tsinghua') { - try { - const pythonPath = path.join(appRoot, 'environment', 'python', 'python.exe'); - const backendPath = path.join(appRoot); - const requirementsPath = path.join(appRoot, 'requirements.txt'); - // 检查文件是否存在 - if (!fs.existsSync(pythonPath)) { - throw new Error('Python可执行文件不存在'); - } - if (!fs.existsSync(requirementsPath)) { - throw new Error('requirements.txt文件不存在'); - } - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'dependencies', - progress: 0, - status: 'downloading', - message: '正在安装Python依赖包...', - }); - } - // 获取pip镜像源URL - const pipMirrorUrl = pipMirrorUrls[mirror] || pipMirrorUrls.tsinghua; - // 使用Scripts文件夹中的pip.exe - const pythonDir = path.join(appRoot, 'environment', 'python'); - const pipExePath = path.join(pythonDir, 'Scripts', 'pip.exe'); - console.log(`开始安装Python依赖`); - console.log(`Python目录: ${pythonDir}`); - console.log(`pip.exe路径: ${pipExePath}`); - console.log(`requirements.txt路径: ${requirementsPath}`); - console.log(`pip镜像源: ${pipMirrorUrl}`); - // 检查pip.exe是否存在 - if (!fs.existsSync(pipExePath)) { - throw new Error(`pip.exe不存在: ${pipExePath}`); - } - // 安装依赖 - 直接使用pip.exe而不是python -m pip - await new Promise((resolve, reject) => { - const process = (0, child_process_1.spawn)(pipExePath, [ - 'install', - '-r', - requirementsPath, - '-i', - pipMirrorUrl, - '--trusted-host', - new URL(pipMirrorUrl).hostname, - ], { - cwd: backendPath, - stdio: 'pipe', - }); - process.stdout?.on('data', data => { - const output = data.toString(); - console.log('Pip output:', output); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'dependencies', - progress: 50, - status: 'downloading', - message: '正在安装依赖包...', - }); - } - }); - process.stderr?.on('data', data => { - const errorOutput = data.toString(); - console.error('Pip error:', errorOutput); - }); - process.on('close', code => { - console.log(`pip安装完成,退出码: ${code}`); - if (code === 0) { - resolve(); - } - else { - reject(new Error(`依赖安装失败,退出码: ${code}`)); - } - }); - process.on('error', error => { - console.error('pip进程错误:', error); - reject(error); - }); - }); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'dependencies', - progress: 100, - status: 'completed', - message: 'Python依赖安装完成', - }); - } - return { success: true }; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'dependencies', - progress: 0, - status: 'error', - message: `依赖安装失败: ${errorMessage}`, - }); - } - return { success: false, error: errorMessage }; - } -} -// 导出pip安装函数 -async function installPipPackage(appRoot) { - try { - const pythonPath = path.join(appRoot, 'environment', 'python'); - if (!fs.existsSync(pythonPath)) { - throw new Error('Python环境不存在,请先安装Python'); - } - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'pip', - progress: 0, - status: 'installing', - message: '正在安装pip...', - }); - } - await installPip(pythonPath, appRoot); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'pip', - progress: 100, - status: 'completed', - message: 'pip安装完成', - }); - } - return { success: true }; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - if (mainWindow) { - mainWindow.webContents.send('download-progress', { - type: 'pip', - progress: 0, - status: 'error', - message: `pip安装失败: ${errorMessage}`, - }); - } - return { success: false, error: errorMessage }; - } -} -// 启动后端 -async function startBackend(appRoot) { - try { - const pythonPath = path.join(appRoot, 'environment', 'python', 'python.exe'); - const backendPath = path.join(appRoot); - const mainPyPath = path.join(backendPath, 'main.py'); - // 检查文件是否存在 - if (!fs.existsSync(pythonPath)) { - throw new Error('Python可执行文件不存在'); - } - if (!fs.existsSync(mainPyPath)) { - throw new Error('后端主文件不存在'); - } - console.log(`启动后端指令: "${pythonPath}" "${mainPyPath}"(cwd: ${appRoot})`); - // 启动后端进程 - const backendProcess = (0, child_process_1.spawn)(pythonPath, [mainPyPath], { - cwd: appRoot, - stdio: 'pipe', - env: { - ...process.env, - PYTHONIOENCODING: 'utf-8', // 设置Python输出编码为UTF-8 - }, - }); - // 等待后端启动 - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('后端启动超时')); - }, 30000); // 30秒超时 - backendProcess.stdout?.on('data', data => { - const output = data.toString(); - console.log('Backend output:', output); - // 检查是否包含启动成功的标志 - if (output.includes('Uvicorn running') || output.includes('36163')) { - clearTimeout(timeout); - resolve(); - } - }); - // ✅ 重要:也要监听 stderr - backendProcess.stderr?.on('data', data => { - const output = data.toString(); - console.error('Backend error:', output); // 保留原有日志 - // ✅ 在 stderr 中也检查启动标志 - if (output.includes('Uvicorn running') || output.includes('36163')) { - clearTimeout(timeout); - resolve(); - } - }); - backendProcess.stderr?.on('data', data => { - console.error('Backend error:', data.toString()); - }); - backendProcess.on('error', error => { - clearTimeout(timeout); - reject(error); - }); - }); - return { success: true }; - } - catch (error) { - return { success: false, error: error instanceof Error ? error.message : String(error) }; - } -}