From 94cb60793040372977bd7dfd851efa5bb9a0aa93 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Sun, 3 Dec 2023 00:14:35 +0100 Subject: [PATCH] Improve typing of various controller entities --- packages/controller/src/BaseConnection.ts | 2 +- packages/controller/src/ControlConnection.ts | 8 +-- packages/controller/src/Controller.ts | 16 +++-- packages/controller/src/HostConnection.ts | 1 - packages/controller/src/routes.ts | 75 +++++++++++--------- 5 files changed, 58 insertions(+), 44 deletions(-) diff --git a/packages/controller/src/BaseConnection.ts b/packages/controller/src/BaseConnection.ts index b12c7e3c..cf8b4052 100644 --- a/packages/controller/src/BaseConnection.ts +++ b/packages/controller/src/BaseConnection.ts @@ -13,7 +13,7 @@ import ControllerRouter from "./ControllerRouter"; * @alias module:controller/src/BaseConnection */ export default class BaseConnection extends lib.Link { - declare connector: lib.WebSocketBaseConnector; + declare ["connector"]: WsServerConnector; constructor( connector: lib.WebSocketBaseConnector, diff --git a/packages/controller/src/ControlConnection.ts b/packages/controller/src/ControlConnection.ts index 940bdec3..32463aef 100644 --- a/packages/controller/src/ControlConnection.ts +++ b/packages/controller/src/ControlConnection.ts @@ -88,9 +88,9 @@ export default class ControlConnection extends BaseConnection { this.handle(lib.InstanceConfigSetFieldRequest, this.handleInstanceConfigSetFieldRequest.bind(this)); this.handle(lib.InstanceConfigSetPropRequest, this.handleInstanceConfigSetPropRequest.bind(this)); this.handle(lib.InstanceAssignRequest, this.handleInstanceAssignRequest.bind(this)); - this.handle(lib.InstanceRenameSaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller)); - this.handle(lib.InstanceCopySaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller)); - this.handle(lib.InstanceDeleteSaveRequest, this._controller.sendToHostByInstanceId.bind(this._controller)); + this.handle(lib.InstanceRenameSaveRequest, controller.sendRequestToHostByInstanceId.bind(controller)); + this.handle(lib.InstanceCopySaveRequest, controller.sendRequestToHostByInstanceId.bind(controller)); + this.handle(lib.InstanceDeleteSaveRequest, controller.sendRequestToHostByInstanceId.bind(controller)); this.handle(lib.InstanceDownloadSaveRequest, this.handleInstanceDownloadSaveRequest.bind(this)); this.handle(lib.InstanceTransferSaveRequest, this.handleInstanceTransferSaveRequest.bind(this)); this.handle(lib.ModPackListRequest, this.handleModPackListRequest.bind(this)); @@ -576,7 +576,7 @@ export default class ControlConnection extends BaseConnection { stream.filename = mod.filename; stream.source = fs.createReadStream(modPath); stream.mime = "application/zip"; - stream.size = mod.size; + stream.size = String(mod.size); return stream.id; } diff --git a/packages/controller/src/Controller.ts b/packages/controller/src/Controller.ts index e21bfe68..4639898f 100644 --- a/packages/controller/src/Controller.ts +++ b/packages/controller/src/Controller.ts @@ -36,6 +36,8 @@ const logSizeGauge = new Gauge( "Size of all log files currently stored on the controller." ); +type InstanceId = { instanceId: number }; + /** * Manages all controller related operations * @alias module:controller/src/Controller @@ -1207,17 +1209,19 @@ export default class Controller { } } - async sendToHostByInstanceId(requestOrEvent: any) { + async sendToHostByInstanceId(request: Req & lib.Request): Promise; + sendToHostByInstanceId(event: T & lib.Event): void; + async sendToHostByInstanceId(requestOrEvent: lib.Request | lib.Event) { if (requestOrEvent.constructor.type === "request") { - return this.sendRequestToHostByInstanceId(requestOrEvent); + return this.sendRequestToHostByInstanceId(requestOrEvent as any); } if (requestOrEvent.constructor.type === "event") { - return this.sendEventToHostByInstanceId(requestOrEvent); + return this.sendEventToHostByInstanceId(requestOrEvent as any); } - throw Error(`Unknown type ${requestOrEvent.constructor.type}.`); + throw Error(`Unknown type ${(requestOrEvent.constructor as any).type}.`); } - async sendRequestToHostByInstanceId(request: any) { + async sendRequestToHostByInstanceId(request: Req & lib.Request) { let instance = this.getRequestInstance(request.instanceId); let hostId = instance.config.get("instance.assigned_host"); if (hostId === null) { @@ -1230,7 +1234,7 @@ export default class Controller { return await connection.send(request); } - sendEventToHostByInstanceId(event: any) { + sendEventToHostByInstanceId(event: T & lib.Event) { let instance = this.getRequestInstance(event.instanceId); let hostId = instance.config.get("instance.assigned_host"); if (hostId === null) { diff --git a/packages/controller/src/HostConnection.ts b/packages/controller/src/HostConnection.ts index 71ffafc2..140bd124 100644 --- a/packages/controller/src/HostConnection.ts +++ b/packages/controller/src/HostConnection.ts @@ -25,7 +25,6 @@ export interface HostInfo { * @alias module:controller/src/HostConnection */ export default class HostConnection extends BaseConnection { - declare connector: WsServerConnector; private _agent: any; private _id: any; private _name: any; diff --git a/packages/controller/src/routes.ts b/packages/controller/src/routes.ts index 719584ba..8abc77ec 100644 --- a/packages/controller/src/routes.ts +++ b/packages/controller/src/routes.ts @@ -16,6 +16,17 @@ const { logger } = lib; const finished = util.promisify(nodeStream.finished); +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Express { + export interface Locals { + controller: Controller, + mainBundle: string, + devPlugins: Map, + streams: Map + } + } +} // Merges samples from sourceResult to destinationResult function mergeSamples(destinationResult: lib.CollectorResultSerialized, sourceResult: lib.CollectorResultSerialized) { @@ -95,14 +106,14 @@ async function getMetrics(req: Request, res: Response, next: any) { function getPlugins(req: Request, res: Response) { let plugins: lib.PluginWebApi[] = []; - for (let pluginInfo of (req.app.locals.controller as Controller).pluginInfos) { + for (let pluginInfo of req.app.locals.controller.pluginInfos) { let name = pluginInfo.name; let loaded = req.app.locals.controller.plugins.has(name); - let enabled = loaded && req.app.locals.controller.config.group(pluginInfo.name).get("load_plugin"); + let enabled = loaded && req.app.locals.controller.config.group(pluginInfo.name).get("load_plugin") as boolean; let web: { main:any, error:string|undefined } = { main: undefined, error: undefined}; let devPlugins = req.app.locals.devPlugins; if (devPlugins && devPlugins.has(name)) { - let stats = res.locals.webpack.devMiddleware.stats.stats[devPlugins.get(name)]; + let stats = res.locals.webpack.devMiddleware.stats.stats[devPlugins.get(name)!]; web.main = stats.toJson().assetsByChunkName[name]; } else if (pluginInfo.manifest) { web.main = pluginInfo.manifest[`${pluginInfo.name}.js`]; @@ -137,7 +148,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.hosts.get(tokenPayload.host); if (!host) { throw new Error("invalid host"); } @@ -251,12 +262,12 @@ async function uploadExport(req: Request, res: Response) { } -interface ProxyStream { +export interface ProxyStream { id: string; source: nodeStream.Readable | null; flowing: boolean; - size: number | null; - mime: string | null; + size?: string; + mime?: string; filename: string | null; events: events.EventEmitter; timeout: ReturnType; @@ -269,8 +280,8 @@ export async function createProxyStream(app: Application): Promise id, source: null, flowing: false, - size: null, - mime: null, + size: undefined, + mime: undefined, filename: null, events: new events.EventEmitter(), timeout: setTimeout(() => { @@ -289,7 +300,7 @@ export async function createProxyStream(app: Application): Promise } async function putStream(req: Request, res: Response) { - let stream = req.app.locals.streams.get(req.params.id); + const stream = req.app.locals.streams.get(req.params.id); if (!stream || stream.source) { res.sendStatus(404); return; @@ -309,35 +320,35 @@ async function putStream(req: Request, res: Response) { }); } +function startStream(res: Response, stream: ProxyStream) { + res.append("Content-Type", stream.mime); + if (stream.size) { + res.append("Content-Length", stream.size); + } + if (stream.filename) { + res.append("Content-Disposition", `attachment; filename="${stream.filename}"`); + } else { + res.append("Content-Disposition", "attachment"); + } + stream.source!.pipe(res); + stream.flowing = true; + res.on("close", () => { + stream.events.emit("close"); + }); + clearTimeout(stream.timeout); +} + async function getStream(req: Request, res: Response) { - let stream = req.app.locals.streams.get(req.params.id); + const stream = req.app.locals.streams.get(req.params.id); if (!stream || stream.flowing) { res.sendStatus(404); return; } - function startStream() { - res.append("Content-Type", stream.mime); - if (stream.size) { - res.append("Content-Length", stream.size); - } - if (stream.filename) { - res.append("Content-Disposition", `attachment; filename="${stream.filename}"`); - } else { - res.append("Content-Disposition", "attachment"); - } - stream.source.pipe(res); - stream.flowing = true; - res.on("close", () => { - stream.events.emit("close"); - }); - clearTimeout(stream.timeout); - } - if (stream.source) { - startStream(); + startStream(res, stream); } else { - stream.events.on("source", startStream); + stream.events.on("source", () => startStream(res, stream)); stream.events.on("timeout", () => { res.sendStatus(500); }); @@ -377,7 +388,7 @@ async function uploadSave(req: Request, res: Response) { let proxyStream = await createProxyStream(req.app); proxyStream.source = stream; proxyStream.mime = streamMime; - let timeout = new Promise((_, reject) => { + let timeout = new Promise((_, reject) => { proxyStream.events.on("timeout", () => { stream.resume(); reject(new Error("Timed out establishing stream to host"));