mirror of
				https://github.com/open-webui/desktop
				synced 2025-06-26 18:15:59 +00:00 
			
		
		
		
	refac
This commit is contained in:
		
							parent
							
								
									8d97c049e6
								
							
						
					
					
						commit
						73740d7d73
					
				| @ -3,7 +3,7 @@ | ||||
| 	<head> | ||||
| 		<meta charset="UTF-8" /> | ||||
| 		<title>Open WebUI</title> | ||||
| 		<link rel="preload" href="/assets/fonts/InstrumentSerif-Regular.ttf" as="font" crossorigin="anonymous" /> | ||||
| 		<link rel="preload" href="/assets/fonts/InstrumentSerif-Regular.ttf" as="font"  /> | ||||
| 	</head> | ||||
| 	<body> | ||||
| 		<div id="app"></div> | ||||
|  | ||||
							
								
								
									
										57
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								src/main.ts
									
									
									
									
									
								
							| @ -9,7 +9,8 @@ import { | ||||
| 	BrowserWindow, | ||||
| 	globalShortcut, | ||||
| 	Notification, | ||||
| 	ipcMain | ||||
| 	ipcMain, | ||||
| 	ipcRenderer | ||||
| } from 'electron'; | ||||
| import path from 'path'; | ||||
| import started from 'electron-squirrel-startup'; | ||||
| @ -17,6 +18,7 @@ import started from 'electron-squirrel-startup'; | ||||
| import { | ||||
| 	installPackage, | ||||
| 	removePackage, | ||||
| 	logEmitter, | ||||
| 	startServer, | ||||
| 	stopAllServers, | ||||
| 	validateInstallation | ||||
| @ -56,6 +58,11 @@ if (!gotTheLock) { | ||||
| 	let tray: Tray | null = null; | ||||
| 
 | ||||
| 	let SERVER_URL = null; | ||||
| 	let SERVER_STATUS = 'stopped'; | ||||
| 
 | ||||
| 	logEmitter.on('log', (message) => { | ||||
| 		mainWindow?.webContents.send('main:log', message); | ||||
| 	}); | ||||
| 
 | ||||
| 	const loadDefaultView = () => { | ||||
| 		// Load index.html or dev server URL
 | ||||
| @ -66,6 +73,34 @@ if (!gotTheLock) { | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const startServerHandler = async () => { | ||||
| 		SERVER_STATUS = 'starting'; | ||||
| 		mainWindow.webContents.send('main:data', { | ||||
| 			type: 'server:status', | ||||
| 			data: SERVER_STATUS | ||||
| 		}); | ||||
| 
 | ||||
| 		try { | ||||
| 			SERVER_URL = await startServer(); | ||||
| 			SERVER_STATUS = 'started'; | ||||
| 			mainWindow.webContents.send('main:data', { | ||||
| 				type: 'server:status', | ||||
| 				data: SERVER_STATUS | ||||
| 			}); | ||||
| 
 | ||||
| 			mainWindow.loadURL(SERVER_URL); | ||||
| 		} catch (error) { | ||||
| 			console.error('Failed to start server:', error); | ||||
| 			SERVER_STATUS = 'failed'; | ||||
| 			mainWindow.webContents.send('main:data', { | ||||
| 				type: 'server:status', | ||||
| 				data: SERVER_STATUS | ||||
| 			}); | ||||
| 
 | ||||
| 			mainWindow.webContents.send('main:log', `Failed to start server: ${error}`); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const onReady = async () => { | ||||
| 		console.log(process.resourcesPath); | ||||
| 		console.log(app.getName()); | ||||
| @ -113,12 +148,7 @@ if (!gotTheLock) { | ||||
| 					data: true | ||||
| 				}); | ||||
| 
 | ||||
| 				try { | ||||
| 					SERVER_URL = await startServer(); | ||||
| 					mainWindow.loadURL(SERVER_URL); | ||||
| 				} catch (error) { | ||||
| 					console.error('Failed to start server:', error); | ||||
| 				} | ||||
| 				await startServerHandler(); | ||||
| 			} else { | ||||
| 				mainWindow.webContents.send('main:data', { | ||||
| 					type: 'install:status', | ||||
| @ -204,16 +234,25 @@ if (!gotTheLock) { | ||||
| 		removePackage(); | ||||
| 	}); | ||||
| 
 | ||||
| 	ipcMain.handle('server:status', async (event) => { | ||||
| 		return SERVER_STATUS; | ||||
| 	}); | ||||
| 
 | ||||
| 	ipcMain.handle('server:start', async (event) => { | ||||
| 		console.log('Starting server...'); | ||||
| 
 | ||||
| 		startServer(); | ||||
| 		await startServerHandler(); | ||||
| 	}); | ||||
| 
 | ||||
| 	ipcMain.handle('server:stop', async (event) => { | ||||
| 		console.log('Stopping server...'); | ||||
| 
 | ||||
| 		stopAllServers(); | ||||
| 		await stopAllServers(); | ||||
| 		SERVER_STATUS = 'stopped'; | ||||
| 		mainWindow.webContents.send('main:data', { | ||||
| 			type: 'server:status', | ||||
| 			data: SERVER_STATUS | ||||
| 		}); | ||||
| 	}); | ||||
| 
 | ||||
| 	ipcMain.handle('server:url', async (event) => { | ||||
|  | ||||
| @ -28,9 +28,12 @@ window.addEventListener('DOMContentLoaded', () => { | ||||
| }); | ||||
| 
 | ||||
| contextBridge.exposeInMainWorld('electronAPI', { | ||||
| 	sendPing: async () => { | ||||
| 		console.log('Sending PING to main process...'); | ||||
| 		await ipcRenderer.invoke('send-ping'); // Send the ping back to the main process
 | ||||
| 	onLog: (callback: (message: string) => void) => { | ||||
| 		if (!isLocalSource()) { | ||||
| 			throw new Error('Access restricted: This operation is only allowed in a local environment.'); | ||||
| 		} | ||||
| 
 | ||||
| 		ipcRenderer.on('main:log', (_, message: string) => callback(message)); | ||||
| 	}, | ||||
| 
 | ||||
| 	installPackage: async () => { | ||||
| @ -53,6 +56,14 @@ contextBridge.exposeInMainWorld('electronAPI', { | ||||
| 		await ipcRenderer.invoke('remove'); | ||||
| 	}, | ||||
| 
 | ||||
| 	getServerStatus: async () => { | ||||
| 		if (!isLocalSource()) { | ||||
| 			throw new Error('Access restricted: This operation is only allowed in a local environment.'); | ||||
| 		} | ||||
| 
 | ||||
| 		return await ipcRenderer.invoke('server:status'); | ||||
| 	}, | ||||
| 
 | ||||
| 	startServer: async () => { | ||||
| 		if (!isLocalSource()) { | ||||
| 			throw new Error('Access restricted: This operation is only allowed in a local environment.'); | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <script lang="ts"> | ||||
| 	import { onMount } from 'svelte'; | ||||
| 	import { installStatus } from './lib/stores'; | ||||
| 	import { installStatus, serverStatus } from './lib/stores'; | ||||
| 
 | ||||
| 	import Main from './lib/components/Main.svelte'; | ||||
| 
 | ||||
| @ -24,6 +24,12 @@ | ||||
| 
 | ||||
| 						break; | ||||
| 
 | ||||
| 					case 'electron:server:status': | ||||
| 						console.log('Server status:', event.data.data); | ||||
| 						serverStatus.set(event.data.data); | ||||
| 
 | ||||
| 						break; | ||||
| 
 | ||||
| 					default: | ||||
| 						console.warn('Unhandled message type:', event.data.type); | ||||
| 				} | ||||
| @ -32,6 +38,11 @@ | ||||
| 
 | ||||
| 		if (window.electronAPI) { | ||||
| 			installStatus.set(await window.electronAPI.getInstallStatus()); | ||||
| 			serverStatus.set(await window.electronAPI.getServerStatus()); | ||||
| 
 | ||||
| 			window.electronAPI.onLog((log) => { | ||||
| 				console.log('Electron log:', log); | ||||
| 			}); | ||||
| 		} | ||||
| 	}); | ||||
| </script> | ||||
|  | ||||
| @ -1,13 +1,16 @@ | ||||
| <script lang="ts"> | ||||
| 	import { onMount } from 'svelte'; | ||||
| 	import { installStatus } from '../stores'; | ||||
| 	import { installStatus, serverStatus } from '../stores'; | ||||
| 
 | ||||
| 	import Spinner from './common/Spinner.svelte'; | ||||
| 	import ArrowRightCircle from './icons/ArrowRightCircle.svelte'; | ||||
| 
 | ||||
| 	let installing = false; | ||||
| 
 | ||||
| 	const continueHandler = async () => { | ||||
| 		if (window?.electronAPI) { | ||||
| 			window.electronAPI.installPackage(); | ||||
| 			installing = true; | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { writable } from 'svelte/store'; | ||||
| 
 | ||||
| export const installStatus = writable(null); | ||||
| export const serverStatus = writable(null); | ||||
|  | ||||
| @ -1,6 +1,9 @@ | ||||
| import * as fs from 'fs'; | ||||
| import * as os from 'os'; | ||||
| import * as path from 'path'; | ||||
| import net from 'net'; | ||||
| import crypto from 'crypto'; | ||||
| 
 | ||||
| import { | ||||
| 	exec, | ||||
| 	execFile, | ||||
| @ -10,13 +13,16 @@ import { | ||||
| 	spawn, | ||||
| 	ChildProcess | ||||
| } from 'child_process'; | ||||
| import net from 'net'; | ||||
| import { EventEmitter } from 'events'; | ||||
| 
 | ||||
| import * as tar from 'tar'; | ||||
| import log from 'electron-log'; | ||||
| 
 | ||||
| import { app } from 'electron'; | ||||
| 
 | ||||
| // Create and export a global event emitter specifically for logs
 | ||||
| export const logEmitter = new EventEmitter(); | ||||
| 
 | ||||
| ////////////////////////////////////////////////
 | ||||
| //
 | ||||
| // General Utils
 | ||||
| @ -60,6 +66,18 @@ export function getOpenWebUIDataPath(): string { | ||||
| 	return openWebUIDataDir; | ||||
| } | ||||
| 
 | ||||
| export function getSecretKey(keyPath?: string, key?: string): string { | ||||
| 	keyPath = keyPath || path.join(getOpenWebUIDataPath(), '.key'); | ||||
| 
 | ||||
| 	if (fs.existsSync(keyPath)) { | ||||
| 		return fs.readFileSync(keyPath, 'utf-8'); | ||||
| 	} | ||||
| 
 | ||||
| 	key = key || crypto.randomBytes(64).toString('hex'); | ||||
| 	fs.writeFileSync(keyPath, key); | ||||
| 	return key; | ||||
| } | ||||
| 
 | ||||
| export async function portInUse(port: number, host: string = '127.0.0.1'): Promise<boolean> { | ||||
| 	return new Promise((resolve) => { | ||||
| 		const client = new net.Socket(); | ||||
| @ -182,22 +200,21 @@ export async function installOpenWebUI(installationPath: string) { | ||||
| 	//     unpackCommand = `${createAdHocSignCommand(installationPath)}\n${unpackCommand}`;
 | ||||
| 	// }
 | ||||
| 
 | ||||
| 	console.log(unpackCommand); | ||||
| 
 | ||||
| 	const commandProcess = exec(unpackCommand, { | ||||
| 		shell: process.platform === 'win32' ? 'cmd.exe' : '/bin/bash' | ||||
| 	}); | ||||
| 
 | ||||
| 	commandProcess.stdout?.on('data', (data) => { | ||||
| 	const onLog = (data) => { | ||||
| 		console.log(data); | ||||
| 	}); | ||||
| 		logEmitter.emit('log', data); | ||||
| 	}; | ||||
| 
 | ||||
| 	commandProcess.stderr?.on('data', (data) => { | ||||
| 		console.error(data); | ||||
| 	}); | ||||
| 	commandProcess.stdout?.on('data', onLog); | ||||
| 	commandProcess.stderr?.on('data', onLog); | ||||
| 
 | ||||
| 	commandProcess.on('exit', (code) => { | ||||
| 		console.log(`Child exited with code ${code}`); | ||||
| 		logEmitter.emit('log', `Child exited with code ${code}`); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| @ -209,6 +226,7 @@ export async function installBundledPython(installationPath?: string) { | ||||
| 	console.log(installationPath, pythonTarPath); | ||||
| 	if (!fs.existsSync(pythonTarPath)) { | ||||
| 		log.error('Python tarball not found'); | ||||
| 		logEmitter.emit('log', 'Python tarball not found'); // Emit log
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| @ -220,6 +238,7 @@ export async function installBundledPython(installationPath?: string) { | ||||
| 		}); | ||||
| 	} catch (error) { | ||||
| 		log.error(error); | ||||
| 		logEmitter.emit('log', error); // Emit log
 | ||||
| 	} | ||||
| 
 | ||||
| 	// Get the path to the installed Python binary
 | ||||
| @ -227,6 +246,7 @@ export async function installBundledPython(installationPath?: string) { | ||||
| 
 | ||||
| 	if (!fs.existsSync(bundledPythonPath)) { | ||||
| 		log.error('Python binary not found in install path'); | ||||
| 		logEmitter.emit('log', 'Python binary not found in install path'); // Emit log
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| @ -236,6 +256,7 @@ export async function installBundledPython(installationPath?: string) { | ||||
| 			encoding: 'utf-8' | ||||
| 		}); | ||||
| 		console.log('Installed Python Version:', pythonVersion.trim()); | ||||
| 		logEmitter.emit('log', `Installed Python Version: ${pythonVersion.trim()}`); // Emit log
 | ||||
| 	} catch (error) { | ||||
| 		log.error('Failed to execute Python binary', error); | ||||
| 	} | ||||
| @ -319,6 +340,7 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 
 | ||||
| 	if (!(await validateInstallation(installationPath))) { | ||||
| 		console.error('Failed to validate installation'); | ||||
| 		logEmitter.emit('log', 'Failed to validate installation'); // Emit log
 | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| @ -327,11 +349,11 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 			? `${installationPath}\\Scripts\\activate.bat && set DATA_DIR="${path.join( | ||||
| 					app.getPath('userData'), | ||||
| 					'data' | ||||
| 				)}" && open-webui serve` | ||||
| 				)}" && set WEBUI_SECRET_KEY=${getSecretKey()} && open-webui serve` | ||||
| 			: `source "${installationPath}/bin/activate" && export DATA_DIR="${path.join( | ||||
| 					app.getPath('userData'), | ||||
| 					'data' | ||||
| 				)}" && open-webui serve`;
 | ||||
| 				)}" && export WEBUI_SECRET_KEY=${getSecretKey()} && open-webui serve`;
 | ||||
| 
 | ||||
| 	port = port || 8080; | ||||
| 	while (await portInUse(port)) { | ||||
| @ -341,6 +363,8 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 	startCommand += ` --port ${port}`; | ||||
| 
 | ||||
| 	console.log('Starting Open-WebUI server...'); | ||||
| 	logEmitter.emit('log', 'Starting Open-WebUI server...'); // Emit log
 | ||||
| 
 | ||||
| 	const childProcess = spawn(startCommand, { | ||||
| 		shell: true, | ||||
| 		detached: true, | ||||
| @ -356,6 +380,7 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 			const handleLog = (data: Buffer) => { | ||||
| 				const logLine = data.toString().trim(); | ||||
| 				console.log(`[Open-WebUI Log]: ${logLine}`); | ||||
| 				logEmitter.emit('log', logLine); | ||||
| 
 | ||||
| 				// Look for "Uvicorn running on http://<hostname>:<port>"
 | ||||
| 				const match = logLine.match( | ||||
| @ -386,6 +411,7 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 	if (childProcess.pid) { | ||||
| 		serverPIDs.add(childProcess.pid); | ||||
| 		console.log(`Server started with PID: ${childProcess.pid}`); | ||||
| 		logEmitter.emit('log', `Server started with PID: ${childProcess.pid}`); // Emit PID log
 | ||||
| 	} else { | ||||
| 		throw new Error('Failed to start server: No PID available'); | ||||
| 	} | ||||
| @ -405,6 +431,7 @@ export async function startServer(installationPath?: string, port?: number): Pro | ||||
| 	} | ||||
| 
 | ||||
| 	console.log(`Server is now running at ${detectedURL}`); | ||||
| 	logEmitter.emit('log', `Server is now running at ${detectedURL}`); // Emit server URL log
 | ||||
| 	return detectedURL; // Return the detected URL
 | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user