From 147473ebdb1828df4949f88ae4aef8f50e754c98 Mon Sep 17 00:00:00 2001 From: tecc Date: Fri, 12 Aug 2022 06:24:18 +0200 Subject: [PATCH] feat(config): Add yaml configuration support + Notea now accepts a CONFIG_FILE environment variable, which supports all the options that the previous environment configuration provided. The equivalent of each option can be found in config/compatibility.yml. --- config/compatibility.yml | 26 +++++++++++ libs/server/config.ts | 98 ++++++++++++++++++++++++++++++++++++++++ libs/shared/env.ts | 1 + 3 files changed, 125 insertions(+) create mode 100644 config/compatibility.yml create mode 100644 libs/server/config.ts diff --git a/config/compatibility.yml b/config/compatibility.yml new file mode 100644 index 0000000..3006ef1 --- /dev/null +++ b/config/compatibility.yml @@ -0,0 +1,26 @@ +# Notea Configuration File (Compatibility version) +# ------------------------------------------------ +# NOTE: Environment variables override file configuration values in most cases + +# Authentication configuration +auth: + # Type of configuration + # none: No authentication, anyone can view/edit (same as DISABLE_PASSWORD) + # basic: Basic authentication; a password. + type: basic + # PLANNED: Multiple users and usernames + password: 123 + +# Store configuration +store: + # type is unused for now + type: s3 + # endpoint is the same as STORE_END_POINT; must be specified if env doesn't + endpoint: http://localhost:9000 + # accessKey is the same as STORE_ACCESS_KEY; must be specified if env doesn't + accessKey: YOUR_ACCESS_KEY + # secretKey is the same as STORE_SECRET_KEY; must be specified if env doesn't + secretKey: YOUR_SECRET_KEY + # forcePathStyle is the same as STORE_FORCE_PATH_STYLE; defaults to false + +baseUrl: \ No newline at end of file diff --git a/libs/server/config.ts b/libs/server/config.ts new file mode 100644 index 0000000..f176fca --- /dev/null +++ b/libs/server/config.ts @@ -0,0 +1,98 @@ +import yaml from 'js-yaml'; +import { getEnv } from "libs/shared/env"; +import { existsSync, readFileSync } from "fs"; + +export type BasicUser = { username: string; password: string }; +export type BasicAuthConfiguration = + { type: 'basic' } + & (Omit | BasicUser | { users: BasicUser[] }) +export type AuthConfiguration = { type: 'none' } | BasicAuthConfiguration; + +export interface S3StoreConfiguration { + accessKey: string; + secretKey: string; + bucket: string; + endpoint: string; + region: string; + forcePathStyle: boolean; + prefix: string; +} + +export type StoreConfiguration = S3StoreConfiguration; + +export interface Configuration { + auth: AuthConfiguration; + store: StoreConfiguration; + baseUrl?: string; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let loaded: Configuration | undefined = undefined; + +export function loadConfig() { + const configFile = String(getEnv('CONFIG_FILE', './notea.yml')); + + let baseConfig: Configuration = {} as Configuration; + if (existsSync(configFile)) { + const data = readFileSync(configFile, 'utf-8'); + baseConfig = yaml.load(data) as Configuration; + + } + + const disablePassword = getEnv("DISABLE_PASSWORD", undefined); + + let auth: AuthConfiguration; + if (disablePassword === undefined || !disablePassword) { + const envPassword = getEnv("PASSWORD", undefined, false); + if (baseConfig.auth === undefined) { + if (envPassword === undefined) { + throw new Error("Authentication undefined"); + } else { + auth = { + type: 'basic', + password: envPassword + } + } + } else { + auth = baseConfig.auth; + if (envPassword !== undefined) { + throw new Error("Cannot specify PASSWORD when auth config section is present") + } + } + + } else { + auth = { type: 'none' }; + } + + let store: StoreConfiguration; + + if (!baseConfig.store) { + store = {} as StoreConfiguration; + } else { + store = baseConfig.store; + } + // for now, this works + { + store.accessKey = getEnv("STORE_ACCESS_KEY", store.accessKey, !store.accessKey).toString(); + store.secretKey = getEnv("STORE_SECRET_KEY", store.secretKey, !store.secretKey).toString(); + store.bucket = getEnv("STORE_BUCKET", store.bucket ?? "notea", false).toString(); + store.forcePathStyle = getEnv("STORE_FORCE_PATH_STYLE", store.forcePathStyle ?? false, !store.forcePathStyle); + store.endpoint = getEnv("STORE_END_POINT", store.endpoint, !store.endpoint); + store.region = getEnv("STORE_REGION", store.region ?? 'us-east-1', false).toString(); + store.prefix = getEnv("STORE_PREFIX", store.prefix ?? '', false); + } + + loaded = { + auth, + store, + baseUrl: getEnv("BASE_URL") + }; +} + +export function config(): Configuration { + if (!loaded) { + loadConfig(); + } + + return loaded as Configuration; +} \ No newline at end of file diff --git a/libs/shared/env.ts b/libs/shared/env.ts index 71df5d2..a9650c5 100644 --- a/libs/shared/env.ts +++ b/libs/shared/env.ts @@ -12,6 +12,7 @@ type AllowedEnvs = | 'DIRECT_RESPONSE_ATTACHMENT' | 'IS_DEMO' | 'STORE_PREFIX' + | 'CONFIG_FILE' export function getEnv( env: AllowedEnvs,