Skip to content

Commit 4376679

Browse files
authored
load config yaml manually, remove more references to static config (#347)
1 parent 64c26e5 commit 4376679

25 files changed

+2949
-3069
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
"version": "sed -i '/# version automated/s/[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*/'$npm_package_version'/' synapse_antispam/setup.py && git add synapse_antispam/setup.py && cat synapse_antispam/setup.py"
1919
},
2020
"devDependencies": {
21-
"@types/config": "0.0.41",
2221
"@types/crypto-js": "^4.0.2",
2322
"@types/html-to-text": "^8.0.1",
2423
"@types/humanize-duration": "^3.27.1",
24+
"@types/js-yaml": "^4.0.5",
2525
"@types/jsdom": "^16.2.11",
2626
"@types/mocha": "^9.0.0",
2727
"@types/node": "^16.7.10",
@@ -36,7 +36,6 @@
3636
"typescript-formatter": "^7.2"
3737
},
3838
"dependencies": {
39-
"config": "^3.3.6",
4039
"express": "^4.17",
4140
"html-to-text": "^8.0.0",
4241
"humanize-duration": "^3.27.1",
@@ -45,7 +44,8 @@
4544
"jsdom": "^16.6.0",
4645
"matrix-bot-sdk": "^0.5.19",
4746
"parse-duration": "^1.0.2",
48-
"shell-quote": "^1.7.3"
47+
"shell-quote": "^1.7.3",
48+
"yaml": "^2.1.1"
4949
},
5050
"engines": {
5151
"node": ">=16.0.0"

src/config.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import * as config from "config";
17+
import * as fs from "fs";
18+
import { load } from "js-yaml";
1819
import { MatrixClient } from "matrix-bot-sdk";
1920

2021
/**
@@ -168,5 +169,9 @@ const defaultConfig: IConfig = {
168169
},
169170
};
170171

171-
const finalConfig = <IConfig>Object.assign({}, defaultConfig, config);
172-
export default finalConfig;
172+
export function read(): IConfig {
173+
const content = fs.readFileSync(`./config/${process.env.NODE_ENV || 'default'}.yaml`, "utf8");
174+
const parsed = load(content);
175+
const config = {...defaultConfig, ...(parsed as object)} as IConfig;
176+
return config;
177+
}

src/health/healthz.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,29 @@ limitations under the License.
1616

1717
import * as http from "http";
1818
import { LogService } from "matrix-bot-sdk";
19+
import { IConfig } from "../config";
1920
// allowed to use the global configuration since this is only intended to be used by `src/index.ts`.
20-
import config from '../config';
2121

2222
export class Healthz {
23-
private static healthCode: number;
23+
private healthCode: number;
2424

25-
public static set isHealthy(val: boolean) {
26-
Healthz.healthCode = val ? config.health.healthz.healthyStatus : config.health.healthz.unhealthyStatus;
25+
constructor(private config: IConfig) { }
26+
27+
public set isHealthy(val: boolean) {
28+
this.healthCode = val ? this.config.health.healthz.healthyStatus : this.config.health.healthz.unhealthyStatus;
2729
}
2830

29-
public static get isHealthy(): boolean {
30-
return Healthz.healthCode === config.health.healthz.healthyStatus;
31+
public get isHealthy(): boolean {
32+
return this.healthCode === this.config.health.healthz.healthyStatus;
3133
}
3234

33-
public static listen() {
35+
public listen() {
3436
const server = http.createServer((req, res) => {
35-
res.writeHead(Healthz.healthCode);
36-
res.end(`health code: ${Healthz.healthCode}`);
37+
res.writeHead(this.healthCode);
38+
res.end(`health code: ${this.healthCode}`);
3739
});
38-
server.listen(config.health.healthz.port, config.health.healthz.address, () => {
39-
LogService.info("Healthz", `Listening for health requests on ${config.health.healthz.address}:${config.health.healthz.port}`);
40+
server.listen(this.config.health.healthz.port, this.config.health.healthz.address, () => {
41+
LogService.info("Healthz", `Listening for health requests on ${this.config.health.healthz.address}:${this.config.health.healthz.port}`);
4042
});
4143
}
4244
}

src/index.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,28 @@ import {
2323
RichConsoleLogger,
2424
SimpleFsStorageProvider
2525
} from "matrix-bot-sdk";
26-
import config from "./config";
26+
import { read as configRead } from "./config";
2727
import { Healthz } from "./health/healthz";
2828
import { Mjolnir } from "./Mjolnir";
2929
import { patchMatrixClient } from "./utils";
3030

31-
config.RUNTIME = {};
3231

33-
LogService.setLogger(new RichConsoleLogger());
34-
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
32+
(async function () {
33+
const config = configRead();
3534

36-
LogService.info("index", "Starting bot...");
35+
config.RUNTIME = {};
3736

38-
Healthz.isHealthy = false; // start off unhealthy
39-
if (config.health.healthz.enabled) {
40-
Healthz.listen();
41-
}
37+
LogService.setLogger(new RichConsoleLogger());
38+
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
39+
40+
LogService.info("index", "Starting bot...");
41+
42+
const healthz = new Healthz(config);
43+
healthz.isHealthy = false; // start off unhealthy
44+
if (config.health.healthz.enabled) {
45+
healthz.listen();
46+
}
4247

43-
(async function () {
4448
let bot: Mjolnir | null = null;
4549
try {
4650
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
@@ -63,7 +67,7 @@ if (config.health.healthz.enabled) {
6367
}
6468
try {
6569
await bot.start();
66-
Healthz.isHealthy = true;
70+
healthz.isHealthy = true;
6771
} catch (err) {
6872
console.error(`Mjolnir failed to start: ${err}`);
6973
throw err;

test/commands/UnbanBanCommandTest.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ import * as expect from "expect";
1818
import { Mjolnir } from "../../src/Mjolnir";
1919
import { DEFAULT_LIST_EVENT_TYPE } from "../../src/commands/SetDefaultBanListCommand";
2020
import { parseArguments } from "../../src/commands/UnbanBanCommand";
21-
import config from "../../src/config";
21+
import { read as configRead } from "../../src/config";
2222
import { RULE_ROOM, RULE_SERVER, RULE_USER } from "../../src/models/ListRule";
2323

2424
function createTestMjolnir(defaultShortcode: string|null = null): Mjolnir {
25+
const config = configRead();
2526
const client = {
2627
// Mock `MatrixClient.getAccountData` .
2728
getAccountData: (eventType: string): Promise<any> => {

test/integration/abuseReportTest.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ describe("Test: Reporting abuse", async () => {
3232
});
3333

3434
// Create a few users and a room.
35-
let goodUser = await newTestUser({ name: { contains: "reporting-abuse-good-user" }});
36-
let badUser = await newTestUser({ name: { contains: "reporting-abuse-bad-user" }});
35+
let goodUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-good-user" }});
36+
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-bad-user" }});
3737
let goodUserId = await goodUser.getUserId();
3838
let badUserId = await badUser.getUserId();
3939

@@ -227,13 +227,13 @@ describe("Test: Reporting abuse", async () => {
227227
});
228228

229229
// Create a moderator.
230-
let moderatorUser = await newTestUser({ name: { contains: "reporting-abuse-moderator-user" }});
230+
let moderatorUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-moderator-user" }});
231231
matrixClient().inviteUser(await moderatorUser.getUserId(), this.mjolnir.managementRoomId);
232232
await moderatorUser.joinRoom(this.mjolnir.managementRoomId);
233233

234234
// Create a few users and a room.
235-
let goodUser = await newTestUser({ name: { contains: "reacting-abuse-good-user" }});
236-
let badUser = await newTestUser({ name: { contains: "reacting-abuse-bad-user" }});
235+
let goodUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reacting-abuse-good-user" }});
236+
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reacting-abuse-bad-user" }});
237237
let goodUserId = await goodUser.getUserId();
238238
let badUserId = await badUser.getUserId();
239239

test/integration/banListTest.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe("Test: Updating the PolicyList", function() {
3030
it("Calculates what has changed correctly.", async function() {
3131
this.timeout(10000);
3232
const mjolnir: Mjolnir = this.mjolnir!
33-
const moderator = await newTestUser({ name: { contains: "moderator" } });
33+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
3434
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
3535
const banList = new PolicyList(banListId, banListId, mjolnir.client);
3636
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@@ -121,7 +121,7 @@ describe("Test: Updating the PolicyList", function() {
121121
it("Will remove rules with old types when they are 'soft redacted' with a different but more recent event type.", async function() {
122122
this.timeout(3000);
123123
const mjolnir: Mjolnir = this.mjolnir!
124-
const moderator = await newTestUser({ name: { contains: "moderator" }} );
124+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }} );
125125
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
126126
const banList = new PolicyList(banListId, banListId, mjolnir.client);
127127
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@@ -142,7 +142,7 @@ describe("Test: Updating the PolicyList", function() {
142142
})
143143
it("A rule of the most recent type won't be deleted when an old rule is deleted for the same entity.", async function() {
144144
const mjolnir: Mjolnir = this.mjolnir!
145-
const moderator = await newTestUser({ name: { contains: "moderator" } });
145+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
146146
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
147147
const banList = new PolicyList(banListId, banListId, mjolnir.client);
148148
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@@ -232,7 +232,7 @@ describe('Test: ACL updates will batch when rules are added in succession.', fun
232232
it('Will batch ACL updates if we spam rules into a PolicyList', async function() {
233233
const mjolnir: Mjolnir = this.mjolnir!
234234
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
235-
const moderator = await newTestUser({ name: { contains: "moderator" } });
235+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
236236
moderator.joinRoom(this.mjolnir.client.managementRoomId);
237237
const mjolnirId = await mjolnir.client.getUserId();
238238

@@ -300,7 +300,7 @@ describe('Test: unbaning entities via the PolicyList.', function() {
300300
it('Will remove rules that have legacy types', async function() {
301301
const mjolnir: Mjolnir = this.mjolnir!
302302
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
303-
const moderator = await newTestUser({ name: { contains: "moderator" } });
303+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
304304
this.moderator = moderator;
305305
await moderator.joinRoom(mjolnir.managementRoomId);
306306
const mjolnirId = await mjolnir.client.getUserId();
@@ -372,7 +372,7 @@ describe('Test: should apply bans to the most recently active rooms first', func
372372
this.timeout(180000)
373373
const mjolnir: Mjolnir = this.mjolnir!
374374
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
375-
const moderator = await newTestUser({ name: { contains: "moderator" } });
375+
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
376376
moderator.joinRoom(mjolnir.managementRoomId);
377377
const mjolnirId = await mjolnir.client.getUserId();
378378

test/integration/clientHelper.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { HmacSHA1 } from "crypto-js";
22
import { getRequestFn, LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
3-
import config from '../../src/config';
43

54
const REGISTRATION_ATTEMPTS = 10;
65
const REGISTRATION_RETRY_BASE_DELAY_MS = 100;
@@ -16,8 +15,8 @@ const REGISTRATION_RETRY_BASE_DELAY_MS = 100;
1615
* @param admin True to make the user an admin, false otherwise.
1716
* @returns The response from synapse.
1817
*/
19-
export async function registerUser(username: string, displayname: string, password: string, admin: boolean): Promise<void> {
20-
let registerUrl = `${config.homeserverUrl}/_synapse/admin/v1/register`
18+
export async function registerUser(homeserver: string, username: string, displayname: string, password: string, admin: boolean): Promise<void> {
19+
let registerUrl = `${homeserver}/_synapse/admin/v1/register`
2120
const data: {nonce: string} = await new Promise((resolve, reject) => {
2221
getRequestFn()({uri: registerUrl, method: "GET", timeout: 60000}, (error: any, response: any, resBody: any) => {
2322
error ? reject(error) : resolve(JSON.parse(resBody))
@@ -81,7 +80,7 @@ export type RegistrationOptions = {
8180
*
8281
* @returns A string that is both the username and password of a new user.
8382
*/
84-
async function registerNewTestUser(options: RegistrationOptions) {
83+
async function registerNewTestUser(homeserver: string, options: RegistrationOptions) {
8584
do {
8685
let username;
8786
if ("exact" in options.name) {
@@ -90,7 +89,7 @@ async function registerNewTestUser(options: RegistrationOptions) {
9089
username = `mjolnir-test-user-${options.name.contains}${Math.floor(Math.random() * 100000)}`
9190
}
9291
try {
93-
await registerUser(username, username, username, Boolean(options.isAdmin));
92+
await registerUser(homeserver, username, username, username, Boolean(options.isAdmin));
9493
return username;
9594
} catch (e) {
9695
if (e?.body?.errcode === 'M_USER_IN_USE') {
@@ -113,13 +112,13 @@ async function registerNewTestUser(options: RegistrationOptions) {
113112
*
114113
* @returns A new `MatrixClient` session for a unique test user.
115114
*/
116-
export async function newTestUser(options: RegistrationOptions): Promise<MatrixClient> {
117-
const username = await registerNewTestUser(options);
118-
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
115+
export async function newTestUser(homeserver: string, options: RegistrationOptions): Promise<MatrixClient> {
116+
const username = await registerNewTestUser(homeserver, options);
117+
const pantalaimon = new PantalaimonClient(homeserver, new MemoryStorageProvider());
119118
const client = await pantalaimon.createClientWithCredentials(username, username);
120119
if (!options.isThrottled) {
121120
let userId = await client.getUserId();
122-
await overrideRatelimitForUser(userId);
121+
await overrideRatelimitForUser(homeserver, userId);
123122
}
124123
return client;
125124
}
@@ -130,20 +129,20 @@ let _globalAdminUser: MatrixClient;
130129
* Get a client that can perform synapse admin API actions.
131130
* @returns A client logged in with an admin user.
132131
*/
133-
async function getGlobalAdminUser(): Promise<MatrixClient> {
132+
async function getGlobalAdminUser(homeserver: string): Promise<MatrixClient> {
134133
// Initialize global admin user if needed.
135134
if (!_globalAdminUser) {
136135
const USERNAME = "mjolnir-test-internal-admin-user";
137136
try {
138-
await registerUser(USERNAME, USERNAME, USERNAME, true);
137+
await registerUser(homeserver, USERNAME, USERNAME, USERNAME, true);
139138
} catch (e) {
140139
if (e.isAxiosError && e?.response?.data?.errcode === 'M_USER_IN_USE') {
141140
// Then we've already registered the user in a previous run and that is ok.
142141
} else {
143142
throw e;
144143
}
145144
}
146-
_globalAdminUser = await new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider()).createClientWithCredentials(USERNAME, USERNAME);
145+
_globalAdminUser = await new PantalaimonClient(homeserver, new MemoryStorageProvider()).createClientWithCredentials(USERNAME, USERNAME);
147146
}
148147
return _globalAdminUser;
149148
}
@@ -152,8 +151,8 @@ async function getGlobalAdminUser(): Promise<MatrixClient> {
152151
* Disable ratelimiting for this user in Synapse.
153152
* @param userId The user to disable ratelimiting for, has to include both the server part and local part.
154153
*/
155-
export async function overrideRatelimitForUser(userId: string) {
156-
await (await getGlobalAdminUser()).doRequest("POST", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null, {
154+
export async function overrideRatelimitForUser(homeserver: string, userId: string) {
155+
await (await getGlobalAdminUser(homeserver)).doRequest("POST", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null, {
157156
"messages_per_second": 0,
158157
"burst_count": 0
159158
});
@@ -163,8 +162,8 @@ export async function overrideRatelimitForUser(userId: string) {
163162
* Put back the default ratelimiting for this user in Synapse.
164163
* @param userId The user to use default ratelimiting for, has to include both the server part and local part.
165164
*/
166-
export async function resetRatelimitForUser(userId: string) {
167-
await (await getGlobalAdminUser()).doRequest("DELETE", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null);
165+
export async function resetRatelimitForUser(homeserver: string, userId: string) {
166+
await (await getGlobalAdminUser(homeserver)).doRequest("DELETE", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null);
168167
}
169168

170169

0 commit comments

Comments
 (0)