mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 23:14:53 +09:00
Add the roles and their permissions for the user the control connection is connected as along with updates when these change. This enables the web interface to know which permissions it has available to it.
379 lines
9.1 KiB
React
379 lines
9.1 KiB
React
import { libErrors, libLink, libLogging, libPlugin } from "@clusterio/lib";
|
|
const { logger } = libLogging;
|
|
import packageJson from "../../package.json";
|
|
|
|
|
|
/**
|
|
* Connector for control connection to master server
|
|
* @private
|
|
*/
|
|
export class ControlConnector extends libLink.WebSocketClientConnector {
|
|
constructor(url, maxReconnectDelay) {
|
|
super(url, maxReconnectDelay);
|
|
this.token = null;
|
|
}
|
|
|
|
register() {
|
|
if (!this.token) {
|
|
throw new Error("Token not set");
|
|
}
|
|
|
|
logger.verbose("Connector | registering control");
|
|
this.sendHandshake("register_control", {
|
|
token: this.token,
|
|
agent: "Clusterio Web UI",
|
|
version: packageJson.version,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles running the control
|
|
*
|
|
* Connects to the master server over WebSocket and sends commands to it.
|
|
* @static
|
|
*/
|
|
export class Control extends libLink.Link {
|
|
constructor(connector, plugins) {
|
|
super("control", "master", connector);
|
|
libLink.attachAllMessages(this);
|
|
|
|
/**
|
|
* Mapping of plugin names to their instance for loaded plugins.
|
|
* @type {Map<string, module:lib/plugin.BaseWebPlugin>}
|
|
*/
|
|
this.plugins = plugins;
|
|
for (let plugin of plugins.values()) {
|
|
plugin.control = this;
|
|
libPlugin.attachPluginMessages(this, plugin);
|
|
}
|
|
|
|
/**
|
|
* Flag indicating the connection is in the process of logging out.
|
|
* @type {boolean}
|
|
*/
|
|
this.loggingOut = false;
|
|
|
|
/**
|
|
* Name of the account this control link is connected as.
|
|
* @type {?string}
|
|
*/
|
|
this.accountName = null;
|
|
|
|
/**
|
|
* Roles of the account this control link is connected as.
|
|
* @type {?Array<object>}
|
|
*/
|
|
this.accountRoles = null;
|
|
|
|
this.accountUpdateHandlers = [];
|
|
this.slaveUpdateHandlers = new Map();
|
|
this.instanceUpdateHandlers = new Map();
|
|
this.saveListUpdateHandlers = new Map();
|
|
this.instanceLogHandlers = new Map();
|
|
|
|
this.connector.on("connect", data => {
|
|
this.accountName = data.account.name;
|
|
this.accountRoles = data.account.roles;
|
|
this.emitAccountUpdate();
|
|
this.updateSlaveSubscriptions().catch(err => logger.error(
|
|
`Unexpected error updating slave subscriptions:\n${err.stack}`
|
|
));
|
|
this.updateInstanceSubscriptions().catch(err => logger.error(
|
|
`Unexpected error updating instance subscriptions:\n${err.stack}`
|
|
));
|
|
this.updateSaveListSubscriptions().catch(err => logger.error(
|
|
`Unexpected error updating save list subscriptions:\n${err.stack}`
|
|
));
|
|
this.updateLogSubscriptions().catch(err => logger.error(
|
|
`Unexpected error updating log subscriptions:\n${err.stack}`
|
|
));
|
|
});
|
|
|
|
this.connector.on("close", () => {
|
|
this.accountName = null;
|
|
this.accountRoles = null;
|
|
this.emitAccountUpdate();
|
|
});
|
|
|
|
for (let event of ["connect", "drop", "resume", "close"]) {
|
|
this.connector.on(event, () => {
|
|
for (let plugin of this.plugins.values()) {
|
|
plugin.onMasterConnectionEvent(event);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
async accountUpdateEventHandler(message) {
|
|
this.accountRoles = message.data.roles;
|
|
this.emitAccountUpdate();
|
|
}
|
|
|
|
emitAccountUpdate() {
|
|
for (let handler of this.accountUpdateHandlers) {
|
|
handler({
|
|
name: this.accountName,
|
|
roles: this.accountRoles,
|
|
});
|
|
}
|
|
}
|
|
|
|
onAccountUpdate(handler) {
|
|
this.accountUpdateHandlers.push(handler);
|
|
}
|
|
|
|
offAccountUpdate(handler) {
|
|
let index = this.accountUpdateHandlers.lastIndexOf(handler);
|
|
if (index === -1) {
|
|
throw new Error("Given handler is not registered for account update");
|
|
}
|
|
this.accountUpdateHandlers.splice(index, 1);
|
|
}
|
|
|
|
async slaveUpdateEventHandler(message) {
|
|
let handlers = [].concat(
|
|
this.slaveUpdateHandlers.get(null) || [],
|
|
this.slaveUpdateHandlers.get(message.data.id) || [],
|
|
);
|
|
for (let handler of handlers) {
|
|
handler(message.data);
|
|
}
|
|
}
|
|
|
|
async onSlaveUpdate(id, handler) {
|
|
if (id !== null && !Number.isInteger(id)) {
|
|
throw new Error("Invalid slave id");
|
|
}
|
|
|
|
let handlers = this.slaveUpdateHandlers.get(id);
|
|
if (!handlers) {
|
|
handlers = [];
|
|
this.slaveUpdateHandlers.set(id, handlers);
|
|
}
|
|
|
|
handlers.push(handler);
|
|
|
|
if (handlers.length === 1) {
|
|
await this.updateSlaveSubscriptions();
|
|
}
|
|
}
|
|
|
|
async offSlaveUpdate(id, handler) {
|
|
let handlers = this.slaveUpdateHandlers.get(id);
|
|
if (!handlers || !handlers.length) {
|
|
throw new Error(`No handlers for slave ${id} exists`);
|
|
}
|
|
|
|
let index = handlers.lastIndexOf(handler);
|
|
if (index === -1) {
|
|
throw new Error(`Given handler is not registered for slave ${id}`);
|
|
}
|
|
|
|
handlers.splice(index, 1);
|
|
if (!handlers.length) {
|
|
this.slaveUpdateHandlers.delete(id);
|
|
await this.updateSlaveSubscriptions();
|
|
}
|
|
}
|
|
|
|
async updateSlaveSubscriptions() {
|
|
if (!this.connector.connected) {
|
|
return;
|
|
}
|
|
|
|
await libLink.messages.setSlaveSubscriptions.send(this, {
|
|
all: this.slaveUpdateHandlers.has(null),
|
|
slave_ids: [...this.slaveUpdateHandlers.keys()].filter(e => e !== null),
|
|
});
|
|
}
|
|
|
|
async instanceUpdateEventHandler(message) {
|
|
let handlers = [].concat(
|
|
this.instanceUpdateHandlers.get(null) || [],
|
|
this.instanceUpdateHandlers.get(message.data.id) || [],
|
|
);
|
|
for (let handler of handlers) {
|
|
handler(message.data);
|
|
}
|
|
};
|
|
|
|
async onInstanceUpdate(id, handler) {
|
|
if (id !== null && !Number.isInteger(id)) {
|
|
throw new Error("Invalid instance id");
|
|
}
|
|
|
|
let handlers = this.instanceUpdateHandlers.get(id);
|
|
if (!handlers) {
|
|
handlers = [];
|
|
this.instanceUpdateHandlers.set(id, handlers);
|
|
}
|
|
|
|
handlers.push(handler);
|
|
|
|
if (handlers.length === 1) {
|
|
await this.updateInstanceSubscriptions();
|
|
}
|
|
}
|
|
|
|
async offInstanceUpdate(id, handler) {
|
|
let handlers = this.instanceUpdateHandlers.get(id);
|
|
if (!handlers || !handlers.length) {
|
|
throw new Error(`No handlers for instance ${id} exist`);
|
|
}
|
|
|
|
let index = handlers.lastIndexOf(handler);
|
|
if (index === -1) {
|
|
throw new Error(`Given handler is not registered for instance ${id}`);
|
|
}
|
|
|
|
handlers.splice(index, 1);
|
|
if (!handlers.length) {
|
|
this.instanceUpdateHandlers.delete(id);
|
|
await this.updateInstanceSubscriptions();
|
|
}
|
|
}
|
|
|
|
async updateInstanceSubscriptions() {
|
|
if (!this.connector.connected) {
|
|
return;
|
|
}
|
|
|
|
await libLink.messages.setInstanceSubscriptions.send(this, {
|
|
all: this.instanceUpdateHandlers.has(null),
|
|
instance_ids: [...this.instanceUpdateHandlers.keys()].filter(e => e !== null),
|
|
});
|
|
}
|
|
|
|
async saveListUpdateEventHandler(message) {
|
|
let handlers = this.saveListUpdateHandlers.get(message.data.instance_id);
|
|
for (let handler of handlers || []) {
|
|
handler(message.data);
|
|
}
|
|
};
|
|
|
|
async onSaveListUpdate(id, handler) {
|
|
if (!Number.isInteger(id)) {
|
|
throw new Error("Invalid instance id");
|
|
}
|
|
|
|
let handlers = this.saveListUpdateHandlers.get(id);
|
|
if (!handlers) {
|
|
handlers = [];
|
|
this.saveListUpdateHandlers.set(id, handlers);
|
|
}
|
|
|
|
handlers.push(handler);
|
|
|
|
if (handlers.length === 1) {
|
|
await this.updateSaveListSubscriptions();
|
|
}
|
|
}
|
|
|
|
async offSaveListUpdate(id, handler) {
|
|
let handlers = this.saveListUpdateHandlers.get(id);
|
|
if (!handlers) {
|
|
throw new Error(`No handlers for instance ${id} exists`);
|
|
}
|
|
|
|
let index = handlers.lastIndexOf(handler);
|
|
if (index === -1) {
|
|
throw new Error(`Given handler is not registered for instance ${id}`);
|
|
}
|
|
|
|
handlers.splice(index, 1);
|
|
if (!handlers.length) {
|
|
this.saveListUpdateHandlers.delete(id);
|
|
await this.updateSaveListSubscriptions();
|
|
}
|
|
}
|
|
|
|
async updateSaveListSubscriptions() {
|
|
if (!this.connector.connected) {
|
|
return;
|
|
}
|
|
|
|
await libLink.messages.setSaveListSubscriptions.send(this, {
|
|
all: false,
|
|
instance_ids: [...this.saveListUpdateHandlers.keys()],
|
|
});
|
|
}
|
|
|
|
async logMessageEventHandler(message) {
|
|
let info = message.data.info;
|
|
|
|
if (info.instance_id !== undefined) {
|
|
let instanceHandlers = this.instanceLogHandlers.get(info.instance_id);
|
|
for (let handler of instanceHandlers || []) {
|
|
handler(info);
|
|
}
|
|
}
|
|
}
|
|
|
|
async onInstanceLog(id, handler) {
|
|
if (!Number.isInteger(id)) {
|
|
throw new Error("Invalid instance id");
|
|
}
|
|
|
|
let handlers = this.instanceLogHandlers.get(id);
|
|
if (!handlers) {
|
|
handlers = [];
|
|
this.instanceLogHandlers.set(id, handlers);
|
|
}
|
|
|
|
handlers.push(handler);
|
|
|
|
if (handlers.length === 1) {
|
|
await this.updateLogSubscriptions();
|
|
}
|
|
}
|
|
|
|
async offInstanceLog(id, handler) {
|
|
let handlers = this.instanceLogHandlers.get(id);
|
|
if (!handlers || !handlers.length) {
|
|
throw new Error(`No handlers for instance ${id} exist`);
|
|
}
|
|
|
|
let index = handlers.lastIndexOf(handler);
|
|
if (index === -1) {
|
|
throw new Error(`Given handler is not registered for instance ${id}`);
|
|
}
|
|
|
|
handlers.splice(index, 1);
|
|
if (!handlers.length) {
|
|
await this.updateLogSubscriptions();
|
|
}
|
|
}
|
|
|
|
async updateLogSubscriptions() {
|
|
if (!this.connector.connected) {
|
|
return;
|
|
}
|
|
|
|
await libLink.messages.setLogSubscriptions.send(this, {
|
|
all: false,
|
|
master: false,
|
|
slave_ids: [],
|
|
instance_ids: [...this.instanceLogHandlers.keys()],
|
|
max_level: null,
|
|
});
|
|
}
|
|
|
|
async debugWsMessageEventHandler(message) {
|
|
// eslint-disable-next-line no-console
|
|
console.log("WS", message.data.direction, message.data.content);
|
|
}
|
|
|
|
async shutdown() {
|
|
try {
|
|
await libLink.messages.prepareDisconnect.send(this);
|
|
} catch (err) {
|
|
if (!(err instanceof libErrors.SessionLost)) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
await this.connector.close(1000, "Control Quit");
|
|
}
|
|
}
|