125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
|
|
import axios from 'axios';
|
|||
|
|
import os from 'os';
|
|||
|
|
|
|||
|
|
export interface NodeTask {
|
|||
|
|
id: string;
|
|||
|
|
type: string;
|
|||
|
|
payload: any;
|
|||
|
|
shopId: string;
|
|||
|
|
traceId: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* [CORE_NODE_01] Win Node Agent
|
|||
|
|
* @description 运行在 Windows 环境下的自管代理,负责无 API 平台的执行动作
|
|||
|
|
*/
|
|||
|
|
export class NodeAgent {
|
|||
|
|
private hubUrl: string;
|
|||
|
|
private nodeId: string;
|
|||
|
|
private heartbeatInterval: number = 30000; // 30秒
|
|||
|
|
private pollInterval: number = 10000; // 10秒
|
|||
|
|
|
|||
|
|
constructor(hubUrl: string) {
|
|||
|
|
this.hubUrl = hubUrl;
|
|||
|
|
this.nodeId = `WIN-${os.hostname()}-${process.pid}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 启动代理
|
|||
|
|
*/
|
|||
|
|
async start() {
|
|||
|
|
console.log(`[Agent] Node ${this.nodeId} starting...`);
|
|||
|
|
|
|||
|
|
// 1. 注册节点
|
|||
|
|
await this.register();
|
|||
|
|
|
|||
|
|
// 2. 开启心跳
|
|||
|
|
const heartbeatTimer = setInterval(() => this.heartbeat(), this.heartbeatInterval);
|
|||
|
|
// @ts-ignore
|
|||
|
|
if (heartbeatTimer.unref) heartbeatTimer.unref();
|
|||
|
|
|
|||
|
|
// 3. 开启任务轮询
|
|||
|
|
const pollTimer = setInterval(() => this.pollTasks(), this.pollInterval);
|
|||
|
|
// @ts-ignore
|
|||
|
|
if (pollTimer.unref) pollTimer.unref();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async register() {
|
|||
|
|
try {
|
|||
|
|
console.log(`[Agent] Registering to Hub: ${this.hubUrl}`);
|
|||
|
|
await axios.post(`${this.hubUrl}/api/v1/nodes/register`, {
|
|||
|
|
nodeId: this.nodeId,
|
|||
|
|
version: '1.0.0',
|
|||
|
|
os: os.type(),
|
|||
|
|
hostname: os.hostname(),
|
|||
|
|
shops: [] // 实际应从本地配置加载
|
|||
|
|
}).catch(err => {
|
|||
|
|
// 如果 Hub 还没启动,忽略错误,仅打印
|
|||
|
|
console.warn(`[Agent] Registration failed (Hub might be offline): ${err.message}`);
|
|||
|
|
});
|
|||
|
|
} catch (err: any) {
|
|||
|
|
console.error(`[Agent] Registration error: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async heartbeat() {
|
|||
|
|
try {
|
|||
|
|
console.log(`[Agent] Heartbeat...`);
|
|||
|
|
await axios.post(`${this.hubUrl}/api/v1/nodes/heartbeat`, { nodeId: this.nodeId })
|
|||
|
|
.catch(() => {}); // 忽略心跳失败
|
|||
|
|
} catch (err: any) {
|
|||
|
|
console.error(`[Agent] Heartbeat error: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async pollTasks() {
|
|||
|
|
try {
|
|||
|
|
console.log(`[Agent] Polling for tasks...`);
|
|||
|
|
const response = await axios.get(`${this.hubUrl}/api/v1/nodes/pull-task?nodeId=${this.nodeId}`)
|
|||
|
|
.catch(() => ({ data: { task: null } }));
|
|||
|
|
|
|||
|
|
if (response.data && response.data.task) {
|
|||
|
|
await this.executeTask(response.data.task);
|
|||
|
|
}
|
|||
|
|
} catch (err: any) {
|
|||
|
|
console.error(`[Agent] Poll error: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async executeTask(task: NodeTask) {
|
|||
|
|
console.log(`[Agent] Executing task: ${task.id} (${task.type})`);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 模拟执行耗时
|
|||
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|||
|
|
|
|||
|
|
// 实际应启动 Playwright 浏览器执行动作
|
|||
|
|
console.log(`[Agent] Task ${task.id} executed successfully.`);
|
|||
|
|
|
|||
|
|
await this.reportReceipt({
|
|||
|
|
taskId: task.id,
|
|||
|
|
traceId: task.traceId,
|
|||
|
|
status: 'success',
|
|||
|
|
resultData: { platform: 'TikTok', action: 'PUBLISH' }
|
|||
|
|
});
|
|||
|
|
} catch (err: any) {
|
|||
|
|
console.error(`[Agent] Task ${task.id} execution failed: ${err.message}`);
|
|||
|
|
await this.reportReceipt({
|
|||
|
|
taskId: task.id,
|
|||
|
|
traceId: task.traceId,
|
|||
|
|
status: 'failed',
|
|||
|
|
errorMessage: err.message
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async reportReceipt(receipt: any) {
|
|||
|
|
try {
|
|||
|
|
console.log(`[Agent] Reporting receipt for Task: ${receipt.taskId}`);
|
|||
|
|
await axios.post(`${this.hubUrl}/api/v1/publish/receipt`, receipt);
|
|||
|
|
} catch (err: any) {
|
|||
|
|
console.error(`[Agent] Failed to report receipt: ${err.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|