/** * ServiceManager - 服务管理类 * 支持:设置路径、连接、状态查询、启动、停止 * DEBUG模式:开启后在浏览器控制台输出详细调试信息 */ export interface ServiceInfo { id: string; name: string; exe_path: string; running: boolean; pid: number | null; port: number | null; port_open: boolean | null; status: 'running' | 'stopped'; } export interface ApiResponse { code: number; data?: T; msg?: string; pid?: number; killed?: number[]; } export type LogLevel = 'info' | 'success' | 'error' | 'warn' | 'debug'; export interface LogEntry { time: string; level: LogLevel; message: string; data?: any; } export type StatusChangeCallback = (service: ServiceInfo) => void; export class ServiceManager { // 调试开关 private static DEBUG: boolean = false; // 实例属性 private baseUrl: string = 'http://127.0.0.1:5000'; private logs: LogEntry[] = []; private maxLogs: number = 200; private pollInterval: number = 3000; private pollTimer: ReturnType | null = null; private onStatusChange: StatusChangeCallback | null = null; private previousStatus: Map = new Map(); private connected: boolean = false; constructor(baseUrl?: string) { if (baseUrl) { this.baseUrl = baseUrl; } this.log('info', `ServiceManager 创建 | API: ${this.baseUrl} | DEBUG: ${ServiceManager.DEBUG}`); } // ============================================ // DEBUG 控制 // ============================================ /** * 开启DEBUG模式 - 在浏览器控制台输出详细信息 */ static enableDebug(): void { ServiceManager.DEBUG = true; console.log('%c[DEBUG]%c 全局DEBUG模式已开启', 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px', 'color:#9b59b6'); } /** * 关闭DEBUG模式 */ static disableDebug(): void { ServiceManager.DEBUG = false; console.log('%c[DEBUG]%c 全局DEBUG模式已关闭', 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px', 'color:#9b59b6'); } /** * 获取当前DEBUG状态 */ static isDebugEnabled(): boolean { return ServiceManager.DEBUG; } // ============================================ // 内部日志 // ============================================ private log(level: LogLevel, message: string, data?: any): LogEntry { const entry: LogEntry = { time: new Date().toLocaleTimeString(), level, message, data }; this.logs.push(entry); if (this.logs.length > this.maxLogs) { this.logs.shift(); } // 控制台输出(DEBUG模式下更详细) const timestamp = new Date().toLocaleTimeString(); const prefix = `[${level.toUpperCase()}]`; if (ServiceManager.DEBUG) { const style = this.getConsoleStyle(level); console.log(`%c${timestamp}%c ${prefix}%c ${message}`, 'color:#888', style, 'color:inherit', data || ''); if (data !== undefined) { console.log(`%c${timestamp}%c [DATA]`, 'color:#888', 'color:#3498db', data); } } else if (level === 'error' || level === 'warn') { const style = this.getConsoleStyle(level); console.log(`%c${prefix}%c ${message}`, style, 'color:inherit'); } return entry; } private getConsoleStyle(level: LogLevel): string { switch (level) { case 'error': return 'background:#e74c3c;color:#fff;padding:2px 6px;border-radius:3px'; case 'warn': return 'background:#f39c12;color:#fff;padding:2px 6px;border-radius:3px'; case 'success': return 'background:#27ae60;color:#fff;padding:2px 6px;border-radius:3px'; case 'info': return 'background:#3498db;color:#fff;padding:2px 6px;border-radius:3px'; case 'debug': return 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px'; default: return 'background:#95a5a6;color:#fff;padding:2px 6px;border-radius:3px'; } } // ============================================ // 配置方法 // ============================================ /** * 设置API地址 * @param url API服务器地址,如 http://127.0.0.1:5000 */ setBaseUrl(url: string): void { this.log('info', `API地址设置: ${this.baseUrl} → ${url}`); this.baseUrl = url; } /** * 获取当前API地址 */ getBaseUrl(): string { return this.baseUrl; } /** * 设置轮询间隔(毫秒) * @param ms 间隔时间,默认3000ms */ setPollInterval(ms: number): void { this.log('info', `轮询间隔设置: ${this.pollInterval}ms → ${ms}ms`); this.pollInterval = ms; if (this.pollTimer) { this.stopAutoRefresh(); this.startAutoRefresh(); } } /** * 获取轮询间隔 */ getPollInterval(): number { return this.pollInterval; } // ============================================ // 状态查询 // ============================================ /** * 获取连接状态 */ isConnected(): boolean { return this.connected; } /** * 获取所有服务状态 */ async getServicesStatus(): Promise { this.log('debug', `GET /api/services`); try { const response = await fetch(`${this.baseUrl}/api/services`); this.log('debug', `响应状态: ${response.status} ${response.statusText}`); if (!response.ok) { this.log('error', `HTTP错误: ${response.status}`); this.connected = false; return []; } const result: ApiResponse = await response.json(); this.log('debug', `响应数据: ${JSON.stringify(result)}`); if (result.code === 0 && result.data) { this.connected = true; // 检查状态变化 for (const service of result.data) { const prev = this.previousStatus.get(service.id); if (prev !== undefined && prev !== service.running) { this.log('warn', `状态变化: ${service.name} → ${service.running ? '运行中' : '已停止'}`, { id: service.id, from: prev, to: service.running }); if (this.onStatusChange) { this.onStatusChange(service); } } this.previousStatus.set(service.id, service.running); } this.log('success', `获取服务状态成功: ${result.data.length} 个服务`); return result.data; } this.log('error', `API返回错误: ${result.msg || '未知错误'}`); return []; } catch (error: any) { this.log('error', `请求失败: ${error.message}`, { error: error.toString(), stack: error.stack }); this.connected = false; return []; } } /** * 获取单个服务状态 * @param id 服务ID */ async getServiceStatus(id: string): Promise { this.log('debug', `GET /api/services/${id}`); try { const response = await fetch(`${this.baseUrl}/api/services/${id}`); this.log('debug', `响应状态: ${response.status}`); if (!response.ok) { this.log('error', `HTTP错误: ${response.status}`); return null; } const result: ApiResponse = await response.json(); this.log('debug', `响应数据: ${JSON.stringify(result)}`); if (result.code === 0 && result.data) { this.log('success', `获取服务 ${id} 状态成功`, result.data); return result.data; } this.log('error', `获取失败: ${result.msg}`); return null; } catch (error: any) { this.log('error', `请求失败: ${error.message}`); return null; } } // ============================================ // 服务控制 // ============================================ /** * 启动服务 * @param id 服务ID */ async startService(id: string): Promise { this.log('info', `启动服务: ${id}`); this.log('debug', `POST /api/services/${id}/start`); try { const response = await fetch(`${this.baseUrl}/api/services/${id}/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); this.log('debug', `响应状态: ${response.status}`); const result: ApiResponse = await response.json(); this.log('debug', `响应数据: ${JSON.stringify(result)}`); if (result.code === 0) { this.log('success', `启动成功: ${result.msg}`, { pid: result.pid }); return true; } this.log('error', `启动失败: ${result.msg}`); return false; } catch (error: any) { this.log('error', `启动请求失败: ${error.message}`); return false; } } /** * 停止服务 * @param id 服务ID */ async stopService(id: string): Promise { this.log('info', `停止服务: ${id}`); this.log('debug', `POST /api/services/${id}/stop`); try { const response = await fetch(`${this.baseUrl}/api/services/${id}/stop`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); this.log('debug', `响应状态: ${response.status}`); const result: ApiResponse = await response.json(); this.log('debug', `响应数据: ${JSON.stringify(result)}`); if (result.code === 0) { this.log('success', `停止成功: ${result.msg}`, { killed: result.killed }); return true; } this.log('error', `停止失败: ${result.msg}`); return false; } catch (error: any) { this.log('error', `停止请求失败: ${error.message}`); return false; } } // ============================================ // 自动刷新 // ============================================ /** * 启动自动刷新 * @param callback 每次刷新后的回调函数 */ startAutoRefresh(callback?: (services: ServiceInfo[]) => void): void { if (this.pollTimer) { this.log('warn', '自动刷新已在运行中'); return; } this.log('info', `启动自动刷新,间隔: ${this.pollInterval}ms`); const doRefresh = async () => { this.log('debug', '--- 自动刷新触发 ---'); const services = await this.getServicesStatus(); if (callback) { callback(services); } }; doRefresh(); this.pollTimer = setInterval(doRefresh, this.pollInterval); this.log('success', '自动刷新已开始'); } /** * 停止自动刷新 */ stopAutoRefresh(): void { if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null; this.log('info', '自动刷新已停止'); } else { this.log('warn', '自动刷新未在运行'); } } /** * 检查自动刷新是否运行中 */ isAutoRefreshRunning(): boolean { return this.pollTimer !== null; } // ============================================ // 状态变化回调 // ============================================ /** * 设置状态变化回调 * @param callback 服务状态变化时调用 */ setOnStatusChange(callback: StatusChangeCallback): void { this.onStatusChange = callback; this.log('info', '状态变化回调已设置'); } // ============================================ // 日志管理 // ============================================ /** * 获取所有日志 */ getLogs(): LogEntry[] { return [...this.logs]; } /** * 清空日志 */ clearLogs(): void { this.logs = []; this.log('info', '日志已清空'); } // ============================================ // 销毁 // ============================================ /** * 销毁实例,停止所有定时器 */ destroy(): void { this.log('info', '销毁 ServiceManager 实例'); this.stopAutoRefresh(); this.logs = []; this.connected = false; } }