aboutsummaryrefslogtreecommitdiff
path: root/services/docker/mail-server/relay/config.ts
diff options
context:
space:
mode:
Diffstat (limited to 'services/docker/mail-server/relay/config.ts')
-rw-r--r--services/docker/mail-server/relay/config.ts110
1 files changed, 110 insertions, 0 deletions
diff --git a/services/docker/mail-server/relay/config.ts b/services/docker/mail-server/relay/config.ts
new file mode 100644
index 0000000..ec7e7d4
--- /dev/null
+++ b/services/docker/mail-server/relay/config.ts
@@ -0,0 +1,110 @@
+import { transformProperties } from "./util.ts";
+
+export const APP_PREFIX = "crupest";
+export const APP_NAME = "mailserver";
+
+interface ConfigItemDef {
+ env: string;
+ description: string;
+ default?: string;
+ secret?: boolean;
+}
+
+export const CONFIG_DEFS = {
+ mailDomain: {
+ env: "MAIL_DOMAIN",
+ description: "the part after `@` of an address",
+ },
+ dataPath: {
+ env: "DATA_PATH",
+ description: "path to save app persistent data",
+ },
+ ldaPath: {
+ env: "LDA_PATH",
+ description: "full path of lda executable",
+ "default": "/dovecot/libexec/dovecot/dovecot-lda",
+ },
+ inboundFallback: {
+ env: "INBOUND_FALLBACK",
+ description: "comma separated addresses used as fallback receipts",
+ "default": "",
+ },
+ awsInboundPath: {
+ env: "AWS_INBOUND_PATH",
+ description: "(random set) path for aws sns",
+ },
+ awsInboundKey: {
+ env: "AWS_INBOUND_KEY",
+ description: "(random set) http header Authorization for aws sns",
+ },
+ awsRegion: { env: "AWS_REGION", description: "aws region" },
+ awsAccessKeyId: { env: "AWS_USER", description: "aws access key id" },
+ awsSecretAccessKey: {
+ env: "AWS_PASSWORD",
+ description: "aws secret access key",
+ secret: true,
+ },
+ awsMailBucket: {
+ env: "AWS_MAIL_BUCKET",
+ description: "aws s3 bucket saving raw mails",
+ secret: true,
+ },
+} as const satisfies Record<string, ConfigItemDef>;
+
+type ConfigDefs = typeof CONFIG_DEFS;
+type ConfigKey = keyof ConfigDefs;
+type ConfigMap = {
+ [K in ConfigKey]: ConfigItemDef & { value: string };
+};
+
+function getFullEnvKey(key: string): string {
+ return `${APP_PREFIX.toUpperCase()}_${APP_NAME.toUpperCase()}_${key}`;
+}
+
+function resolveAppConfigItem(def: ConfigItemDef): string {
+ const envKey = getFullEnvKey(def.env);
+ const value = Deno.env.get(envKey);
+ if (value != null) return value;
+ if (def.default != null) return def.default;
+ throw new Error(
+ `Required env ${envKey} (${def.description}) is not set.`,
+ );
+}
+
+export class Config {
+ #config = transformProperties(
+ CONFIG_DEFS,
+ (def) => ({ ...def, value: resolveAppConfigItem(def) }),
+ ) as ConfigMap;
+
+ readonly HTTP_HOST = "0.0.0.0";
+ readonly HTTP_PORT = 2345;
+ readonly SMTP_HOST = "127.0.0.1";
+ readonly SMTP_PORT = 2346;
+
+ get<K extends ConfigKey>(key: K): ConfigMap[K] {
+ return this.#config[key];
+ }
+
+ getValue(key: ConfigKey): string {
+ return this.get(key).value;
+ }
+
+ getValueList(key: ConfigKey, separator: string = ","): string[] {
+ const value = this.getValue(key);
+ if (value.length === 0) return [];
+ return value.split(separator);
+ }
+
+ [Symbol.for("Deno.customInspect")]() {
+ return Object.entries(this.#config).map(([key, item]) =>
+ `${key} [env: ${getFullEnvKey(item.env)}]: ${
+ item.secret === true ? "***" : item.value
+ }`
+ ).join("\n");
+ }
+}
+
+const config = new Config();
+export default config;
+export const getConfigValue = config.getValue.bind(config);