Skip to content

Commit

Permalink
Allow running the webserver portion standalone. (#184)
Browse files Browse the repository at this point in the history
* Update packages + add missing packages

* Drop postcss in favour of sass-loader

* Update lockfile

* Add support for specifying a "runmode" + split out web routes

* Explicitly use qs package as webpack5 does not provide a polyfill

* Replace 7 year old string-template library with a simple typed implementation

* Ensure we build the web typescript with a DOM build config

* Fix run check
  • Loading branch information
Half-Shot authored Nov 28, 2023
1 parent 016d55a commit d20c012
Show file tree
Hide file tree
Showing 18 changed files with 1,059 additions and 2,607 deletions.
3 changes: 3 additions & 0 deletions config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,6 @@ ircBridge: null

# The allowed channel prefix of the bot
#channelPrefix: '#conference-'

templatesPath: ./srv
runMode: normal
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testTimeout: 30000,
};
35 changes: 18 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"private": true,
"scripts": {
"start:bot": "yarn build:jsonschema && yarn build:ts && node lib/index.js",
"start:bot": "yarn build:jsonschema && yarn build:ts && node --enable-source-maps lib/index.js",
"start:web": "webpack serve",
"watch:web": "webpack --watch",
"build": "yarn build:jsonschema && yarn build:ts && yarn build:web",
Expand All @@ -23,10 +23,9 @@
"test:e2e": "jest spec/"
},
"dependencies": {
"@tsconfig/node18": "^1.0.1",
"@tsconfig/node18": "^18.2.2",
"await-lock": "^2.1.0",
"config": "^3.3.3",
"escape-html": "^1.0.3",
"express": "^4.17.1",
"fast-xml-parser": "^3.17.6",
"hls.js": "^0.14.17",
Expand All @@ -39,27 +38,29 @@
"moment": "^2.29.4",
"node-fetch": "^2.6.1",
"pg": "^8.9.0",
"postcss-preset-env": "^6.7.0",
"qs": "^6.11.2",
"rfc4648": "^1.4.0",
"string-template": "^1.0.0"
"xss": "^1.0.14"
},
"devDependencies": {
"@types/jest": "^29.2.5",
"@types/node": "^18",
"@types/pg": "^7.14.7",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.8.1",
"homerunner-client": "^0.0.6",
"html-webpack-plugin": "^4.5.1",
"jest": "^29.7.0",
"html-webpack-plugin": "^5.5.3",
"jest": "^29.3.1",
"json-schema-to-typescript": "^11.0.2",
"postcss-loader": "^4.1.0",
"style-loader": "^2.0.0",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.12.1",
"webpack-cli": "^4.3.1",
"webpack-dev-server": "^3.11.1"
"sass": "^1.69.0",
"sass-loader": "^13.3.2",
"style-loader": "^3.3.3",
"ts-jest": "^29.0.3",
"ts-loader": "^9.4.4",
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"yaml": "^2.3.2"
}
}
10 changes: 0 additions & 10 deletions postcss.config.js

This file was deleted.

7 changes: 4 additions & 3 deletions spec/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { E2ESetupTestTimeout, E2ETestEnv } from "./util/e2e-test";
import { describe, it, beforeEach, afterEach, expect } from "@jest/globals";


