mjolnir/src/config.ts
Gnuxie 333c55e18c
Config fixes (#432)
* Use the npm package `config` to load the config.

This is what was used prior to https://github.com/matrix-org/mjolnir/pull/347.
It was a nice idea motivated to drop a dependency that was confusing.
It was just never followed through and was underestimated how much disruption it would cause.
It was also believed that the library would mean there could only ever be one global copy of the config,
It was followed up by:
https://github.com/matrix-org/mjolnir/pull/369
https://github.com/matrix-org/mjolnir/pull/357
https://github.com/matrix-org/mjolnir/pull/429
https://github.com/matrix-org/mjolnir/pull/397/files
https://github.com/matrix-org/mjolnir/issues/365

For simplicity sake I am reinstating the library.
The practice of loading default.yaml by default is also dangerous
and has led to issues multiple times in #mjolnir:matrix.org.
It is a sample and not a default.

In a following commit I will be adding the ability to specify the
config to use from the cli.

* Allow config to be specified with an explicit cli argument.

* Update doc to transition away from old config handling
2022-11-23 10:55:22 +00:00

202 lines
5.6 KiB
TypeScript

/*
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as fs from "fs";
import { load } from "js-yaml";
import { MatrixClient } from "matrix-bot-sdk";
import Config from "config";
/**
* The configuration, as read from production.yaml
*
* See file default.yaml for the documentation on individual options.
*/
// The object is magically generated by external lib `config`
// from the file specified by `NODE_ENV`, e.g. production.yaml
// or harness.yaml.
export interface IConfig {
homeserverUrl: string;
rawHomeserverUrl: string;
accessToken: string;
pantalaimon: {
use: boolean;
username: string;
password: string;
};
dataPath: string;
acceptInvitesFromSpace: string;
autojoinOnlyIfManager: boolean;
recordIgnoredInvites: boolean;
managementRoom: string;
verboseLogging: boolean;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
syncOnStartup: boolean;
verifyPermissionsOnStartup: boolean;
noop: boolean;
protectedRooms: string[]; // matrix.to urls
fasterMembershipChecks: boolean;
automaticallyRedactForReasons: string[]; // case-insensitive globs
protectAllJoinedRooms: boolean;
/**
* Backgrounded tasks: number of milliseconds to wait between the completion
* of one background task and the start of the next one.
*/
backgroundDelayMS: number;
pollReports: boolean;
/**
* Whether or not new reports, received either by webapi or polling,
* should be printed to our managementRoom.
*/
displayReports: boolean;
admin?: {
enableMakeRoomAdminCommand?: boolean;
}
commands: {
allowNoPrefix: boolean;
additionalPrefixes: string[];
confirmWildcardBan: boolean;
};
protections: {
wordlist: {
words: string[];
minutesBeforeTrusting: number;
};
};
health: {
healthz: {
enabled: boolean;
port: number;
address: string;
endpoint: string;
healthyStatus: number;
unhealthyStatus: number;
};
};
web: {
enabled: boolean;
port: number;
address: string;
abuseReporting: {
enabled: boolean;
}
ruleServer?: {
enabled: boolean;
}
}
/**
* Config options only set at runtime. Try to avoid using the objects
* here as much as possible.
*/
RUNTIME: {
client?: MatrixClient;
};
}
const defaultConfig: IConfig = {
homeserverUrl: "http://localhost:8008",
rawHomeserverUrl: "http://localhost:8008",
accessToken: "NONE_PROVIDED",
pantalaimon: {
use: false,
username: "",
password: "",
},
dataPath: "/data/storage",
acceptInvitesFromSpace: '!noop:example.org',
autojoinOnlyIfManager: false,
recordIgnoredInvites: false,
managementRoom: "!noop:example.org",
verboseLogging: false,
logLevel: "INFO",
syncOnStartup: true,
verifyPermissionsOnStartup: true,
noop: false,
protectedRooms: [],
fasterMembershipChecks: false,
automaticallyRedactForReasons: ["spam", "advertising"],
protectAllJoinedRooms: false,
backgroundDelayMS: 500,
pollReports: false,
displayReports: true,
commands: {
allowNoPrefix: false,
additionalPrefixes: [],
confirmWildcardBan: true,
},
protections: {
wordlist: {
words: [],
minutesBeforeTrusting: 20
}
},
health: {
healthz: {
enabled: false,
port: 8080,
address: "0.0.0.0",
endpoint: "/healthz",
healthyStatus: 200,
unhealthyStatus: 418,
},
},
web: {
enabled: false,
port: 8080,
address: "localhost",
abuseReporting: {
enabled: false,
},
ruleServer: {
enabled: false,
},
},
// Needed to make the interface happy.
RUNTIME: {
},
};
/**
* Grabs an explicit path provided for mjolnir's config from an arguments vector if provided, otherwise returns undefined.
* @param argv An arguments vector sourced from `process.argv`.
* @returns The path if one was provided or undefined.
*/
function configPathFromArguments(argv: string[]): undefined|string {
const configOptionIndex = argv.findIndex(arg => arg === "--mjolnir-config");
if (configOptionIndex > 0) {
const configOptionPath = argv.at(configOptionIndex + 1);
if (!configOptionPath) {
throw new Error("No path provided with option --mjolnir-config");
}
return configOptionPath;
} else {
return;
}
}
export function read(): IConfig {
const explicitConfigPath = configPathFromArguments(process.argv);
if (explicitConfigPath) {
const content = fs.readFileSync(explicitConfigPath, "utf8");
const parsed = load(content);
return Config.util.extendDeep({}, defaultConfig, parsed);
} else {
const config = Config.util.extendDeep({}, defaultConfig, Config.util.toObject()) as IConfig;
return config;
}
}