// services/meshcentral.service.ts export interface MeshCentralConfig { serverUrl: string; username: string; password: string; domain?: string; } export interface DeviceGroup { _id: string; name: string; desc: string; mtype: number; // 1: FreeAgent, 2: IntelAMT, 3: Mixed } export interface Device { _id: string; name: string; meshid: string; host: string; state: string; // online, offline, unknown rname?: string; // remote name } export class MeshCentralService { private ws: WebSocket | null = null; private messageId = 1; private pendingRequests = new Map void; reject: (error: any) => void; timeout: ReturnType; }>(); private messageHandlers = new Map void>(); constructor(private config: MeshCentralConfig) {} /** * Kết nối đến MeshCentral Server */ async connect(): Promise { return new Promise((resolve, reject) => { try { const wsUrl = `${this.config.serverUrl.replace(/^http/, 'ws')}/control.ashx`; this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { console.log('Connected to MeshCentral'); this.authenticate().then(resolve).catch(reject); }; this.ws.onmessage = (event) => this.handleMessage(event.data); this.ws.onerror = (error) => { console.error('WebSocket error:', error); reject(error); }; this.ws.onclose = () => { console.log('Disconnected from MeshCentral'); this.ws = null; }; } catch (error) { reject(error); } }); } /** * Xác thực với server */ private async authenticate(): Promise { const authMessage = { action: 'authCookie', username: this.config.username, password: this.config.password, domain: this.config.domain || '' }; return this.sendMessage(authMessage); } /** * Gửi message và chờ response */ private async sendMessage(message: any): Promise { return new Promise((resolve, reject) => { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { reject(new Error('WebSocket not connected')); return; } const msgId = this.messageId++; message.sessionid = msgId; const timeout = setTimeout(() => { this.pendingRequests.delete(msgId); reject(new Error('Request timeout')); }, 30000); // 30 seconds timeout this.pendingRequests.set(msgId, { resolve, reject, timeout }); try { this.ws.send(JSON.stringify(message)); } catch (error) { this.pendingRequests.delete(msgId); clearTimeout(timeout); reject(error); } }); } /** * Xử lý incoming messages */ private handleMessage(data: string): void { try { const message = JSON.parse(data); // Handle response to pending request if (message.sessionid && this.pendingRequests.has(message.sessionid)) { const { resolve, reject, timeout } = this.pendingRequests.get(message.sessionid)!; this.pendingRequests.delete(message.sessionid); clearTimeout(timeout); if (message.error) { reject(new Error(message.error)); } else { resolve(message); } } // Handle event handlers if (message.action && this.messageHandlers.has(message.action)) { const handler = this.messageHandlers.get(message.action)!; handler(message); } } catch (error) { console.error('Error parsing message:', error); } } /** * Đăng ký handler cho event */ on(action: string, handler: (data: any) => void): void { this.messageHandlers.set(action, handler); } /** * === TẠIDEVICE GROUP === * Tạo một device group (Mesh) mới */ async createDeviceGroup(name: string, desc: string = ''): Promise { const response = await this.sendMessage({ action: 'createmesh', meshname: name, meshdesc: desc, meshtype: 2 // 2 = Window }); if (response.error) { throw new Error(`Failed to create device group: ${response.error}`); } return response.meshid; } /** * Lấy danh sách device groups */ async getDeviceGroups(): Promise { const response = await this.sendMessage({ action: 'meshes' }); return response.meshes || []; } /** * === THÊM DEVICE === * Lấy Agent invite link để install trên device */ async getAgentInviteLink(meshId: string, platform: string = 'linux'): Promise { const response = await this.sendMessage({ action: 'getmesh', meshid: meshId }); // Agent download URL format: // /meshagents?id=&installflags=&exeType= const flags = 0; // Windows install flags return `/meshagents?id=4&meshid=${meshId}&installflags=${flags}`; } /** * Lấy danh sách devices trong group */ async getDevices(meshId: string): Promise { const response = await this.sendMessage({ action: 'getmesh', meshid: meshId }); return response.nodes || []; } /** * === QUẢN LÝ DEVICE === * Vô hiệu hóa/kích hoạt device */ async setDeviceState(deviceId: string, enabled: boolean): Promise { await this.sendMessage({ action: 'changedevice', nodeid: deviceId, enabled: enabled }); } /** * Xóa device khỏi group */ async removeDevice(deviceId: string): Promise { await this.sendMessage({ action: 'removenode', nodeids: [deviceId] }); } /** * === QUẢN LÝ NGƯỜI DÙNG === * Tạo user mới */ async createUser(username: string, password: string, email?: string): Promise { const response = await this.sendMessage({ action: 'createuser', username: username, password: password, email: email || '' }); return response; } /** * Gán quyền truy cập device group cho user * rights: bitmask của permissions * - 1: Edit mesh * - 2: Manage users * - 4: Manage computers * - 8: Remote control * - 16: Agent console * - 32: Server files * - 64: Wake device * - 128: Set notes * - 256: Remote view only */ async addUserToMesh(meshId: string, username: string, rights: number): Promise { await this.sendMessage({ action: 'editmesh', meshid: meshId, usernames: [username], rights: rights }); } /** * === ĐIỀU KHIỂN THIẾT BỊ === * Gửi command đến device */ async sendDeviceCommand(deviceId: string, command: string, parameters?: any): Promise { return this.sendMessage({ action: 'runcommand', nodeid: deviceId, command: command, params: parameters || {} }); } /** * Khởi động lại device */ async rebootDevice(deviceId: string): Promise { await this.sendMessage({ action: 'poweraction', nodeid: deviceId, actiontype: 1 // 1 = reboot }); } /** * Tắt device */ async shutdownDevice(deviceId: string): Promise { await this.sendMessage({ action: 'poweraction', nodeid: deviceId, actiontype: 2 // 2 = shutdown }); } /** * Disconnect user session từ MeshCentral */ disconnect(): void { if (this.ws) { this.ws.close(); this.ws = null; } this.pendingRequests.clear(); this.messageHandlers.clear(); } }