describe('Basic test setup', () => {
let testEnv: E2ETestEnv;
beforeEach(async () => {
testEnv = await E2ETestEnv.createTestEnv({
fixture: 'basic-conference',
});
const welcomeMsg = testEnv.waitForMessage();
await testEnv.setUp();
console.log((await welcomeMsg).event.content.body.startsWith('WECOME!'));
}, E2ESetupTestTimeout);
afterEach(() => {
return testEnv?.tearDown();
Expand All @@ -25,7 +26,7 @@ describe('Basic test setup', () => {
const waitForFinish = new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error(
`Build incomplete. spaceBuild: ${spaceBuilt}, supportRoomsBuilt: ${supportRoomsBuilt}, conferenceBuilt: ${conferenceBuilt}`
)), 5000);
)), 30000);
testEnv.adminClient.on('room.message', (_, event) => {
if (event.content.body.includes("Your conference's space is at")) {
spaceBuilt = true;
Expand All @@ -44,5 +45,5 @@ describe('Basic test setup', () => {
await testEnv.sendAdminCommand('!conference build');
await waitForFinish;
// TODO: Now test that all the expected rooms are there.
}, 7000);
});
});
43 changes: 36 additions & 7 deletions spec/util/e2e-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ComplementHomeServer, createHS, destroyHS } from "./homerunner";
import { MatrixClient, PowerLevelsEventContent, RoomEvent, TextualMessageEventContent } from "matrix-bot-sdk";
import dns from 'node:dns';
import { mkdtemp, rm } from "node:fs/promises";
import { IConfig } from "../../src/config";
import { IConfig, RunMode } from "../../src/config";
import { ConferenceBot } from "../../src/index";
import path from "node:path";

Expand Down Expand Up @@ -134,7 +134,7 @@ export class E2ETestMatrixClient extends MatrixClient {
}

