| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- const { app, BrowserWindow, dialog, Tray, Menu } = require('electron');
- const { spawn } = require('child_process');
- const path = require('path');
- const http = require('http');
- const fs = require('fs');
- let mainWindow;
- let serverProcess;
- let tray = null;
- const PORT = 3000;
- function getBinaryPath() {
- const isDev = process.env.NODE_ENV === 'development';
- const platform = process.platform;
- if (isDev) {
- const binaryName = platform === 'win32' ? 'new-api.exe' : 'new-api';
- return path.join(__dirname, '..', binaryName);
- }
- let binaryName;
- switch (platform) {
- case 'win32':
- binaryName = 'new-api.exe';
- break;
- case 'darwin':
- binaryName = 'new-api';
- break;
- case 'linux':
- binaryName = 'new-api';
- break;
- default:
- binaryName = 'new-api';
- }
- return path.join(process.resourcesPath, 'bin', binaryName);
- }
- function startServer() {
- return new Promise((resolve, reject) => {
- const binaryPath = getBinaryPath();
- const isDev = process.env.NODE_ENV === 'development';
- console.log('Starting server from:', binaryPath);
- const env = { ...process.env, PORT: PORT.toString() };
- let dataDir;
- if (isDev) {
- dataDir = path.join(__dirname, '..', 'data');
- } else {
- const userDataPath = app.getPath('userData');
- dataDir = path.join(userDataPath, 'data');
- }
- if (!fs.existsSync(dataDir)) {
- fs.mkdirSync(dataDir, { recursive: true });
- }
- env.SQLITE_PATH = path.join(dataDir, 'new-api.db');
- const workingDir = isDev
- ? path.join(__dirname, '..')
- : process.resourcesPath;
- serverProcess = spawn(binaryPath, [], {
- env,
- cwd: workingDir
- });
- serverProcess.stdout.on('data', (data) => {
- console.log(`Server: ${data}`);
- });
- serverProcess.stderr.on('data', (data) => {
- console.error(`Server Error: ${data}`);
- });
- serverProcess.on('error', (err) => {
- console.error('Failed to start server:', err);
- reject(err);
- });
- serverProcess.on('close', (code) => {
- console.log(`Server process exited with code ${code}`);
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.close();
- }
- });
- waitForServer(resolve, reject);
- });
- }
- function waitForServer(resolve, reject, retries = 30) {
- if (retries === 0) {
- reject(new Error('Server failed to start within timeout'));
- return;
- }
- const req = http.get(`http://localhost:${PORT}`, (res) => {
- console.log('Server is ready');
- resolve();
- });
- req.on('error', () => {
- setTimeout(() => waitForServer(resolve, reject, retries - 1), 1000);
- });
- req.end();
- }
- function createWindow() {
- mainWindow = new BrowserWindow({
- width: 1400,
- height: 900,
- webPreferences: {
- preload: path.join(__dirname, 'preload.js'),
- nodeIntegration: false,
- contextIsolation: true
- },
- title: 'New API',
- icon: path.join(__dirname, 'icon.png')
- });
- mainWindow.loadURL(`http://localhost:${PORT}`);
- if (process.env.NODE_ENV === 'development') {
- mainWindow.webContents.openDevTools();
- }
- // Close to tray instead of quitting
- mainWindow.on('close', (event) => {
- if (!app.isQuitting) {
- event.preventDefault();
- mainWindow.hide();
- if (process.platform === 'darwin') {
- app.dock.hide();
- }
- }
- });
- mainWindow.on('closed', () => {
- mainWindow = null;
- });
- }
- function createTray() {
- // Use template icon for macOS (black with transparency, auto-adapts to theme)
- // Use colored icon for Windows
- const trayIconPath = process.platform === 'darwin'
- ? path.join(__dirname, 'tray-iconTemplate.png')
- : path.join(__dirname, 'tray-icon-windows.png');
- tray = new Tray(trayIconPath);
- const contextMenu = Menu.buildFromTemplate([
- {
- label: 'Show New API',
- click: () => {
- if (mainWindow === null) {
- createWindow();
- } else {
- mainWindow.show();
- if (process.platform === 'darwin') {
- app.dock.show();
- }
- }
- }
- },
- { type: 'separator' },
- {
- label: 'Quit',
- click: () => {
- app.isQuitting = true;
- app.quit();
- }
- }
- ]);
- tray.setToolTip('New API');
- tray.setContextMenu(contextMenu);
- // On macOS, clicking the tray icon shows the window
- tray.on('click', () => {
- if (mainWindow === null) {
- createWindow();
- } else {
- mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show();
- if (mainWindow.isVisible() && process.platform === 'darwin') {
- app.dock.show();
- }
- }
- });
- }
- app.whenReady().then(async () => {
- try {
- await startServer();
- createTray();
- createWindow();
- } catch (err) {
- console.error('Failed to start application:', err);
- dialog.showErrorBox('Startup Error', `Failed to start server: ${err.message}`);
- app.quit();
- }
- });
- app.on('window-all-closed', () => {
- // Don't quit when window is closed, keep running in tray
- // Only quit when explicitly choosing Quit from tray menu
- });
- app.on('activate', () => {
- if (BrowserWindow.getAllWindows().length === 0) {
- createWindow();
- }
- });
- app.on('before-quit', (event) => {
- if (serverProcess) {
- event.preventDefault();
- console.log('Shutting down server...');
- serverProcess.kill('SIGTERM');
- setTimeout(() => {
- if (serverProcess) {
- serverProcess.kill('SIGKILL');
- }
- app.exit();
- }, 5000);
- serverProcess.on('close', () => {
- serverProcess = null;
- app.exit();
- });
- }
- });
|