158 lines
4.5 KiB
JavaScript
158 lines
4.5 KiB
JavaScript
// main.js —— Electron 主进程(瘦客户端壳)。
|
|
// 启动流程:读后端各服务地址配置 → 起本地静态 + /api 反代服务器(仅监听 127.0.0.1)→ 建窗口加载。
|
|
// 前端 ui/ 源码零改动:相对路径 /api 与 SSE 流靠「同源」天然可用,后端 CORS 也无需改。
|
|
// 反代功能上只走 bff(BFF 是前端唯一入口);qa/rag/vstore/paper/bioflow 地址在设置里可改、持久化,
|
|
// 实际的各服务再路由由 BFF 自己的 config/.env 完成(见 README)。
|
|
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const { createServer } = require('./server');
|
|
const { loadServices, saveServices } = require('./config');
|
|
|
|
let mainWindow = null;
|
|
let settingsWindow = null;
|
|
let server = null;
|
|
let currentOrigin = null;
|
|
|
|
/** 前端构建产物目录:开发态在 desktop/renderer-dist,打包态在 resources/renderer-dist(extraResources)。 */
|
|
function staticDir() {
|
|
return app.isPackaged
|
|
? path.join(process.resourcesPath, 'renderer-dist')
|
|
: path.join(__dirname, 'renderer-dist');
|
|
}
|
|
|
|
function startServer(bffUrl) {
|
|
return new Promise((resolve, reject) => {
|
|
const srv = createServer({ staticDir: staticDir(), backendUrl: bffUrl });
|
|
srv.once('error', reject);
|
|
srv.listen(0, '127.0.0.1', () => {
|
|
server = srv;
|
|
resolve(`http://127.0.0.1:${srv.address().port}`);
|
|
});
|
|
});
|
|
}
|
|
|
|
function stopServer() {
|
|
if (server) {
|
|
server.close();
|
|
server = null;
|
|
}
|
|
}
|
|
|
|
async function restartServer(bffUrl) {
|
|
stopServer();
|
|
currentOrigin = await startServer(bffUrl);
|
|
return currentOrigin;
|
|
}
|
|
|
|
function createMainWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1280,
|
|
height: 860,
|
|
minWidth: 960,
|
|
minHeight: 640,
|
|
title: '海古纪 PlantAI',
|
|
backgroundColor: '#0f1115',
|
|
show: false,
|
|
webPreferences: {
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: true,
|
|
},
|
|
});
|
|
mainWindow.loadURL(currentOrigin);
|
|
mainWindow.once('ready-to-show', () => mainWindow.show());
|
|
mainWindow.on('closed', () => {
|
|
mainWindow = null;
|
|
});
|
|
}
|
|
|
|
function openSettingsWindow() {
|
|
if (settingsWindow) {
|
|
settingsWindow.focus();
|
|
return;
|
|
}
|
|
settingsWindow = new BrowserWindow({
|
|
width: 500,
|
|
height: 560,
|
|
resizable: false,
|
|
minimizable: false,
|
|
maximizable: false,
|
|
title: '后端服务设置',
|
|
parent: mainWindow || undefined,
|
|
modal: !!mainWindow,
|
|
autoHideMenuBar: true,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
sandbox: true,
|
|
},
|
|
});
|
|
settingsWindow.loadFile(path.join(__dirname, 'settings.html'));
|
|
settingsWindow.on('closed', () => {
|
|
settingsWindow = null;
|
|
});
|
|
}
|
|
|
|
function buildMenu() {
|
|
const template = [
|
|
{
|
|
label: '设置',
|
|
submenu: [
|
|
{ label: '后端服务地址…', click: openSettingsWindow },
|
|
{ type: 'separator' },
|
|
{ role: 'reload' },
|
|
{ role: 'toggleDevTools' },
|
|
{ type: 'separator' },
|
|
{ role: 'quit', label: '退出' },
|
|
],
|
|
},
|
|
];
|
|
if (process.platform === 'darwin') {
|
|
template.unshift({
|
|
label: app.name,
|
|
submenu: [{ role: 'about' }, { type: 'separator' }, { role: 'quit' }],
|
|
});
|
|
}
|
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
}
|
|
|
|
app.whenReady().then(async () => {
|
|
buildMenu();
|
|
|
|
const dir = staticDir();
|
|
if (!fs.existsSync(path.join(dir, 'index.html'))) {
|
|
dialog.showErrorBox(
|
|
'缺少前端构建产物',
|
|
`未找到 ${dir}\n\n请先构建前端:\n cd ui && npm run build\n cd desktop && npm run build:ui`
|
|
);
|
|
app.quit();
|
|
return;
|
|
}
|
|
|
|
ipcMain.handle('desktop:get-config', () => loadServices(app.getPath('userData')));
|
|
ipcMain.handle('desktop:set-config', async (_event, services) => {
|
|
// 用户显式保存后,文件配置优先于环境变量(否则 env 会一直盖住用户设置)
|
|
delete process.env.PLANTAI_BFF_URL;
|
|
const saved = saveServices(app.getPath('userData'), services);
|
|
await restartServer(saved.bff);
|
|
if (mainWindow) await mainWindow.loadURL(currentOrigin);
|
|
return saved;
|
|
});
|
|
|
|
const services = loadServices(app.getPath('userData'));
|
|
currentOrigin = await startServer(services.bff);
|
|
createMainWindow();
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) createMainWindow();
|
|
});
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') app.quit();
|
|
});
|
|
|
|
app.on('will-quit', stopServer);
|