2023-12-20 15:29:24 +00:00
|
|
|
import { DockgeSocket } from "./util-server";
|
|
|
|
import { io, Socket as SocketClient } from "socket.io-client";
|
|
|
|
import { log } from "./log";
|
|
|
|
import { Agent } from "./models/agent";
|
2023-12-24 14:01:17 +00:00
|
|
|
import { isDev, LooseObject } from "../common/util-common";
|
|
|
|
import semver from "semver";
|
|
|
|
import { R } from "redbean-node";
|
2023-12-20 15:29:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Dockge Instance Manager
|
|
|
|
*/
|
|
|
|
export class AgentManager {
|
|
|
|
|
|
|
|
protected socket : DockgeSocket;
|
|
|
|
protected instanceSocketList : Record<string, SocketClient> = {};
|
|
|
|
|
|
|
|
constructor(socket: DockgeSocket) {
|
|
|
|
this.socket = socket;
|
|
|
|
}
|
|
|
|
|
2023-12-24 14:01:17 +00:00
|
|
|
test(url : string, username : string, password : string) : Promise<void> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let obj = new URL(url);
|
|
|
|
let endpoint = obj.host;
|
|
|
|
|
|
|
|
if (!endpoint) {
|
|
|
|
reject(new Error("Invalid Dockge URL"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.instanceSocketList[endpoint]) {
|
|
|
|
reject(new Error("The Dockge URL already exists"));
|
|
|
|
}
|
|
|
|
|
|
|
|
let client = io(url, {
|
|
|
|
reconnection: false,
|
|
|
|
extraHeaders: {
|
|
|
|
endpoint,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on("connect", () => {
|
|
|
|
client.emit("login", {
|
|
|
|
username: username,
|
|
|
|
password: password,
|
|
|
|
}, (res : LooseObject) => {
|
|
|
|
if (res.ok) {
|
|
|
|
resolve();
|
|
|
|
} else {
|
|
|
|
reject(new Error(res.msg));
|
|
|
|
}
|
|
|
|
client.disconnect();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on("connect_error", (err) => {
|
|
|
|
if (err.message === "xhr poll error") {
|
|
|
|
reject(new Error("Unable to connect to the Dockge instance"));
|
|
|
|
} else {
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
client.disconnect();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param url
|
|
|
|
* @param username
|
|
|
|
* @param password
|
|
|
|
*/
|
|
|
|
async add(url : string, username : string, password : string) : Promise<Agent> {
|
|
|
|
let bean = R.dispense("agent") as Agent;
|
|
|
|
bean.url = url;
|
|
|
|
bean.username = username;
|
|
|
|
bean.password = password;
|
|
|
|
await R.store(bean);
|
|
|
|
return bean;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param endpoint
|
|
|
|
*/
|
|
|
|
remove(endpoint : string) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-12-20 15:29:24 +00:00
|
|
|
connect(url : string, username : string, password : string) {
|
|
|
|
let obj = new URL(url);
|
|
|
|
let endpoint = obj.host;
|
|
|
|
|
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "connecting",
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!endpoint) {
|
|
|
|
log.error("agent-manager", "Invalid endpoint: " + endpoint + " URL: " + url);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.instanceSocketList[endpoint]) {
|
|
|
|
log.debug("agent-manager", "Already connected to the socket server: " + endpoint);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
log.info("agent-manager", "Connecting to the socket server: " + endpoint);
|
|
|
|
let client = io(url, {
|
|
|
|
extraHeaders: {
|
|
|
|
endpoint,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on("connect", () => {
|
|
|
|
log.info("agent-manager", "Connected to the socket server: " + endpoint);
|
|
|
|
|
|
|
|
client.emit("login", {
|
|
|
|
username: username,
|
|
|
|
password: password,
|
2023-12-24 14:01:17 +00:00
|
|
|
}, (res : LooseObject) => {
|
2023-12-20 15:29:24 +00:00
|
|
|
if (res.ok) {
|
|
|
|
log.info("agent-manager", "Logged in to the socket server: " + endpoint);
|
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "online",
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
log.error("agent-manager", "Failed to login to the socket server: " + endpoint);
|
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "offline",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-12-24 14:01:17 +00:00
|
|
|
client.on("connect_error", (err) => {
|
2023-12-20 15:29:24 +00:00
|
|
|
log.error("agent-manager", "Error from the socket server: " + endpoint);
|
2023-12-24 14:01:17 +00:00
|
|
|
log.debug("agent-manager", err);
|
2023-12-20 15:29:24 +00:00
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "offline",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on("disconnect", () => {
|
|
|
|
log.info("agent-manager", "Disconnected from the socket server: " + endpoint);
|
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "offline",
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on("agent", (...args : unknown[]) => {
|
|
|
|
log.debug("agent-manager", "Forward event");
|
|
|
|
this.socket.emit("agent", ...args);
|
|
|
|
});
|
|
|
|
|
2023-12-24 14:01:17 +00:00
|
|
|
client.on("info", (res) => {
|
|
|
|
log.debug("agent-manager", res);
|
|
|
|
|
|
|
|
// Disconnect if the version is lower than 1.4.0
|
|
|
|
if (!isDev && semver.satisfies(res.version, "< 1.4.0")) {
|
|
|
|
this.socket.emit("agentStatus", {
|
|
|
|
endpoint: endpoint,
|
|
|
|
status: "offline",
|
|
|
|
msg: `${endpoint}: Unsupported version: ` + res.version,
|
|
|
|
});
|
|
|
|
client.disconnect();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-12-20 15:29:24 +00:00
|
|
|
this.instanceSocketList[endpoint] = client;
|
|
|
|
}
|
|
|
|
|
|
|
|
disconnect(endpoint : string) {
|
|
|
|
let client = this.instanceSocketList[endpoint];
|
|
|
|
client?.disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
async connectAll() {
|
|
|
|
if (this.socket.endpoint) {
|
|
|
|
log.info("agent-manager", "This connection is connected as an agent, skip connectAll()");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let list : Record<string, Agent> = await Agent.getAgentList();
|
|
|
|
|
|
|
|
if (Object.keys(list).length !== 0) {
|
|
|
|
log.info("agent-manager", "Connecting to all instance socket server(s)...");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let endpoint in list) {
|
|
|
|
let agent = list[endpoint];
|
|
|
|
this.connect(agent.url, agent.username, agent.password);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
disconnectAll() {
|
|
|
|
for (let endpoint in this.instanceSocketList) {
|
|
|
|
this.disconnect(endpoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emitToEndpoint(endpoint: string, eventName: string, ...args : unknown[]) {
|
|
|
|
log.debug("agent-manager", "Emitting event to endpoint: " + endpoint);
|
|
|
|
let client = this.instanceSocketList[endpoint];
|
|
|
|
if (!client) {
|
|
|
|
log.error("agent-manager", "Socket client not found for endpoint: " + endpoint);
|
|
|
|
throw new Error("Socket client not found for endpoint: " + endpoint);
|
|
|
|
}
|
|
|
|
client?.emit("agent", endpoint, eventName, ...args);
|
|
|
|
}
|
|
|
|
|
|
|
|
emitToAllEndpoints(eventName: string, ...args : unknown[]) {
|
|
|
|
log.debug("agent-manager", "Emitting event to all endpoints");
|
|
|
|
for (let endpoint in this.instanceSocketList) {
|
|
|
|
this.emitToEndpoint(endpoint, eventName, ...args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async sendAgentList() {
|
|
|
|
let list = await Agent.getAgentList();
|
|
|
|
let result : Record<string, LooseObject> = {};
|
2023-12-21 12:47:47 +00:00
|
|
|
|
|
|
|
// Myself
|
|
|
|
result[""] = {
|
|
|
|
url: "",
|
|
|
|
username: "",
|
|
|
|
endpoint: "",
|
|
|
|
};
|
|
|
|
|
2023-12-20 15:29:24 +00:00
|
|
|
for (let endpoint in list) {
|
|
|
|
let agent = list[endpoint];
|
|
|
|
result[endpoint] = agent.toJSON();
|
|
|
|
}
|
2023-12-21 12:47:47 +00:00
|
|
|
|
2023-12-20 15:29:24 +00:00
|
|
|
this.socket.emit("agentList", {
|
|
|
|
ok: true,
|
|
|
|
agentList: result,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|