Merge pull request #527 from Hornwitser/autosave-data

Implement autosaving of data held in memory
This commit is contained in:
Hornwitser authored and GitHub committed 2023-12-01 02:30:54 +01:00
commit f802e6ba67
22 files changed
+366 -138

No files matched your search

+2 -2
View File
@@ -50,7 +50,7 @@ export default class BaseConnection extends lib.Link {
async handleModPackGetRequest(request: lib.ModPackGetRequest): Promise<lib.ModPack> {
let { id } = request;
let modPack = this._controller.modPacks!.get(id);
let modPack = this._controller.modPacks.get(id);
if (!modPack) {
throw new lib.RequestError(`Mod pack with ID ${id} does not exist`);
}
@@ -62,7 +62,7 @@ export default class BaseConnection extends lib.Link {
if (id === null) {
throw new lib.RequestError("Default mod pack not set on controller");
}
let modPack = this._controller.modPacks!.get(id);
let modPack = this._controller.modPacks.get(id);
if (!modPack) {
throw new lib.RequestError(`Default mod pack configured (${id}) does not exist`);
}
@@ -40,6 +40,19 @@ export default class BaseControllerPlugin {
*/
async init() { }
/**
* Called when the controller saves data in memory to disk
*
* Invoked on the configured controller.autosave_interval by the
* controller and intended to be used to flush any in memory data that
* has changed to disk.
*
* This will also be called during graceful shutdown after {@link
* BaseControllerPlugin.onShutdown} have been invoked and all links have
* been disconnected.
*/
async onSaveData() { }
/**
* Called when the status of an instance changes
*
+16 -14
View File
@@ -185,7 +185,7 @@ export default class ControlConnection extends BaseConnection {
}
async handleHostRevokeTokensRequest(request: lib.HostRevokeTokensRequest) {
const host = this._controller.hosts?.get(request.hostId);
const host = this._controller.hosts.get(request.hostId);
if (!host) {
throw new Error(`Unknown host id (${request.hostId})`);
}
@@ -202,7 +202,7 @@ export default class ControlConnection extends BaseConnection {
async handleHostListRequest(): Promise<lib.HostDetails[]> {
let list = [];
for (let host of this._controller.hosts!.values()) {
for (let host of this._controller.hosts.values()) {
list.push(new lib.HostDetails(
host.agent,
host.version,
@@ -266,7 +266,7 @@ export default class ControlConnection extends BaseConnection {
async handleInstanceDetailsListRequest() {
let list = [];
for (let instance of this._controller.instances!.values()) {
for (let instance of this._controller.instances.values()) {
let assigned_host: number|null|undefined = instance.config.get("instance.assigned_host");
if (assigned_host === null) {
assigned_host = undefined;
@@ -424,7 +424,7 @@ export default class ControlConnection extends BaseConnection {
}
async handleModPackListRequest() {
return [...this._controller.modPacks!.values()];
return [...this._controller.modPacks.values()];
}
async handleModPackCreateRequest(request: lib.ModPackCreateRequest) {
@@ -432,37 +432,37 @@ export default class ControlConnection extends BaseConnection {
if (modPack.id === undefined) {
throw new lib.RequestError("Mod pack need an ID to be created");
}
if (this._controller.modPacks!.has(modPack.id)) {
if (this._controller.modPacks.has(modPack.id)) {
throw new lib.RequestError(`Mod pack with ID ${modPack.id} already exist`);
}
this._controller.modPacks!.set(modPack.id, modPack);
this._controller.modPacks.set(modPack.id, modPack);
this._controller.modPackUpdated(modPack);
}
async handleModPackUpdateRequest(request: lib.ModPackUpdateRequest) {
let modPack = request.modPack;
if (modPack.id === undefined || !this._controller.modPacks!.has(modPack.id)) {
if (modPack.id === undefined || !this._controller.modPacks.has(modPack.id)) {
throw new lib.RequestError(`Mod pack with ID ${modPack.id} does not exist`);
}
this._controller.modPacks!.set(modPack.id, modPack);
this._controller.modPacks.set(modPack.id, modPack);
this._controller.modPackUpdated(modPack);
}
async handleModPackDeleteRequest(request: lib.ModPackDeleteRequest) {
let { id } = request;
let modPack = this._controller.modPacks!.get(id);
let modPack = this._controller.modPacks.get(id);
if (!modPack) {
throw new lib.RequestError(`Mod pack with ID ${id} does not exist`);
}
modPack.isDeleted = true;
this._controller.modPacks!.delete(id);
this._controller.modPacks.delete(id);
this._controller.modPackUpdated(modPack);
}
async handleModGetRequest(request: lib.ModGetRequest) {
let { name, version } = request;
let filename = `${name}_${version}.zip`;
let mod = this._controller.mods!.get(filename);
let mod = this._controller.mods.get(filename);
if (!mod) {
throw new lib.RequestError(`Mod ${filename} does not exist`);
}
@@ -470,7 +470,7 @@ export default class ControlConnection extends BaseConnection {
}
async handleModListRequest() {
return [...this._controller.mods!.values()];
return [...this._controller.mods.values()];
}
static termsMatchesMod(terms: lib.ParsedTerm[], mod: lib.ModInfo) {
@@ -510,7 +510,7 @@ export default class ControlConnection extends BaseConnection {
type ModVersions = { name: string, versions: lib.ModInfo[] };
let results: Map<string, ModVersions> = new Map();
for (let mod of this._controller.mods!.values()) {
for (let mod of this._controller.mods.values()) {
if (
mod.factorioVersion !== factorioVersion
|| !ControlConnection.termsMatchesMod(query.terms, mod)
@@ -565,7 +565,7 @@ export default class ControlConnection extends BaseConnection {
async handleModDownloadRequest(request: lib.ModDownloadRequest) {
let { name, version } = request;
let filename = `${name}_${version}.zip`;
let mod = this._controller.mods!.get(filename);
let mod = this._controller.mods.get(filename);
if (!mod) {
throw new lib.RequestError(`Mod ${filename} does not exist`);
}
@@ -665,6 +665,7 @@ export default class ControlConnection extends BaseConnection {
// Start at 5 to leave space for future default roles
let id = Math.max(5, lastId+1);
this._controller.userManager.roles.set(id, new lib.Role({ id, ...request }));
this._controller.userManager.dirty = true;
return id;
}
@@ -699,6 +700,7 @@ export default class ControlConnection extends BaseConnection {
}
this._controller.userManager.roles.delete(id);
this._controller.userManager.dirty = true;
for (let user of this._controller.userManager.users.values()) {
user.roles.delete(role);
this._controller.userPermissionsUpdated(user);
+133 -61
View File
@@ -48,16 +48,12 @@ export default class Controller {
config: lib.ControllerConfig;
app: Application;
/** Mapping of host id to host info */
hosts: Map<number, HostInfo> | null = null;
/** Mapping of instance id to instance info */
instances: Map<number, InstanceInfo> | null = null;
/** Mapping of mod pack id to mod pack */
modPacks: Map<number, lib.ModPack> | null = null;
/** Mapping of mod names to mod infos */
mods: Map<string, lib.ModInfo> | null = null;
/** User and roles manager for the cluster */
userManager: UserManager;
/** True if {@link Controller.hosts} has changed since last time it was saved */
hostsDirty = false;
/** True if {@link Controller.instances} has changed since last time it was saved */
instancesDirty = false;
/** True if {@link Controller.modPacks} has changed since last time it was saved */
modPacksDirty = false;
httpServer: http.Server | null = null;
httpServerCloser: HttpCloser | null = null;
@@ -85,15 +81,51 @@ export default class Controller {
devMiddleware: any | null = null;
autosaveInterval?: ReturnType<typeof setInterval>;
logDirectory: string = "";
clusterLogIndex: lib.LogIndex | null = null;
clusterLogBuildInterval: ReturnType<typeof setInterval> | null = null;
static async bootstrap(config: lib.ControllerConfig) {
let databaseDirectory = config.get("controller.database_directory");
await fs.ensureDir(databaseDirectory);
const hosts = await Controller.loadHosts(path.join(databaseDirectory, "hosts.json"));
const instances = await Controller.loadInstances(path.join(databaseDirectory, "instances.json"));
const modPacks = await Controller.loadModPacks(path.join(databaseDirectory, "mod-packs.json"));
const userManager = new UserManager(config);
await userManager.load(path.join(databaseDirectory, "users.json"));
let modsDirectory = config.get("controller.mods_directory");
await fs.ensureDir(modsDirectory);
const mods = await Controller.loadModInfos(modsDirectory);
return [
hosts,
instances,
modPacks,
mods,
userManager,
] as const;
}
constructor(
clusterLogger: winston.Logger,
pluginInfos: any[],
configPath: string,
config: lib.ControllerConfig
config: lib.ControllerConfig,
/** Mapping of host id to host info */
public hosts = new Map<number, HostInfo>(),
/** Mapping of instance id to instance info */
public instances = new Map<number, InstanceInfo>(),
/** Mapping of mod pack id to mod pack */
public modPacks = new Map<number, lib.ModPack>(),
/** Mapping of mod names to mod infos */
public mods = new Map<string, lib.ModInfo>(),
/** User and roles manager for the cluster */
public userManager = new UserManager(config),
) {
this.clusterLogger = clusterLogger;
this.pluginInfos = pluginInfos;
@@ -105,7 +137,6 @@ export default class Controller {
this.app.locals.streams = new Map();
this.wsServer = new WsServer(this);
this.userManager = new UserManager(this.config);
this.subscriptions = new lib.SubscriptionController(this);
// Handle subscriptions for all internal properties
@@ -169,22 +200,13 @@ export default class Controller {
this._startDevServer(args);
}
let databaseDirectory = this.config.get("controller.database_directory");
await fs.ensureDir(databaseDirectory);
this.hosts = await Controller.loadHosts(path.join(databaseDirectory, "hosts.json"));
this.instances = await Controller.loadInstances(path.join(databaseDirectory, "instances.json"));
this.modPacks = await Controller.loadModPacks(path.join(databaseDirectory, "mod-packs.json"));
await this.userManager.load(path.join(databaseDirectory, "users.json"));
let modsDirectory = this.config.get("controller.mods_directory");
await fs.ensureDir(modsDirectory);
this.mods = await Controller.loadModInfos(modsDirectory);
this.config.on("fieldChanged", (group, field, prev) => {
if (group.name === "controller" && field === "autosave_interval") {
this.onAutosaveIntervalChanged();
}
lib.invokeHook(this.plugins, "onControllerConfigFieldChanged", group, field, prev);
});
for (let instance of this.instances!.values()) {
for (let instance of this.instances.values()) {
this.addInstanceHooks(instance);
}
@@ -253,6 +275,8 @@ export default class Controller {
logger.info(`Listening for HTTPS on port ${(this.httpsServer.address() as AddressInfo).port}`);
}
this.onAutosaveIntervalChanged();
logger.info("Started controller");
this._state = "running";
}
@@ -317,39 +341,22 @@ export default class Controller {
clearInterval(this.clusterLogBuildInterval);
}
if (this.autosaveInterval) {
clearInterval(this.autosaveInterval);
this.autosaveInterval = undefined;
}
if (this.clusterLogIndex) {
await this.clusterLogIndex.save();
}
logger.info("Saving config");
await lib.safeOutputFile(this.configPath, JSON.stringify(this.config.serialize(), null, "\t"));
if (this.devMiddleware) {
await new Promise((resolve, reject) => { this.devMiddleware.close(resolve); });
}
let databaseDirectory = this.config.get("controller.database_directory");
if (this.hosts) {
await Controller.saveHosts(path.join(databaseDirectory, "hosts.json"), this.hosts);
}
if (this.instances) {
await Controller.saveInstances(path.join(databaseDirectory, "instances.json"), this.instances);
}
if (this.modPacks) {
await Controller.saveModPacks(path.join(databaseDirectory, "mod-packs.json"), this.modPacks);
}
if (this.userManager) {
await this.userManager.save(path.join(databaseDirectory, "users.json"));
}
await lib.invokeHook(this.plugins, "onShutdown");
if (this.wsServer) {
await this.wsServer.stop();
}
await this.wsServer.stop();
let stopTasks = [];
logger.info("Stopping HTTP(S) server");
@@ -361,9 +368,69 @@ export default class Controller {
}
await Promise.all(stopTasks);
logger.info("Saving data");
await this.saveData();
logger.info("Goodbye");
}
onAutosaveIntervalChanged() {
if (this.autosaveInterval) {
clearInterval(this.autosaveInterval);
this.autosaveInterval = undefined;
}
const autosaveIntervalSeconds = this.config.get("controller.autosave_interval");
if (autosaveIntervalSeconds > 0) {
this.autosaveInterval = setInterval(this.saveData.bind(this), autosaveIntervalSeconds * 1000);
}
}
/**
* Save all data currently in memory to disk
*
* Note: If a save is currently in progress this will wait until that
* completes and start another save.
*/
async saveData() {
await this._saveDataAsyncSerial.invoke();
}
private _saveDataAsyncSerial = new lib.AsyncSerialMergingCallback(async () => {
try {
await this._saveDataInternal();
} catch (err: any) {
logger.error(`Unexpected error during saveData:\n${err.stack}`);
}
});
private async _saveDataInternal() {
if (this.config.dirty) {
this.config.dirty = false;
await lib.safeOutputFile(this.configPath, JSON.stringify(this.config.serialize(), null, "\t"));
}
let databaseDirectory = this.config.get("controller.database_directory");
if (this.hostsDirty) {
this.hostsDirty = false;
await Controller.saveHosts(path.join(databaseDirectory, "hosts.json"), this.hosts);
}
if (this.instancesDirty) {
this.instancesDirty = false;
await Controller.saveInstances(path.join(databaseDirectory, "instances.json"), this.instances);
}
if (this.modPacksDirty) {
this.modPacksDirty = false;
await Controller.saveModPacks(path.join(databaseDirectory, "mod-packs.json"), this.modPacks);
}
if (this.userManager.dirty) {
await this.userManager.save(path.join(databaseDirectory, "users.json"));
}
await lib.invokeHook(this.plugins, "onSaveData");
}
static async loadHosts(filePath: string): Promise<Map<number, HostInfo>> {
let serialized: [number, HostInfo][];
try {
@@ -489,14 +556,14 @@ export default class Controller {
async deleteMod(name: string, version: string): Promise<void> {
let filename = `${name}_${version}.zip`;
let mod = this.mods!.get(filename);
let mod = this.mods.get(filename);
if (!mod) {
throw new Error(`Mod ${filename} does not exist`);
}
let modsDirectory = this.config.get("controller.mods_directory");
await fs.unlink(path.join(modsDirectory, filename));
this.mods!.delete(filename);
this.mods.delete(filename);
mod.isDeleted = true;
this.modUpdated(mod);
}
@@ -603,7 +670,7 @@ export default class Controller {
* @throws {module:lib.RequestError} if the instance does not exist.
*/
getRequestInstance(instanceId:number): InstanceInfo {
let instance = this.instances!.get(instanceId);
let instance = this.instances.get(instanceId);
if (!instance) {
throw new lib.RequestError(`Instance with ID ${instanceId} does not exist`);
}
@@ -629,7 +696,7 @@ export default class Controller {
*/
async instanceCreate(instanceConfig: lib.InstanceConfig): Promise<InstanceInfo> {
let instanceId = instanceConfig.get("instance.id");
if (this.instances!.has(instanceId)) {
if (this.instances.has(instanceId)) {
throw new lib.RequestError(`Instance with ID ${instanceId} already exists`);
}
@@ -660,9 +727,11 @@ export default class Controller {
instanceConfig.set("factorio.settings", settings);
let instance = new InstanceInfo({ config: instanceConfig, status: "unassigned" });
this.instances!.set(instanceId, instance);
this.instances.set(instanceId, instance);
this.instancesDirty = true;
await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance);
this.addInstanceHooks(instance);
this.instanceDetailsUpdated(instance);
return instance;
}
@@ -707,13 +776,14 @@ export default class Controller {
// Assign to target
instance.config.set("instance.assigned_host", hostId);
this.instancesDirty = true;
if (hostId !== null && newHostConnection) {
await newHostConnection.send(
new lib.InstanceAssignInternalRequest(instanceId, instance.config.serialize("host"))
);
} else {
instance.status = "unassigned";
this.instanceUpdated(instance);
this.instanceDetailsUpdated(instance);
}
}
@@ -731,11 +801,12 @@ export default class Controller {
if (hostId !== null) {
await this.sendTo({ hostId }, new lib.InstanceDeleteInternalRequest(instanceId));
}
this.instances!.delete(instanceId);
this.instances.delete(instanceId);
this.instancesDirty = true;
let prev = instance.status;
instance.status = "deleted";
this.instanceUpdated(instance);
this.instanceDetailsUpdated(instance);
await lib.invokeHook(this.plugins, "onInstanceStatusChanged", instance, prev);
}
@@ -763,16 +834,15 @@ export default class Controller {
addInstanceHooks(instance: InstanceInfo) {
instance.config.on("fieldChanged", (group: lib.ConfigGroup, field: string, prev: any) => {
if (group.name === "instance" && field === "name") {
this.instanceUpdated(instance);
this.instanceDetailsUpdated(instance);
}
this.instancesDirty = true;
lib.invokeHook(this.plugins, "onInstanceConfigFieldChanged", instance, group, field, prev);
});
this.instanceUpdated(instance);
}
instanceUpdated(instance: InstanceInfo) {
instanceDetailsUpdated(instance: InstanceInfo) {
let assigned_host: number|null|undefined = instance.config.get("instance.assigned_host");
if (assigned_host === null) {
assigned_host = undefined;
@@ -799,6 +869,7 @@ export default class Controller {
}
modPackUpdated(modPack: lib.ModPack) {
this.modPacksDirty = true;
this.subscriptions.broadcast(new lib.ModPackUpdateEvent(modPack));
lib.invokeHook(this.plugins, "onModPackUpdated", modPack);
}
@@ -809,6 +880,7 @@ export default class Controller {
}
userUpdated(user: lib.User) {
this.userManager.dirty = true;
this.subscriptions.broadcast(new lib.UserUpdateEvent(
new lib.RawUser(
user.name,
@@ -1109,7 +1181,7 @@ export default class Controller {
connection = this.wsServer.controlConnections.get(dst.id);
} else if (dst.type === lib.Address.instance) {
let instance = this.instances!.get(dst.id);
let instance = this.instances.get(dst.id);
if (!instance) {
return;
}
+1 -1
View File
@@ -40,7 +40,7 @@ export default class ControllerRouter {
msg = `Host ${dst.id} is offline`;
}
} else if (dst.type === lib.Address.instance) {
let instance = this.controller.instances!.get(dst.id);
let instance = this.controller.instances.get(dst.id);
if (!instance) {
msg = `Instance ${dst.id} does not exist`;
} else {
+15 -12
View File
@@ -46,9 +46,9 @@ export default class HostConnection extends BaseConnection {
this.plugins = new Map(Object.entries(registerData.plugins));
this._checkPluginVersions();
let currentHostInfo = this._controller.hosts!.get(this._id);
let currentHostInfo = this._controller.hosts.get(this._id);
this._controller.hosts!.set(this._id, {
this._controller.hosts.set(this._id, {
agent: this._agent,
id: this._id,
name: this._name,
@@ -57,6 +57,7 @@ export default class HostConnection extends BaseConnection {
plugins: registerData.plugins,
token_valid_after: currentHostInfo?.token_valid_after,
});
this._controller.hostsDirty = true;
for (let event of ["connect", "drop", "resume", "close"] as const) {
// eslint-disable-next-line no-loop-func
@@ -69,14 +70,14 @@ export default class HostConnection extends BaseConnection {
this.connector.on("close", () => {
// Update status to unknown for instances on this host.
for (let instance of this._controller.instances!.values()) {
for (let instance of this._controller.instances.values()) {
if (instance.config.get("instance.assigned_host") !== this._id) {
continue;
}
let prev = instance.status;
instance.status = "unknown";
this._controller.instanceUpdated(instance);
this._controller.instanceDetailsUpdated(instance);
lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", instance, prev);
}
});
@@ -104,7 +105,7 @@ export default class HostConnection extends BaseConnection {
break;
case lib.Address.instance:
let instance = this._controller.instances!.get(message.src.id);
let instance = this._controller.instances.get(message.src.id);
if (!instance || instance.config.get("instance.assigned_host") !== this._id) {
throw new lib.InvalidMessage(
`Received message with invalid src ${message.src} from ${origin}`
@@ -156,7 +157,7 @@ export default class HostConnection extends BaseConnection {
}
async handleInstanceStatusChangedEvent(request: lib.InstanceStatusChangedEvent) {
let instance = this._controller.instances!.get(request.instanceId);
let instance = this._controller.instances.get(request.instanceId);
// It's possible to get updates from an instance that does not exist
// or is not assigned to the host it originated from if it was
@@ -180,13 +181,13 @@ export default class HostConnection extends BaseConnection {
instance.status = request.status as lib.InstanceStatus;
instance.game_port = request.gamePort || null;
logger.verbose(`Instance ${instance.config.get("instance.name")} State: ${instance.status}`);
this._controller.instanceUpdated(instance);
this._controller.instanceDetailsUpdated(instance);
await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", instance, prev);
}
async handleInstancesUpdateRequest(request: lib.InstancesUpdateRequest) {
// Push updated instance configs
for (let instance of this._controller.instances!.values()) {
for (let instance of this._controller.instances.values()) {
if (instance.config.get("instance.assigned_host") === this._id) {
await this.send(
new lib.InstanceAssignInternalRequest(instance.id, instance.config.serialize("host"))
@@ -199,7 +200,7 @@ export default class HostConnection extends BaseConnection {
let instanceConfig = new lib.InstanceConfig("controller");
await instanceConfig.load(instanceData.config as lib.SerializedConfig, "host");
let controllerInstance = this._controller.instances!.get(instanceConfig.get("instance.id"));
let controllerInstance = this._controller.instances.get(instanceConfig.get("instance.id"));
if (controllerInstance) {
// Check if this instance is assigned somewhere else.
if (controllerInstance.config.get("instance.assigned_host") !== this._id) {
@@ -214,7 +215,7 @@ export default class HostConnection extends BaseConnection {
let prev = controllerInstance.status;
controllerInstance.status = instanceData.status as InstanceStatus;
logger.verbose(`Instance ${instanceConfig.get("instance.name")} State: ${instanceData.status}`);
this._controller.instanceUpdated(controllerInstance);
this._controller.instanceDetailsUpdated(controllerInstance);
await lib.invokeHook(
this._controller.plugins, "onInstanceStatusChanged", controllerInstance, prev
);
@@ -226,13 +227,15 @@ export default class HostConnection extends BaseConnection {
let newInstance = new InstanceInfo(
{ config: instanceConfig, status: instanceData.status as InstanceStatus }
);
this._controller.instances!.set(instanceConfig.get("instance.id"), newInstance);
this._controller.instances.set(instanceConfig.get("instance.id"), newInstance);
this._controller.instancesDirty = true;
this._controller.addInstanceHooks(newInstance);
await this.send(
new lib.InstanceAssignInternalRequest(
instanceConfig.get("instance.id"), instanceConfig.serialize("host")
)
);
this._controller.instanceDetailsUpdated(newInstance);
await lib.invokeHook(this._controller.plugins, "onInstanceStatusChanged", newInstance);
}
@@ -289,7 +292,7 @@ export default class HostConnection extends BaseConnection {
user.recalculatePlayerStats();
this._controller.userUpdated(user);
let instance = this._controller.instances!.get(instanceId)!;
let instance = this._controller.instances.get(instanceId)!;
await lib.invokeHook(this._controller.plugins, "onPlayerEvent", instance, {
type: event.type,
name: event.name,
+3 -4
View File
@@ -10,6 +10,7 @@ import * as lib from "@clusterio/lib";
export default class UserManager {
roles: Map<number, lib.Role> = new Map();
users: Map<string, lib.User> = new Map();
dirty = false;
constructor(
private _config: lib.ControllerConfig
@@ -41,10 +42,6 @@ export default class UserManager {
}
async save(filePath:string): Promise<void> {
if (this.roles.size === 0 || this.users.size === 0) {
return;
}
let serializedRoles = [];
for (let role of this.roles.values()) {
serializedRoles.push(role.serialize());
@@ -59,6 +56,7 @@ export default class UserManager {
users: serializedUsers,
roles: serializedRoles,
};
this.dirty = false;
await lib.safeOutputFile(filePath, JSON.stringify(serialized, null, "\t"));
}
@@ -80,6 +78,7 @@ export default class UserManager {
let user = new lib.User({ name, roles }, this.roles);
this.users.set(name, user);
this.dirty = true;
return user;
}
+3 -3
View File
@@ -242,7 +242,7 @@ ${err.stack}`
throw new Error("missmatched host id");
}
let host = this.controller.hosts!.get(data.id);
let host = this.controller.hosts.get(data.id);
if (!tokenPayload.iat || tokenPayload.iat < (host?.token_valid_after??0)) {
throw new Error("invalid token");
}
@@ -274,13 +274,13 @@ ${err.stack}`
connector.on("close", () => {
if (this.hostConnections.get(data.id) === connection) {
this.hostConnections.delete(data.id);
this.controller.hostUpdated(this.controller.hosts!.get(data.id) as HostInfo);
this.controller.hostUpdated(this.controller.hosts.get(data.id) as HostInfo);
} else {
logger.warn("Unlisted HostConnection closed");
}
});
this.hostConnections.set(data.id, connection);
this.controller.hostUpdated(this.controller.hosts!.get(data.id) as HostInfo);
this.controller.hostUpdated(this.controller.hosts.get(data.id) as HostInfo);
let src = new lib.Address(lib.Address.host, data.id);
connector.ready(socket, src, sessionToken, undefined);
}
+1 -1
View File
@@ -137,7 +137,7 @@ function validateHostToken(req: Request, res: Response, next: any) {
if (typeof tokenPayload === "string") {
throw new Error("unexpected JsonWebToken type");
}
let host = (req.app.locals.controller as Controller).hosts!.get(tokenPayload.host);
let host = (req.app.locals.controller as Controller).hosts.get(tokenPayload.host);
if (!host) {
throw new Error("invalid host");
}