338 lines
12 KiB
JavaScript
338 lines
12 KiB
JavaScript
/**
|
||
* ServiceManager - 服务管理类
|
||
* 支持:设置路径、连接、状态查询、启动、停止
|
||
* DEBUG模式:开启后在浏览器控制台输出详细调试信息
|
||
*/
|
||
export class ServiceManager {
|
||
constructor(baseUrl) {
|
||
// 实例属性
|
||
this.baseUrl = 'http://127.0.0.1:5000';
|
||
this.logs = [];
|
||
this.maxLogs = 200;
|
||
this.pollInterval = 3000;
|
||
this.pollTimer = null;
|
||
this.onStatusChange = null;
|
||
this.previousStatus = new Map();
|
||
this.connected = false;
|
||
if (baseUrl) {
|
||
this.baseUrl = baseUrl;
|
||
}
|
||
this.log('info', `ServiceManager 创建 | API: ${this.baseUrl} | DEBUG: ${ServiceManager.DEBUG}`);
|
||
}
|
||
// ============================================
|
||
// DEBUG 控制
|
||
// ============================================
|
||
/**
|
||
* 开启DEBUG模式 - 在浏览器控制台输出详细信息
|
||
*/
|
||
static enableDebug() {
|
||
ServiceManager.DEBUG = true;
|
||
console.log('%c[DEBUG]%c 全局DEBUG模式已开启', 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px', 'color:#9b59b6');
|
||
}
|
||
/**
|
||
* 关闭DEBUG模式
|
||
*/
|
||
static disableDebug() {
|
||
ServiceManager.DEBUG = false;
|
||
console.log('%c[DEBUG]%c 全局DEBUG模式已关闭', 'background:#9b59b6;color:#fff;padding:2px 6px;border-radius:3px', 'color:#9b59b6');
|
||
}
|
||
/**
|
||
* 获取当前DEBUG状态
|
||
*/
|
||
static isDebugEnabled() {
|
||
return ServiceManager.DEBUG;
|
||
}
|
||
// ============================================
|
||
// 内部日志
|
||
// ============================================
|
||
log(level, message, data) {
|
||
const entry = {
|
||
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;
|
||
}
|
||
getConsoleStyle(level) {
|
||
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) {
|
||
this.log('info', `API地址设置: ${this.baseUrl} → ${url}`);
|
||
this.baseUrl = url;
|
||
}
|
||
/**
|
||
* 获取当前API地址
|
||
*/
|
||
getBaseUrl() {
|
||
return this.baseUrl;
|
||
}
|
||
/**
|
||
* 设置轮询间隔(毫秒)
|
||
* @param ms 间隔时间,默认3000ms
|
||
*/
|
||
setPollInterval(ms) {
|
||
this.log('info', `轮询间隔设置: ${this.pollInterval}ms → ${ms}ms`);
|
||
this.pollInterval = ms;
|
||
if (this.pollTimer) {
|
||
this.stopAutoRefresh();
|
||
this.startAutoRefresh();
|
||
}
|
||
}
|
||
/**
|
||
* 获取轮询间隔
|
||
*/
|
||
getPollInterval() {
|
||
return this.pollInterval;
|
||
}
|
||
// ============================================
|
||
// 状态查询
|
||
// ============================================
|
||
/**
|
||
* 获取连接状态
|
||
*/
|
||
isConnected() {
|
||
return this.connected;
|
||
}
|
||
/**
|
||
* 获取所有服务状态
|
||
*/
|
||
async getServicesStatus() {
|
||
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 = 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) {
|
||
this.log('error', `请求失败: ${error.message}`, { error: error.toString(), stack: error.stack });
|
||
this.connected = false;
|
||
return [];
|
||
}
|
||
}
|
||
/**
|
||
* 获取单个服务状态
|
||
* @param id 服务ID
|
||
*/
|
||
async getServiceStatus(id) {
|
||
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 = 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) {
|
||
this.log('error', `请求失败: ${error.message}`);
|
||
return null;
|
||
}
|
||
}
|
||
// ============================================
|
||
// 服务控制
|
||
// ============================================
|
||
/**
|
||
* 启动服务
|
||
* @param id 服务ID
|
||
*/
|
||
async startService(id) {
|
||
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 = 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) {
|
||
this.log('error', `启动请求失败: ${error.message}`);
|
||
return false;
|
||
}
|
||
}
|
||
/**
|
||
* 停止服务
|
||
* @param id 服务ID
|
||
*/
|
||
async stopService(id) {
|
||
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 = 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) {
|
||
this.log('error', `停止请求失败: ${error.message}`);
|
||
return false;
|
||
}
|
||
}
|
||
// ============================================
|
||
// 自动刷新
|
||
// ============================================
|
||
/**
|
||
* 启动自动刷新
|
||
* @param callback 每次刷新后的回调函数
|
||
*/
|
||
startAutoRefresh(callback) {
|
||
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() {
|
||
if (this.pollTimer) {
|
||
clearInterval(this.pollTimer);
|
||
this.pollTimer = null;
|
||
this.log('info', '自动刷新已停止');
|
||
}
|
||
else {
|
||
this.log('warn', '自动刷新未在运行');
|
||
}
|
||
}
|
||
/**
|
||
* 检查自动刷新是否运行中
|
||
*/
|
||
isAutoRefreshRunning() {
|
||
return this.pollTimer !== null;
|
||
}
|
||
// ============================================
|
||
// 状态变化回调
|
||
// ============================================
|
||
/**
|
||
* 设置状态变化回调
|
||
* @param callback 服务状态变化时调用
|
||
*/
|
||
setOnStatusChange(callback) {
|
||
this.onStatusChange = callback;
|
||
this.log('info', '状态变化回调已设置');
|
||
}
|
||
// ============================================
|
||
// 日志管理
|
||
// ============================================
|
||
/**
|
||
* 获取所有日志
|
||
*/
|
||
getLogs() {
|
||
return [...this.logs];
|
||
}
|
||
/**
|
||
* 清空日志
|
||
*/
|
||
clearLogs() {
|
||
this.logs = [];
|
||
this.log('info', '日志已清空');
|
||
}
|
||
// ============================================
|
||
// 销毁
|
||
// ============================================
|
||
/**
|
||
* 销毁实例,停止所有定时器
|
||
*/
|
||
destroy() {
|
||
this.log('info', '销毁 ServiceManager 实例');
|
||
this.stopAutoRefresh();
|
||
this.logs = [];
|
||
this.connected = false;
|
||
}
|
||
}
|
||
// 调试开关
|
||
ServiceManager.DEBUG = false;
|