/** * Viking Server Manager - Local OpenViking Server Management * * Manages a local OpenViking server instance for privacy-first deployment. * All data is stored locally in ~/.openviking/ - nothing is uploaded to remote servers. * * Usage: * const manager = getVikingServerManager(); * * // Check server status * const status = await manager.getStatus(); * * // Start server if not running * if (!status.running) { * await manager.start(); * } * * // Server is now available at http://127.0.0.1:1933 */ import { invoke } from '@tauri-apps/api/core'; // === Types === export interface VikingServerStatus { running: boolean; port: number; pid?: number; dataDir?: string; version?: string; error?: string; } export interface VikingServerConfig { port?: number; dataDir?: string; configFile?: string; } // === Default Configuration === const DEFAULT_CONFIG: Required = { port: 1933, dataDir: '', // Will use default ~/.openviking/workspace configFile: '', // Will use default ~/.openviking/ov.conf }; // === Server Manager Class === export class VikingServerManager { private status: VikingServerStatus | null = null; private startPromise: Promise | null = null; /** * Get current server status */ async getStatus(): Promise { try { this.status = await invoke('viking_server_status'); return this.status; } catch (err) { console.error('[VikingServerManager] Failed to get status:', err); return { running: false, port: DEFAULT_CONFIG.port, error: err instanceof Error ? err.message : String(err), }; } } /** * Start local OpenViking server * If server is already running, returns current status */ async start(config?: VikingServerConfig): Promise { // Prevent concurrent start attempts if (this.startPromise) { return this.startPromise; } // Check if already running const currentStatus = await this.getStatus(); if (currentStatus.running) { console.log('[VikingServerManager] Server already running on port', currentStatus.port); return currentStatus; } this.startPromise = this.doStart(config); try { const result = await this.startPromise; return result; } finally { this.startPromise = null; } } private async doStart(config?: VikingServerConfig): Promise { const fullConfig = { ...DEFAULT_CONFIG, ...config }; console.log('[VikingServerManager] Starting local server on port', fullConfig.port); try { const status = await invoke('viking_server_start', { config: { port: fullConfig.port, dataDir: fullConfig.dataDir || undefined, configFile: fullConfig.configFile || undefined, }, }); this.status = status; console.log('[VikingServerManager] Server started:', status); return status; } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); console.error('[VikingServerManager] Failed to start server:', errorMsg); this.status = { running: false, port: fullConfig.port, error: errorMsg, }; return this.status; } } /** * Stop local OpenViking server */ async stop(): Promise { console.log('[VikingServerManager] Stopping server'); try { await invoke('viking_server_stop'); this.status = { running: false, port: DEFAULT_CONFIG.port, }; console.log('[VikingServerManager] Server stopped'); } catch (err) { console.error('[VikingServerManager] Failed to stop server:', err); throw err; } } /** * Restart local OpenViking server */ async restart(config?: VikingServerConfig): Promise { console.log('[VikingServerManager] Restarting server'); try { const status = await invoke('viking_server_restart', { config: config ? { port: config.port, dataDir: config.dataDir, configFile: config.configFile, } : undefined, }); this.status = status; console.log('[VikingServerManager] Server restarted:', status); return status; } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); console.error('[VikingServerManager] Failed to restart server:', errorMsg); this.status = { running: false, port: config?.port || DEFAULT_CONFIG.port, error: errorMsg, }; return this.status; } } /** * Ensure server is running, starting if necessary * This is the main entry point for ensuring availability */ async ensureRunning(config?: VikingServerConfig): Promise { const status = await this.getStatus(); if (status.running) { return true; } const startResult = await this.start(config); return startResult.running; } /** * Get the server URL for HTTP client connections */ getServerUrl(port?: number): string { const actualPort = port || this.status?.port || DEFAULT_CONFIG.port; return `http://127.0.0.1:${actualPort}`; } /** * Check if server is available (cached status) */ isRunning(): boolean { return this.status?.running ?? false; } /** * Clear cached status (force refresh on next call) */ clearCache(): void { this.status = null; } } // === Singleton === let _instance: VikingServerManager | null = null; export function getVikingServerManager(): VikingServerManager { if (!_instance) { _instance = new VikingServerManager(); } return _instance; } export function resetVikingServerManager(): void { _instance = null; }