2021-12-21 16:52:12 +00:00
|
|
|
import { Server } from "http";
|
2022-10-05 14:49:53 +01:00
|
|
|
import { Logger } from "matrix-appservice-bridge";
|
2022-07-13 16:14:21 +01:00
|
|
|
import { Application, default as expressApp, NextFunction, Request, Response, Router } from "express";
|
|
|
|
import { errorMiddleware } from "./api";
|
2021-12-21 16:52:12 +00:00
|
|
|
|
|
|
|
// Appserices can't be handled yet because the bot-sdk maintains control of it.
|
2022-01-04 17:49:59 +00:00
|
|
|
// See https://github.com/turt2live/matrix-bot-sdk/issues/191
|
|
|
|
export type ResourceName = "webhooks"|"widgets"|"metrics"|"provisioning";
|
|
|
|
export const ResourceTypeArray: ResourceName[] = ["webhooks","widgets","metrics","provisioning"];
|
2023-05-18 12:05:23 +01:00
|
|
|
import { Handlers } from "@sentry/node";
|
2021-12-21 16:52:12 +00:00
|
|
|
export interface BridgeConfigListener {
|
|
|
|
bindAddress?: string;
|
|
|
|
port: number;
|
|
|
|
resources: Array<ResourceName>;
|
|
|
|
}
|
|
|
|
|
2022-10-05 14:49:53 +01:00
|
|
|
const log = new Logger("ListenerService");
|
2021-12-21 16:52:12 +00:00
|
|
|
|
|
|
|
export class ListenerService {
|
|
|
|
private readonly listeners: {
|
|
|
|
server?: Server,
|
|
|
|
app: Application,
|
|
|
|
config: BridgeConfigListener,
|
|
|
|
resourcesBound: boolean,
|
|
|
|
}[] = [];
|
|
|
|
|
|
|
|
constructor(config: BridgeConfigListener[]) {
|
|
|
|
if (config.length < 1) {
|
|
|
|
throw Error('No listeners configured');
|
|
|
|
}
|
|
|
|
for (const listenerConfig of config) {
|
|
|
|
const app = expressApp();
|
2023-05-18 12:05:23 +01:00
|
|
|
app.use(Handlers.requestHandler());
|
2021-12-21 16:52:12 +00:00
|
|
|
this.listeners.push({
|
|
|
|
config: listenerConfig,
|
|
|
|
app,
|
|
|
|
resourcesBound: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bindResource(resourceName: ResourceName, router: Router) {
|
2022-04-08 16:16:12 +01:00
|
|
|
const listeners = this.listeners.filter((l) => l.config.resources.includes(resourceName));
|
|
|
|
if (listeners.length === 0) {
|
|
|
|
throw Error(`No listeners found for resource ${resourceName}`);
|
|
|
|
}
|
|
|
|
for (const listener of listeners) {
|
2022-03-07 20:04:26 +00:00
|
|
|
log.debug(`Registering ${listener.config.bindAddress || "127.0.0.1"}:${listener.config.port} for ${resourceName}`);
|
2021-12-21 16:52:12 +00:00
|
|
|
listener.app.use(router);
|
|
|
|
listener.resourcesBound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 15:58:25 +00:00
|
|
|
public finaliseListeners() {
|
|
|
|
for (const listener of this.listeners) {
|
|
|
|
// By default, Sentry only reports 500+ errors, which is what we want.
|
|
|
|
listener.app.use(Handlers.errorHandler());
|
|
|
|
listener.app.use((err: unknown, req: Request, res: Response, next: NextFunction) => errorMiddleware(log)(err, req, res, next));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-08 16:16:12 +01:00
|
|
|
public getApplicationsForResource(resourceName: ResourceName): Application[] {
|
|
|
|
const listeners = this.listeners.filter((l) => l.config.resources.includes(resourceName));
|
|
|
|
if (listeners.length === 0) {
|
|
|
|
throw Error(`No listener found for resource ${resourceName}`);
|
|
|
|
}
|
|
|
|
for (const listener of listeners) {
|
|
|
|
log.debug(`Reverse binding ${listener.config.bindAddress || "127.0.0.1"}:${listener.config.port} for ${resourceName}`);
|
|
|
|
listener.resourcesBound = true;
|
|
|
|
}
|
|
|
|
return listeners.map(l => l.app);
|
|
|
|
}
|
|
|
|
|
2021-12-21 16:52:12 +00:00
|
|
|
public start() {
|
|
|
|
for (const listener of this.listeners) {
|
|
|
|
if (listener.server) {
|
|
|
|
throw Error('Cannot run start() twice');
|
|
|
|
}
|
|
|
|
const addr = listener.config.bindAddress || "127.0.0.1";
|
|
|
|
listener.server = listener.app.listen(listener.config.port, addr);
|
2023-03-24 14:09:09 +00:00
|
|
|
|
|
|
|
// Ensure each listener has a ready probe.
|
|
|
|
listener.app.get("/live", (_, res) => res.send({ok: true}));
|
|
|
|
listener.app.get("/ready", (_, res) => res.status(listener.resourcesBound ? 200 : 500).send({ready: listener.resourcesBound}));
|
2022-03-07 20:04:26 +00:00
|
|
|
log.info(`Listening on http://${addr}:${listener.config.port} for ${listener.config.resources.join(', ')}`)
|
2021-12-21 16:52:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async stop() {
|
|
|
|
const promises = [];
|
|
|
|
log.info(`Stopping all listeners`);
|
|
|
|
for (const listener of this.listeners) {
|
|
|
|
if (listener.server) {
|
|
|
|
promises.push(new Promise<void>((res, rej) => listener.server?.close((e) => e ? rej(e) : res())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
}
|
|
|
|
}
|