refactor(.gitignore): add dist-electron to ignore list
This commit is contained in:
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
dist-electron
|
||||||
|
|||||||
@@ -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();
|
|
||||||
});
|
|
||||||
@@ -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');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user