Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/database/settings/server-settings.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import LRU from "lru-cache";67import getLogger from "@cocalc/backend/logger";8import getPool from "@cocalc/database/pool";9import type { PostgreSQL } from "@cocalc/database/postgres/types";10import { PassportStrategyDB } from "@cocalc/database/settings/auth-sso-types";11import { callback2 as cb2 } from "@cocalc/util/async-utils";12import { SERVER_SETTINGS_ENV_PREFIX } from "@cocalc/util/consts";13import { EXTRAS } from "@cocalc/util/db-schema/site-settings-extras";14import {15AllSiteSettingsKeys,16AllSiteSettingsCached as ServerSettings,17} from "@cocalc/util/db-schema/types";18import { site_settings_conf as CONF } from "@cocalc/util/schema";19export type { ServerSettings };2021const L = getLogger("server:server-settings");2223// We're just using this to cache this result for a **few seconds**.24const CACHE_TIME_SECONDS = process.env.NODE_ENV == "development" ? 3 : 15;25type CacheKeys = "server-settings" | "passports";26// TODO add something for the passports data type?27const cache = new LRU<CacheKeys, ServerSettings | PassportStrategyDB[]>({28max: 10,29ttl: 1000 * CACHE_TIME_SECONDS,30});31const KEY: CacheKeys = "server-settings";3233export function resetServerSettingsCache() {34cache.clear();35}3637export function getPassportsCached(): PassportStrategyDB[] | undefined {38return cache.get("passports") as PassportStrategyDB[] | undefined;39}4041export function setPassportsCached(val: PassportStrategyDB[]) {42return cache.set("passports", val);43}4445export async function getServerSettings(): Promise<ServerSettings> {46if (cache.has(KEY)) {47return cache.get(KEY)! as ServerSettings; // can't be null48}49const pool = getPool();50const { rows } = await pool.query("SELECT name, value FROM server_settings");5152const settings: ServerSettings = { _timestamp: Date.now() };5354const raw: { [key in AllSiteSettingsKeys]?: string } = {};55for (const row of rows) {56raw[row.name] = row.value;57}5859// process values, including any post-processing.60for (const row of rows) {61const { name, value } = row;62const spec = CONF[name] ?? EXTRAS[name];63// we only process values we know64if (spec == null) continue;65const toVal = spec.to_val;66settings[name] = toVal != null ? toVal(value, raw) : value;67}68// set default values for missing keys69for (const config of [EXTRAS, CONF]) {70for (const key in config) {71if (settings[key] == null) {72const spec = config[key];73settings[key] =74spec?.to_val != null ? spec.to_val(spec.default, raw) : spec.default;75}76}77}7879cache.set(KEY, settings);80return settings;81}8283/*84This stores environment variables for server settings in the DB to make the life of an admin easier.85e.g. COCALC_SETTING_DNS, COCALC_SETTING_EMAIL_SMTP_SERVER, COCALC_SETTING_EMAIL_SMTP_PASSWORD, ...86Loaded once at startup, right after configuring the db schema, see hub/hub.ts.87*/88export async function load_server_settings_from_env(89db: PostgreSQL,90): Promise<void> {91const PREFIX = SERVER_SETTINGS_ENV_PREFIX;92L.debug("load_server_settings_from_env variables prefixed by ", PREFIX);93// reset all readonly values94await db.async_query({95query: "UPDATE server_settings",96set: { readonly: false },97where: ["1=1"], // otherwise there is an exception about not restricting the query98});99// now, check if there are any we know of100for (const config of [EXTRAS, CONF]) {101for (const key in config) {102const envvar = `${PREFIX}_${key.toUpperCase()}`;103const envval = process.env[envvar];104if (envval == null) continue;105// ATTN do not expose the value, could be a password106L.debug(`picking up $${envvar} and saving it in the database`);107108// check validity109const valid = (CONF[key] ?? EXTRAS[key])?.valid;110if (valid != null) {111if (Array.isArray(valid) && !valid.includes(envval)) {112throw new Error(113`The value of $${envvar} is invalid. allowed are ${valid}.`,114);115} else if (typeof valid == "function" && !valid(envval)) {116throw new Error(117`The validation function rejected the value of $${envvar}.`,118);119}120}121122await cb2(db.set_server_setting, {123name: key,124value: envval,125readonly: true,126});127}128}129}130131132