export class E2ETestEnv {
static async createTestEnv(opts): Promise<E2ETestEnv> {
static async createTestEnv(opts: Opts): Promise<E2ETestEnv> {
const workerID = parseInt(process.env.JEST_WORKER_ID ?? '0');
const { matrixLocalparts, config: providedConfig } = opts;
const tmpDir = await mkdtemp('confbot-test');
Expand All @@ -155,8 +155,20 @@ export class E2ETestEnv {
// Configure JSON schedule
const scheduleDefinition = path.resolve(__dirname, '..', 'fixtures', opts.fixture + ".json");

const config = {
...providedConfig,
const config: IConfig = {
livestream: {
auditoriumUrl: "https://stream.example.org/conference/hls/{id}.m3u8",
talkUrl: "https://stream.example.org/conference-talks/hls/{slug}.m3u8",
hybridUrl: "https://stream.example.org/conference-hybrids/hls/{jitsi}.m3u8",
scheduleUrl: "https://example.org/?theme=$theme&room={audId}",
jitsiDomain: "jitsi.riot.im",
widgetAvatar: "mxc://example.org/mediaid",
onpublish: {
rtmpHostnameTemplate: "{squishedAudId}.example.org",
rtmpUrlTemplate: "rtmp://{hostname}/stream/{saltedHash}",
salt: "change me",
}
},
conference: {
id: 'test-conf',
name: 'Test Conf',
Expand All @@ -165,15 +177,22 @@ export class E2ETestEnv {
coordinators: `#coordinators:${homeserver.domain}`,
specialInterest: `#specialInterest:${homeserver.domain}`,
},
timezone: 'Europe/Brussels',
lookaheadMinutes: 5,
existingInterestRooms: {},
prefixes: {
auditoriumRooms: ["D."],
interestRooms: ["S.", "B."],
aliases: "",
displayNameSuffixes: {},
suffixes: {},
qaAuditoriumRooms: [],
physicalAuditoriumRooms: [],
nameOverrides: {},
},
schedule: {
backend: 'json',
database: undefined,
scheduleDefinition,
},
subspaces: {
Expand All @@ -186,6 +205,9 @@ export class E2ETestEnv {
},
moderatorUserId: `@modbot:${homeserver.domain}`,
webserver: {
address: '0.0.0.0',
port: 0,
publicBaseUrl: '/feh',
additionalAssetsPath: '/dev/null'
},
ircBridge: null,
Expand All @@ -194,7 +216,10 @@ export class E2ETestEnv {
userId: confBotOpts.userId,
dataPath: tmpDir,
managementRoom: mgmntRoom,
} as IConfig;
templatesPath: '/dev/null',
mode: RunMode.normal,
...providedConfig,
};
const conferenceBot = await ConferenceBot.start(config);
return new E2ETestEnv(homeserver, conferenceBot, adminUser.client, opts, tmpDir, config);
}
Expand All @@ -219,8 +244,8 @@ export class E2ETestEnv {
await rm(this.dataDir, { recursive: true, force: true })
}

public async sendAdminCommand(cmd: string) {
const response = new Promise<{roomId: string, event: RoomEvent<TextualMessageEventContent>}>((resolve, reject) => {
public async waitForMessage() {
return new Promise<{roomId: string, event: RoomEvent<TextualMessageEventContent>}>((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("Timed out waiting for admin response")), 5000);
this.adminClient.on('room.message', (roomId, event) => {
if (event.sender === this.config.userId) {
Expand All @@ -229,6 +254,10 @@ export class E2ETestEnv {
}
});
});
}

public async sendAdminCommand(cmd: string) {
const response = this.waitForMessage();
await this.adminClient.sendText(this.config.managementRoom, cmd);
return response;
}
Expand Down
26 changes: 26 additions & 0 deletions spec/webserver.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { AddressInfo } from "net";
import { RunMode } from "../src/config";
import { E2ESetupTestTimeout, E2ETestEnv } from "./util/e2e-test";
import { describe, it, beforeEach, afterEach, expect } from "@jest/globals";
import * as fetch from "node-fetch";

describe('Webserver', () => {
let testEnv: E2ETestEnv;
beforeEach(async () => {
testEnv = await E2ETestEnv.createTestEnv({
fixture: 'basic-conference',
config: {
mode: RunMode.webserver,
}
});
await testEnv.setUp();
}, E2ESetupTestTimeout);
afterEach(() => {
return testEnv?.tearDown();
});
it('should start up successfully', async () => {
const serverAddress = testEnv.confBot.webServer?.address() as AddressInfo;
const req = await fetch(`http://${serverAddress.address}:${serverAddress.port}/healthz`, {});
expect(req.status).toBe(200);
});
});
4 changes: 2 additions & 2 deletions src/LogProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ limitations under the License.

import { LogLevel, LogService, TextualMessageEventContent } from "matrix-bot-sdk";
import { replaceRoomIdsWithPills } from "./utils";
import * as htmlEscape from "escape-html";
import { escapeHtml } from "xss";
import { ConferenceMatrixClient } from "./ConferenceMatrixClient";

const levelToFn = {
Expand All @@ -41,7 +41,7 @@ export async function logMessage(level: LogLevel, module: string, message: strin

let evContent: TextualMessageEventContent = {
body: message,
formatted_body: htmlEscape(message),
formatted_body: escapeHtml(message),
msgtype: "m.notice",
format: "org.matrix.custom.html",
};
Expand Down
21 changes: 18 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@ limitations under the License.
import * as config from "config";
import { IRCBridgeOpts } from "./IRCBridge";

export enum RunMode {
normal = "normal",
webserver = "webserver",
}

export interface IConfig {
homeserverUrl: string;
accessToken: string;
userId: string;
dataPath: string;
managementRoom: string;
idServerDomain: string;
idServerBrand: string;
idServerDomain?: string;
idServerBrand?: string;
moderatorUserId: string;
livestream: {
auditoriumUrl: string;
Expand Down Expand Up @@ -70,6 +75,10 @@ export interface IConfig {
};
};
ircBridge: IRCBridgeOpts | null;

templatesPath: string;

mode: RunMode;
}

export interface IPrefixConfig {
Expand Down Expand Up @@ -126,4 +135,10 @@ export interface IPentaDbConfig {
schedulePostBufferSeconds: number;
}

export default <IConfig>config;
const liveConfig: IConfig = {
...config,
mode: process.env.CONF_RUN_MODE ?? RunMode.normal,
templatesPath: process.env.CONF_TEMPLATES_PATH ?? config.templatesPath,
}

export default <IConfig>liveConfig;
Loading

0 comments on commit d20c012

Please sign in to comment.