mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 13:17:08 +00:00
Add a listeners config block (#102)
* Add support for a multi-resource listener style handler * Update configs * Speed up config check * Speed up config check * Update docs
This commit is contained in:
parent
6d46492693
commit
060b73263f
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -37,8 +37,8 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16
|
||||
- run: yarn --ignore-scripts && yarn build:app
|
||||
- run: node lib/Config/Defaults.js --config | diff config.sample.yml -
|
||||
- run: yarn --ignore-scripts
|
||||
- run: yarn --silent ts-node src/Config/Defaults.ts --config | diff config.sample.yml -
|
||||
|
||||
metrics-docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
1
changelog.d/102.feature
Normal file
1
changelog.d/102.feature
Normal file
@ -0,0 +1 @@
|
||||
Allow running multiple resources on the same HTTP listener. See the new `listeners` config.
|
@ -54,16 +54,9 @@ generic:
|
||||
urlPrefix: https://example.com/mywebhookspath/
|
||||
allowJsTransformationFunctions: false
|
||||
userIdPrefix: webhooks_
|
||||
webhook:
|
||||
# HTTP webhook listener options
|
||||
#
|
||||
port: 9000
|
||||
bindAddress: 0.0.0.0
|
||||
provisioning:
|
||||
# (Optional) Provisioning API for integration managers
|
||||
#
|
||||
bindAddress: 127.0.0.1
|
||||
port: 9001
|
||||
secret: "!secretToken"
|
||||
passFile:
|
||||
# A passkey used to encrypt tokens stored inside the bridge.
|
||||
@ -79,8 +72,6 @@ metrics:
|
||||
# (Optional) Prometheus metrics support
|
||||
#
|
||||
enabled: true
|
||||
bindAddress: 127.0.0.1
|
||||
port: 9002
|
||||
queue:
|
||||
# (Optional) Message queue / cache configuration options for large scale deployments
|
||||
#
|
||||
@ -91,4 +82,19 @@ logging:
|
||||
# (Optional) Logging settings. You can have a severity debug,info,warn,error
|
||||
#
|
||||
level: info
|
||||
listeners:
|
||||
# (Optional) HTTP Listener configuration.
|
||||
# Bind resource endpoints to ports and addresses.
|
||||
# 'resources' may be any of webhooks, widgets, metrics, provisioning, appservice
|
||||
#
|
||||
- port: 9000
|
||||
bindAddress: 0.0.0.0
|
||||
resources:
|
||||
- webhooks
|
||||
- widgets
|
||||
- port: 9001
|
||||
bindAddress: 127.0.0.1
|
||||
resources:
|
||||
- metrics
|
||||
- provisioning
|
||||
|
||||
|
@ -58,17 +58,30 @@ Copy `registration.sample.yml` into `registration.yml` and fill in:
|
||||
You will need to link the registration file to the homeserver. Consult your homeserver documentation
|
||||
on how to add appservices. [Synapse documents the process here](https://matrix-org.github.io/synapse/latest/application_services.html)
|
||||
|
||||
### Webhooks configuration
|
||||
### Listeners configuration
|
||||
|
||||
You will need to configure the webhooks listener at a minimum to make the bridge functional.
|
||||
To do this, ensure that your configuration for `webhook` is correct:
|
||||
You will need to configure some listeners to make the bridge functional.
|
||||
|
||||
```yaml
|
||||
webhook:
|
||||
port: 5061
|
||||
host: 0.0.0.0
|
||||
# (Optional) HTTP Listener configuration.
|
||||
# Bind resource endpoints to ports and addresses.
|
||||
# 'resources' may be any of webhooks, widgets, metrics, provisioning, appservice
|
||||
#
|
||||
- port: 9000
|
||||
bindAddress: 0.0.0.0
|
||||
resources:
|
||||
- webhooks
|
||||
- widgets
|
||||
- port: 9001
|
||||
bindAddress: 127.0.0.1
|
||||
resources:
|
||||
- metrics
|
||||
- provisioning
|
||||
```
|
||||
|
||||
At a minimum, you should bind the `webooks` resource to a port and address. You can have multiple resources on the same
|
||||
port, or one on each.
|
||||
|
||||
You will also need to make this port accessible to the internet so services like GitHub can reach the bridge. It
|
||||
is reccomended to factor hookshot into your load balancer configuration, but currrently this process is left as an
|
||||
excercise to the user.
|
||||
|
@ -29,7 +29,7 @@
|
||||
"lint": "yarn run lint:js && yarn run lint:rs",
|
||||
"lint:js": "eslint -c .eslintrc.js src/**/*.ts",
|
||||
"lint:rs": "cargo fmt --all -- --check",
|
||||
"generate-default-config": "node lib/Config/Defaults.js --config > config.sample.yml"
|
||||
"generate-default-config": "ts-node src/Config/Defaults.ts --config > config.sample.yml"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
|
@ -5,6 +5,7 @@ import { BridgeConfig, parseRegistrationFile } from "../Config/Config";
|
||||
import { Webhooks } from "../Webhooks";
|
||||
import { MatrixSender } from "../MatrixSender";
|
||||
import { UserNotificationWatcher } from "../Notifications/UserNotificationWatcher";
|
||||
import { ListenerService } from "../ListenerService";
|
||||
|
||||
LogWrapper.configureLogging("debug");
|
||||
const log = new LogWrapper("App");
|
||||
@ -14,24 +15,27 @@ async function start() {
|
||||
const registrationFile = process.argv[3] || "./registration.yml";
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
const registration = await parseRegistrationFile(registrationFile);
|
||||
const listener = new ListenerService(config.listeners);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
|
||||
if (config.queue.monolithic) {
|
||||
const webhookHandler = new Webhooks(config);
|
||||
webhookHandler.listen();
|
||||
listener.bindResource('webhooks', webhookHandler.expressRouter);
|
||||
const matrixSender = new MatrixSender(config, registration);
|
||||
matrixSender.listen();
|
||||
const userNotificationWatcher = new UserNotificationWatcher(config);
|
||||
userNotificationWatcher.start();
|
||||
}
|
||||
|
||||
const bridgeApp = new Bridge(config, registration);
|
||||
const bridgeApp = new Bridge(config, registration, listener);
|
||||
|
||||
process.once("SIGTERM", () => {
|
||||
log.error("Got SIGTERM");
|
||||
listener.stop();
|
||||
bridgeApp.stop();
|
||||
});
|
||||
await bridgeApp.start();
|
||||
listener.start();
|
||||
}
|
||||
|
||||
start().catch((ex) => {
|
||||
|
@ -3,6 +3,7 @@ import { Webhooks } from "../Webhooks";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import { UserNotificationWatcher } from "../Notifications/UserNotificationWatcher";
|
||||
import Metrics from "../Metrics";
|
||||
import { ListenerService } from "../ListenerService";
|
||||
|
||||
|
||||
const log = new LogWrapper("App");
|
||||
@ -11,22 +12,24 @@ async function start() {
|
||||
const configFile = process.argv[2] || "./config.yml";
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
const listener = new ListenerService(config.listeners);
|
||||
if (config.metrics) {
|
||||
if (!config.metrics.port) {
|
||||
log.warn(`Not running metrics for service, no port specified`);
|
||||
} else {
|
||||
Metrics.start(config.metrics);
|
||||
listener.bindResource('metrics', Metrics.expressRouter);
|
||||
}
|
||||
}
|
||||
const webhookHandler = new Webhooks(config);
|
||||
webhookHandler.listen();
|
||||
listener.bindResource('webhooks', webhookHandler.expressRouter);
|
||||
const userWatcher = new UserNotificationWatcher(config);
|
||||
userWatcher.start();
|
||||
listener.start();
|
||||
process.once("SIGTERM", () => {
|
||||
log.error("Got SIGTERM");
|
||||
webhookHandler.stop();
|
||||
listener.stop();
|
||||
userWatcher.stop();
|
||||
Metrics.stop();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { BridgeConfig, parseRegistrationFile } from "../Config/Config";
|
||||
import { MatrixSender } from "../MatrixSender";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import Metrics from "../Metrics";
|
||||
import { ListenerService } from "../ListenerService";
|
||||
|
||||
|
||||
const log = new LogWrapper("App");
|
||||
@ -12,19 +13,21 @@ async function start() {
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
const registration = await parseRegistrationFile(registrationFile);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
const listener = new ListenerService(config.listeners);
|
||||
const sender = new MatrixSender(config, registration);
|
||||
if (config.metrics) {
|
||||
if (!config.metrics.port) {
|
||||
log.warn(`Not running metrics for service, no port specified`);
|
||||
} else {
|
||||
Metrics.start(config.metrics);
|
||||
listener.bindResource('metrics', Metrics.expressRouter);
|
||||
}
|
||||
}
|
||||
sender.listen();
|
||||
listener.start();
|
||||
process.once("SIGTERM", () => {
|
||||
log.error("Got SIGTERM");
|
||||
sender.stop();
|
||||
Metrics.stop();
|
||||
listener.stop();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import { OAuthRequest } from "./WebhookTypes";
|
||||
import { promises as fs } from "fs";
|
||||
import { SetupConnection } from "./Connections/SetupConnection";
|
||||
import Metrics from "./Metrics";
|
||||
import { ListenerService } from "./ListenerService";
|
||||
const log = new LogWrapper("Bridge");
|
||||
|
||||
export function getAppservice(config: BridgeConfig, registration: IAppserviceRegistration, storage: IAppserviceStorageProvider) {
|
||||
@ -77,7 +78,7 @@ export class Bridge {
|
||||
|
||||
private ready = false;
|
||||
|
||||
constructor(private config: BridgeConfig, private registration: IAppserviceRegistration) {
|
||||
constructor(private config: BridgeConfig, private registration: IAppserviceRegistration, private readonly listener: ListenerService) {
|
||||
if (this.config.queue.host && this.config.queue.port) {
|
||||
log.info(`Initialising Redis storage (on ${this.config.queue.host}:${this.config.queue.port})`);
|
||||
this.storage = new RedisStorageProvider(this.config.queue.host, this.config.queue.port);
|
||||
@ -95,10 +96,7 @@ export class Bridge {
|
||||
|
||||
public stop() {
|
||||
this.as.stop();
|
||||
Metrics.stop();
|
||||
if (this.queue.stop) this.queue.stop();
|
||||
if (this.widgetApi) this.widgetApi.stop();
|
||||
if (this.provisioningApi) this.provisioningApi.stop();
|
||||
}
|
||||
|
||||
public async start() {
|
||||
@ -578,13 +576,13 @@ export class Bridge {
|
||||
}
|
||||
|
||||
if (this.config.widgets) {
|
||||
await this.widgetApi.start(this.config.widgets.port);
|
||||
this.listener.bindResource('widgets', this.widgetApi.expressRouter);
|
||||
}
|
||||
if (this.provisioningApi) {
|
||||
await this.provisioningApi.listen();
|
||||
this.listener.bindResource('provisioning', this.provisioningApi.expressRouter);
|
||||
}
|
||||
if (this.config.metrics?.enabled) {
|
||||
Metrics.start(this.config.metrics, this.as);
|
||||
this.listener.bindResource('metrics', Metrics.expressRouter);
|
||||
}
|
||||
await this.as.begin();
|
||||
log.info("Started bridge");
|
||||
|
@ -3,6 +3,7 @@ import { promises as fs } from "fs";
|
||||
import { IAppserviceRegistration } from "matrix-bot-sdk";
|
||||
import * as assert from "assert";
|
||||
import { configKey } from "./Decorators";
|
||||
import { BridgeConfigListener, ResourceTypeArray } from "../ListenerService";
|
||||
import { GitHubRepoConnectionOptions } from "../Connections/GithubRepo";
|
||||
|
||||
interface BridgeConfigGitHubYAML {
|
||||
@ -92,11 +93,12 @@ export interface BridgeGenericWebhooksConfig {
|
||||
}
|
||||
|
||||
interface BridgeWidgetConfig {
|
||||
port: number;
|
||||
port?: number;
|
||||
addToAdminRooms: boolean;
|
||||
publicUrl: string;
|
||||
}
|
||||
|
||||
|
||||
interface BridgeConfigBridge {
|
||||
domain: string;
|
||||
url: string;
|
||||
@ -111,8 +113,8 @@ interface BridgeConfigBridge {
|
||||
}
|
||||
|
||||
interface BridgeConfigWebhook {
|
||||
port: number;
|
||||
bindAddress: string;
|
||||
port?: number;
|
||||
bindAddress?: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigQueue {
|
||||
@ -132,7 +134,7 @@ interface BridgeConfigBot {
|
||||
|
||||
export interface BridgeConfigProvisioning {
|
||||
bindAddress?: string;
|
||||
port: number;
|
||||
port?: number;
|
||||
secret: string;
|
||||
}
|
||||
|
||||
@ -142,7 +144,6 @@ export interface BridgeConfigMetrics {
|
||||
port?: number;
|
||||
}
|
||||
|
||||
|
||||
interface BridgeConfigRoot {
|
||||
bot?: BridgeConfigBot;
|
||||
bridge: BridgeConfigBridge;
|
||||
@ -154,16 +155,15 @@ interface BridgeConfigRoot {
|
||||
logging: BridgeConfigLogging;
|
||||
passFile: string;
|
||||
queue: BridgeConfigQueue;
|
||||
webhook: BridgeConfigWebhook;
|
||||
webhook?: BridgeConfigWebhook;
|
||||
widgets?: BridgeWidgetConfig;
|
||||
metrics?: BridgeConfigMetrics;
|
||||
listeners?: BridgeConfigListener[];
|
||||
}
|
||||
|
||||
export class BridgeConfig {
|
||||
@configKey("Basic homeserver configuration")
|
||||
public readonly bridge: BridgeConfigBridge;
|
||||
@configKey("HTTP webhook listener options")
|
||||
public readonly webhook: BridgeConfigWebhook;
|
||||
@configKey("Message queue / cache configuration options for large scale deployments", true)
|
||||
public readonly queue: BridgeConfigQueue;
|
||||
@configKey("Logging settings. You can have a severity debug,info,warn,error", true)
|
||||
@ -188,6 +188,11 @@ export class BridgeConfig {
|
||||
@configKey("Prometheus metrics support", true)
|
||||
public readonly metrics?: BridgeConfigMetrics;
|
||||
|
||||
@configKey(`HTTP Listener configuration.
|
||||
Bind resource endpoints to ports and addresses.
|
||||
'resources' may be any of ${ResourceTypeArray.join(', ')}`, true)
|
||||
public readonly listeners: BridgeConfigListener[];
|
||||
|
||||
constructor(configData: BridgeConfigRoot, env: {[key: string]: string|undefined}) {
|
||||
this.bridge = configData.bridge;
|
||||
assert.ok(this.bridge);
|
||||
@ -201,12 +206,10 @@ export class BridgeConfig {
|
||||
this.gitlab = configData.gitlab;
|
||||
this.jira = configData.jira;
|
||||
this.generic = configData.generic;
|
||||
this.webhook = configData.webhook;
|
||||
this.provisioning = configData.provisioning;
|
||||
this.passFile = configData.passFile;
|
||||
this.bot = configData.bot;
|
||||
this.metrics = configData.metrics;
|
||||
assert.ok(this.webhook);
|
||||
this.queue = configData.queue || {
|
||||
monolithic: true,
|
||||
};
|
||||
@ -220,6 +223,41 @@ export class BridgeConfig {
|
||||
this.queue.port = env.CFG_QUEUE_POST ? parseInt(env.CFG_QUEUE_POST, 10) : undefined;
|
||||
}
|
||||
|
||||
// Listeners is a bit special
|
||||
this.listeners = configData.listeners || [];
|
||||
|
||||
// For legacy reasons, copy across the per-service listener config into the listeners array.
|
||||
if (configData.webhook?.port) {
|
||||
this.listeners.push({
|
||||
resources: ['webhooks'],
|
||||
port: configData.webhook.port,
|
||||
bindAddress: configData.webhook.bindAddress,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.provisioning?.port) {
|
||||
this.listeners.push({
|
||||
resources: ['provisioning'],
|
||||
port: this.provisioning.port,
|
||||
bindAddress: this.provisioning.bindAddress,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.metrics?.port) {
|
||||
this.listeners.push({
|
||||
resources: ['metrics'],
|
||||
port: this.metrics.port,
|
||||
bindAddress: this.metrics.bindAddress,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.widgets?.port) {
|
||||
this.listeners.push({
|
||||
resources: ['widgets'],
|
||||
port: this.widgets.port,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static async parseConfig(filename: string, env: {[key: string]: string|undefined}) {
|
||||
|
@ -21,12 +21,7 @@ export const DefaultConfig = new BridgeConfig({
|
||||
level: "info",
|
||||
},
|
||||
passFile: "passkey.pem",
|
||||
webhook: {
|
||||
port: 9000,
|
||||
bindAddress: "0.0.0.0"
|
||||
},
|
||||
widgets: {
|
||||
port: 5000,
|
||||
publicUrl: "https://example.com/bridge_widget/",
|
||||
addToAdminRooms: true,
|
||||
},
|
||||
@ -78,15 +73,23 @@ export const DefaultConfig = new BridgeConfig({
|
||||
userIdPrefix: "webhooks_",
|
||||
},
|
||||
provisioning: {
|
||||
bindAddress: "127.0.0.1",
|
||||
port: 9001,
|
||||
secret: "!secretToken"
|
||||
},
|
||||
metrics: {
|
||||
enabled: true,
|
||||
bindAddress: "127.0.0.1",
|
||||
port: 9002,
|
||||
}
|
||||
},
|
||||
listeners: [
|
||||
{
|
||||
port: 9000,
|
||||
bindAddress: '0.0.0.0',
|
||||
resources: ['webhooks', 'widgets'],
|
||||
},
|
||||
{
|
||||
port: 9001,
|
||||
bindAddress: '127.0.0.1',
|
||||
resources: ['metrics', 'provisioning'],
|
||||
}
|
||||
]
|
||||
}, {});
|
||||
|
||||
function renderSection(doc: YAML.Document, obj: Record<string, unknown>, parentNode?: YAMLSeq) {
|
||||
|
71
src/ListenerService.ts
Normal file
71
src/ListenerService.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { Server } from "http";
|
||||
import { Application, default as expressApp, Router } from "express";
|
||||
import LogWrapper from "./LogWrapper";
|
||||
|
||||
// Appserices can't be handled yet because the bot-sdk maintains control of it.
|
||||
export type ResourceName = "webhooks"|"widgets"|"metrics"|"provisioning"|"appservice";
|
||||
export const ResourceTypeArray: ResourceName[] = ["webhooks","widgets","metrics","provisioning","appservice"];
|
||||
|
||||
export interface BridgeConfigListener {
|
||||
bindAddress?: string;
|
||||
port: number;
|
||||
resources: Array<ResourceName>;
|
||||
}
|
||||
|
||||
const log = new LogWrapper("ListenerService");
|
||||
|
||||
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();
|
||||
this.listeners.push({
|
||||
config: listenerConfig,
|
||||
app,
|
||||
resourcesBound: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bindResource(resourceName: ResourceName, router: Router) {
|
||||
for (const listener of this.listeners.filter((l) => l.config.resources.includes(resourceName))) {
|
||||
log.info(`Registering ${listener.config.bindAddress || "127.0.0.1"}:${listener.config.port} for ${resourceName}`);
|
||||
listener.app.use(router);
|
||||
listener.resourcesBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
public start() {
|
||||
for (const listener of this.listeners) {
|
||||
if (listener.server) {
|
||||
throw Error('Cannot run start() twice');
|
||||
}
|
||||
if (!listener.resourcesBound) {
|
||||
continue;
|
||||
}
|
||||
const addr = listener.config.bindAddress || "127.0.0.1";
|
||||
listener.server = listener.app.listen(listener.config.port, addr);
|
||||
log.info(`Listening on ${addr}:${listener.config.port} for ${listener.config.resources.join(', ')}`)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
import { Appservice, FunctionCallContext, METRIC_MATRIX_CLIENT_FAILED_FUNCTION_CALL, METRIC_MATRIX_CLIENT_SUCCESSFUL_FUNCTION_CALL } from "matrix-bot-sdk";
|
||||
import { collectDefaultMetrics, Counter, Gauge, register, Registry } from "prom-client";
|
||||
import { BridgeConfigMetrics } from "./Config/Config";
|
||||
import { Response, default as expressApp } from "express";
|
||||
import { Response, Router } from "express";
|
||||
import LogWrapper from "./LogWrapper";
|
||||
import { Server } from "http";
|
||||
const log = new LogWrapper("Metrics");
|
||||
|
||||
export class Metrics {
|
||||
private httpServer?: Server;
|
||||
public readonly expressRouter = Router();
|
||||
|
||||
public readonly webhooksHttpRequest = new Counter({ name: "hookshot_webhooks_http_request", help: "Number of requests made to the hookshot webhooks handler", labelNames: ["path", "method"], registers: [this.registry]});
|
||||
public readonly provisioningHttpRequest = new Counter({ name: "hookshot_provisioning_http_request", help: "Number of requests made to the hookshot webhooks handler", labelNames: ["path", "method"], registers: [this.registry]});
|
||||
@ -24,6 +22,7 @@ export class Metrics {
|
||||
public readonly matrixAppserviceEvents = new Counter({ name: "matrix_appservice_events", help: "The number of events sent over the AS API", labelNames: [], registers: [this.registry]});
|
||||
|
||||
constructor(private registry: Registry = register) {
|
||||
this.expressRouter.get('/metrics', this.metricsFunc.bind(this));
|
||||
collectDefaultMetrics({
|
||||
register: this.registry
|
||||
})
|
||||
@ -79,26 +78,6 @@ export class Metrics {
|
||||
res.status(500).send('Could not fetch metrics due to an error');
|
||||
});
|
||||
}
|
||||
|
||||
public start(config: BridgeConfigMetrics, as?: Appservice) {
|
||||
if (!config.port) {
|
||||
if (!as) {
|
||||
throw Error("No metric port defined in config, and service doesn't run a appservice");
|
||||
}
|
||||
as.expressAppInstance.get('/metrics', this.metricsFunc.bind(this));
|
||||
return;
|
||||
}
|
||||
const app = expressApp();
|
||||
app.get('/metrics', this.metricsFunc.bind(this));
|
||||
this.httpServer = app.listen(config.port, config.bindAddress || "127.0.0.1");
|
||||
}
|
||||
|
||||
public async stop() {
|
||||
if (!this.httpServer) {
|
||||
return;
|
||||
}
|
||||
return new Promise<void>((res, rej) => this.httpServer?.close(err => err ? rej(err) : res()));
|
||||
}
|
||||
}
|
||||
|
||||
const singleton = new Metrics();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BridgeConfig } from "./Config/Config";
|
||||
import { Application, default as express, Request, Response } from "express";
|
||||
import { Router, default as express, Request, Response } from "express";
|
||||
import { EventEmitter } from "events";
|
||||
import { MessageQueue, createMessageQueue } from "./MessageQueue";
|
||||
import LogWrapper from "./LogWrapper";
|
||||
@ -37,14 +37,12 @@ export interface NotificationsDisableEvent {
|
||||
}
|
||||
|
||||
export class Webhooks extends EventEmitter {
|
||||
private expressApp: Application;
|
||||
public readonly expressRouter = Router();
|
||||
private queue: MessageQueue;
|
||||
private server?: Server;
|
||||
private ghWebhooks?: OctokitWebhooks;
|
||||
constructor(private config: BridgeConfig) {
|
||||
super();
|
||||
this.expressApp = express();
|
||||
this.expressApp.use((req, _res, next) => {
|
||||
this.expressRouter.use((req, _res, next) => {
|
||||
Metrics.webhooksHttpRequest.inc({path: req.path, method: req.method});
|
||||
next();
|
||||
});
|
||||
@ -56,39 +54,27 @@ export class Webhooks extends EventEmitter {
|
||||
}
|
||||
|
||||
// TODO: Move these
|
||||
this.expressApp.get("/oauth", this.onGitHubGetOauth.bind(this));
|
||||
this.expressApp.all(
|
||||
this.expressRouter.get("/oauth", this.onGitHubGetOauth.bind(this));
|
||||
this.expressRouter.all(
|
||||
'/:hookId',
|
||||
express.json({ type: ['application/json', 'application/x-www-form-urlencoded'] }),
|
||||
this.onGenericPayload.bind(this),
|
||||
);
|
||||
|
||||
this.expressApp.use(express.json({
|
||||
this.expressRouter.use(express.json({
|
||||
verify: this.verifyRequest.bind(this),
|
||||
}));
|
||||
this.expressApp.post("/", this.onPayload.bind(this));
|
||||
this.expressRouter.post("/", this.onPayload.bind(this));
|
||||
this.queue = createMessageQueue(config);
|
||||
if (this.config.jira) {
|
||||
this.expressApp.use("/jira", new JiraWebhooksRouter(this.config.jira, this.queue).getRouter());
|
||||
this.expressRouter.use("/jira", new JiraWebhooksRouter(this.config.jira, this.queue).getRouter());
|
||||
}
|
||||
this.queue = createMessageQueue(config);
|
||||
}
|
||||
|
||||
public listen() {
|
||||
const bindAddr = this.config.webhook.bindAddress || "0.0.0.0";
|
||||
this.server = this.expressApp.listen(
|
||||
this.config.webhook.port,
|
||||
bindAddr,
|
||||
);
|
||||
log.info(`Listening on http://${bindAddr}:${this.config.webhook.port}`);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this.queue.stop) {
|
||||
this.queue.stop();
|
||||
}
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Application, default as express, Request, Response } from "express";
|
||||
import express, { Router, Request, Response } from "express";
|
||||
import cors from "cors";
|
||||
import { AdminRoom } from "../AdminRoom";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
@ -7,27 +7,19 @@ import { Server } from "http";
|
||||
const log = new LogWrapper("BridgeWidgetApi");
|
||||
|
||||
export class BridgeWidgetApi {
|
||||
private app: Application;
|
||||
public readonly expressRouter: Router;
|
||||
private server?: Server;
|
||||
constructor(private adminRooms: Map<string, AdminRoom>) {
|
||||
this.app = express();
|
||||
this.app.use((req, _res, next) => {
|
||||
this.expressRouter = Router();
|
||||
this.expressRouter.use((req, _res, next) => {
|
||||
log.info(`${req.method} ${req.path} ${req.ip || ''} ${req.headers["user-agent"] || ''}`);
|
||||
next();
|
||||
});
|
||||
this.app.use('/', express.static('public'));
|
||||
this.app.use(cors());
|
||||
this.app.get('/widgetapi/:roomId/verify', this.getVerifyToken.bind(this));
|
||||
this.app.get('/widgetapi/:roomId', this.getRoomState.bind(this));
|
||||
this.app.get('/health', this.getHealth.bind(this));
|
||||
}
|
||||
|
||||
public start(port = 5000) {
|
||||
log.info(`Widget API listening on port ${port}`)
|
||||
this.server = this.app.listen(port);
|
||||
}
|
||||
public stop() {
|
||||
if (this.server) this.server.close();
|
||||
this.expressRouter.use('/', express.static('public'));
|
||||
this.expressRouter.use(cors());
|
||||
this.expressRouter.get('/widgetapi/:roomId/verify', this.getVerifyToken.bind(this));
|
||||
this.expressRouter.get('/widgetapi/:roomId', this.getRoomState.bind(this));
|
||||
this.expressRouter.get('/health', this.getHealth.bind(this));
|
||||
}
|
||||
|
||||
private async getRoomFromRequest(req: Request): Promise<AdminRoom|{error: string, statusCode: number}> {
|
||||
@ -74,4 +66,4 @@ export class BridgeWidgetApi {
|
||||
private getHealth(req: Request, res: Response) {
|
||||
res.status(200).send({ok: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { BridgeConfigProvisioning } from "../Config/Config";
|
||||
import { Application, default as express, NextFunction, Request, Response, Router } from "express";
|
||||
import { Router, default as express, NextFunction, Request, Response } from "express";
|
||||
import { ConnectionManager } from "../ConnectionManager";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
import { Server } from "http";
|
||||
import { ApiError, ErrCode, GetConnectionsResponseItem, GetConnectionTypeResponseItem } from "./api";
|
||||
import { Intent, MembershipEventContent, PowerLevelsEventContent } from "matrix-bot-sdk";
|
||||
import Metrics from "../Metrics";
|
||||
@ -15,8 +14,7 @@ const USER_ID_VALIDATOR = /@.+:.+/;
|
||||
|
||||
|
||||
export class Provisioner {
|
||||
private expressApp: Application;
|
||||
private server?: Server;
|
||||
public readonly expressRouter: Router = Router();
|
||||
constructor(
|
||||
private readonly config: BridgeConfigProvisioning,
|
||||
private readonly connMan: ConnectionManager,
|
||||
@ -28,54 +26,53 @@ export class Provisioner {
|
||||
if (!this.config.port) {
|
||||
throw Error('Missing port in provisioning config');
|
||||
}
|
||||
this.expressApp = express();
|
||||
this.expressApp.use((req, _res, next) => {
|
||||
this.expressRouter.use((req, _res, next) => {
|
||||
Metrics.provisioningHttpRequest.inc({path: req.path, method: req.method});
|
||||
next();
|
||||
});
|
||||
this.expressApp.get("/v1/health", this.getHealth);
|
||||
this.expressApp.use(this.checkAuth.bind(this));
|
||||
this.expressApp.use(express.json());
|
||||
this.expressRouter.get("/v1/health", this.getHealth);
|
||||
this.expressRouter.use(this.checkAuth.bind(this));
|
||||
this.expressRouter.use(express.json());
|
||||
// Room Routes
|
||||
this.expressApp.get(
|
||||
this.expressRouter.get(
|
||||
"/v1/connectiontypes",
|
||||
this.getConnectionTypes.bind(this),
|
||||
);
|
||||
this.expressApp.use(this.checkUserId.bind(this));
|
||||
this.expressRouter.use(this.checkUserId.bind(this));
|
||||
additionalRoutes.forEach(route => {
|
||||
this.expressApp.use(route.route, route.router);
|
||||
this.expressRouter.use(route.route, route.router);
|
||||
});
|
||||
this.expressApp.get<{roomId: string}, unknown, unknown, {userId: string}>(
|
||||
this.expressRouter.get<{roomId: string}, unknown, unknown, {userId: string}>(
|
||||
"/v1/:roomId/connections",
|
||||
this.checkRoomId.bind(this),
|
||||
(...args) => this.checkUserPermission("read", ...args),
|
||||
this.getConnections.bind(this),
|
||||
);
|
||||
this.expressApp.get<{roomId: string, connectionId: string}, unknown, unknown, {userId: string}>(
|
||||
this.expressRouter.get<{roomId: string, connectionId: string}, unknown, unknown, {userId: string}>(
|
||||
"/v1/:roomId/connections/:connectionId",
|
||||
this.checkRoomId.bind(this),
|
||||
(...args) => this.checkUserPermission("read", ...args),
|
||||
this.getConnection.bind(this),
|
||||
);
|
||||
this.expressApp.put<{roomId: string, type: string}, unknown, Record<string, unknown>, {userId: string}>(
|
||||
this.expressRouter.put<{roomId: string, type: string}, unknown, Record<string, unknown>, {userId: string}>(
|
||||
"/v1/:roomId/connections/:type",
|
||||
this.checkRoomId.bind(this),
|
||||
(...args) => this.checkUserPermission("write", ...args),
|
||||
this.putConnection.bind(this),
|
||||
);
|
||||
this.expressApp.patch<{roomId: string, connectionId: string}, unknown, Record<string, unknown>, {userId: string}>(
|
||||
this.expressRouter.patch<{roomId: string, connectionId: string}, unknown, Record<string, unknown>, {userId: string}>(
|
||||
"/v1/:roomId/connections/:connectionId",
|
||||
this.checkRoomId.bind(this),
|
||||
(...args) => this.checkUserPermission("write", ...args),
|
||||
this.patchConnection.bind(this),
|
||||
);
|
||||
this.expressApp.delete<{roomId: string, connectionId: string}, unknown, unknown, {userId: string}>(
|
||||
this.expressRouter.delete<{roomId: string, connectionId: string}, unknown, unknown, {userId: string}>(
|
||||
"/v1/:roomId/connections/:connectionId",
|
||||
this.checkRoomId.bind(this),
|
||||
(...args) => this.checkUserPermission("write", ...args),
|
||||
this.deleteConnection.bind(this),
|
||||
);
|
||||
this.expressApp.use(this.onError);
|
||||
this.expressRouter.use(this.onError);
|
||||
}
|
||||
|
||||
private checkAuth(req: Request, _res: Response, next: NextFunction) {
|
||||
@ -247,19 +244,4 @@ export class Provisioner {
|
||||
return next(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public listen() {
|
||||
const bindAddr = this.config.bindAddress || "0.0.0.0";
|
||||
this.server = this.expressApp.listen(
|
||||
this.config.port,
|
||||
bindAddr,
|
||||
);
|
||||
log.info(`Listening on http://${bindAddr}:${this.config.port}`);
|
||||
}
|
||||
|
||||
public stop() {
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user