Add electron packaging for single exe server distribution
This commit is contained in:
@@ -1,68 +1,107 @@
|
|||||||
const { app, BrowserWindow } = require('electron');
|
const { app, Tray, Menu, nativeImage } = require('electron');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process');
|
||||||
|
|
||||||
let mainWindow = null;
|
let tray = null;
|
||||||
let serverProcess = null;
|
|
||||||
|
|
||||||
function startServer() {
|
function startServer() {
|
||||||
console.log('Starting OpenChamber server...');
|
console.log('Starting OpenChamber server...');
|
||||||
|
|
||||||
serverProcess = spawn('bun', ['run', 'start:web'], {
|
let serverPath;
|
||||||
cwd: path.join(__dirname, '..'),
|
let cwdPath;
|
||||||
|
let execPath;
|
||||||
|
|
||||||
|
if (app.isPackaged) {
|
||||||
|
serverPath = path.join(process.resourcesPath, 'server', 'index.js');
|
||||||
|
cwdPath = path.join(process.resourcesPath);
|
||||||
|
execPath = path.join(process.resourcesPath, 'nodejs', 'node.exe');
|
||||||
|
} else {
|
||||||
|
serverPath = path.join(__dirname, '..', 'server', 'index.js');
|
||||||
|
cwdPath = path.join(__dirname, '..');
|
||||||
|
execPath = 'node';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Server path:', serverPath);
|
||||||
|
console.log('CWD:', cwdPath);
|
||||||
|
console.log('Exec:', execPath);
|
||||||
|
|
||||||
|
const env = { ...process.env };
|
||||||
|
env.OPENCHAMBER_PORT = '3000';
|
||||||
|
env.OPENCODE_SKIP_START = 'true';
|
||||||
|
|
||||||
|
// 创建批处理脚本
|
||||||
|
const batContent = `"${execPath}" "${serverPath}"`;
|
||||||
|
const batPath = path.join(cwdPath, 'start-server.bat');
|
||||||
|
fs.writeFileSync(batPath, batContent);
|
||||||
|
|
||||||
|
console.log('Bat path:', batPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const child = spawn('cmd.exe', ['/c', batPath], {
|
||||||
|
cwd: cwdPath,
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
shell: true,
|
env: env,
|
||||||
detached: false
|
detached: false
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('error', (err) => {
|
child.on('error', (err) => {
|
||||||
console.error('Server error:', err);
|
console.error('Server error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
serverProcess.on('exit', (code) => {
|
child.on('exit', (code) => {
|
||||||
console.log('Server exited with code:', code);
|
console.log('Server exited:', code);
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to start:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWindow() {
|
function createTray() {
|
||||||
mainWindow = new BrowserWindow({
|
try {
|
||||||
width: 800,
|
let iconPath;
|
||||||
height: 600,
|
if (app.isPackaged) {
|
||||||
show: false,
|
iconPath = path.join(process.resourcesPath, 'public', 'favicon.png');
|
||||||
webPreferences: {
|
} else {
|
||||||
nodeIntegration: false,
|
iconPath = path.join(__dirname, '..', 'public', 'favicon.png');
|
||||||
contextIsolation: true
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.loadURL('http://localhost:3000');
|
let icon;
|
||||||
|
try {
|
||||||
|
icon = nativeImage.createFromPath(iconPath);
|
||||||
|
if (icon.isEmpty()) {
|
||||||
|
icon = nativeImage.createEmpty();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
icon = nativeImage.createEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
mainWindow.once('ready-to-show', () => {
|
tray = new Tray(icon);
|
||||||
mainWindow.show();
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.on('closed', () => {
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
mainWindow = null;
|
{ label: 'OpenChamber Server', enabled: false },
|
||||||
});
|
{ type: 'separator' },
|
||||||
|
{ label: 'Open http://localhost:3000', click: () => {
|
||||||
|
require('electron').shell.openExternal('http://localhost:3000');
|
||||||
|
}},
|
||||||
|
{ type: 'separator' },
|
||||||
|
{ label: 'Quit', click: () => {
|
||||||
|
app.quit();
|
||||||
|
}}
|
||||||
|
]);
|
||||||
|
|
||||||
|
tray.setToolTip('OpenChamber Server');
|
||||||
|
tray.setContextMenu(contextMenu);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to create tray:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
startServer();
|
startServer();
|
||||||
|
createTray();
|
||||||
setTimeout(() => {
|
|
||||||
createWindow();
|
|
||||||
}, 3000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
if (serverProcess) {
|
|
||||||
serverProcess.kill();
|
|
||||||
}
|
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('before-quit', () => {
|
|
||||||
if (serverProcess) {
|
|
||||||
serverProcess.kill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -17,12 +17,35 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"main.js",
|
"main.js",
|
||||||
"../dist/**/*",
|
|
||||||
"../server/**/*",
|
|
||||||
"../bin/**/*",
|
|
||||||
"../public/**/*",
|
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
|
"extraResources": [
|
||||||
|
{
|
||||||
|
"from": "../dist",
|
||||||
|
"to": "dist",
|
||||||
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "../server",
|
||||||
|
"to": "server",
|
||||||
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "../bin",
|
||||||
|
"to": "bin",
|
||||||
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "../public",
|
||||||
|
"to": "public",
|
||||||
|
"filter": ["**/*"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "D:\\Program Files\\nodejs",
|
||||||
|
"to": "nodejs",
|
||||||
|
"filter": ["node.exe"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"target": "portable"
|
"target": "portable"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user