import LRU from "lru-cache" ; import { AllSiteSettingsCached as ServerSettings } from "@cocalc/util/db-schema/types" ; import { EXTRAS } from "@cocalc/util/db-schema/site-settings-extras" ; import { site_settings_conf as CONF } from "@cocalc/util/schema" ; import { SERVER_SETTINGS_ENV_PREFIX } from "@cocalc/util/consts" ; import getPool from "@cocalc/database/pool" ; import { callback2 as cb2 } from "@cocalc/util/async-utils" ; import type { PostgreSQL } from "@cocalc/database/postgres/types" ; import getLogger from "@cocalc/backend/logger" ; const L = getLogger ( "server:server-settings" ) ; const CACHE_TIME_SECONDS = process . env . NODE_ENV == "development" ? 3 : 15 ; type CacheKeys = "server-settings" | "passports" ; const cache = new LRU < CacheKeys , ServerSettings > ( { max : 10 , maxAge : 1000 * CACHE_TIME_SECONDS , } ) ; const KEY = "server-settings" ; export function resetServerSettingsCache ( ) { cache . reset ( ) ; } export function getPassportsCached ( ) { return cache . get ( "passports" ) ; } export function setPassportsCached ( val ) { return cache . set ( "passports" , val ) ; } export async function getServerSettings ( ) { if ( cache . has ( KEY ) ) { return cache . get ( KEY ) ! ; } const pool = getPool ( ) ; const { rows } = await pool . query ( "SELECT name, value FROM server_settings" ) ; const settings = { _timestamp : Date . now ( ) } ; for ( const row of rows ) { const { name , value } = row ; const toVal = ( CONF [ name ] ?? EXTRAS [ name ] ) ?. to_val ; settings [ name ] = toVal != null ? toVal ( value ) : value ; } for ( const config of [ EXTRAS , CONF ] ) { for ( const key in config ) { if ( settings [ key ] == null ) { const conf = config [ key ] ; settings [ key ] = conf ?. to_val != null ? conf . to_val ( conf . default ) : conf . default ; } } } cache . set ( KEY , settings ) ; return settings ; } export async function load_server_settings_from_env ( db ) { const PREFIX = SERVER_SETTINGS_ENV_PREFIX ; await db . async_query ( { query : "UPDATE server_settings" , set : { readonly : false } , where : [ "1=1" ] , } ) ; for ( const config of [ EXTRAS , CONF ] ) { for ( const key in config ) { const envvar = `${ PREFIX }_${ key . toUpperCase ( ) }` ; const envval = process . env [ envvar ] ; if ( envval == null ) continue ; L . debug ( `picking up $${ envvar } and saving it in the database` ) ; const valid = ( CONF [ key ] ?? EXTRAS [ key ] ) ?. valid ; if ( valid != null ) { if ( Array . isArray ( valid ) && ! valid . includes ( envval ) ) { throw new Error ( `The value of $${ envvar } is invalid. allowed are ${ valid }.` ) ; } else if ( typeof valid == "function" && ! valid ( envval ) ) { throw new Error ( `The validation function rejected the value of $${ envvar }.` ) ; } } await cb2 ( db . set_server_setting , { name : key , value : envval , readonly : true , } ) ; } } }