Add config value type converters for numbers and booleans

3f0393612558acb0f1783f0cf28cfaa6c09b494f

Cohee <18619528+Cohee1207@users.noreply.github.com>

18 files changed, +83 -48Showing whitespace changes
server.js+15 -15
@@ -261,26 +261,26 @@ app.use(responseTime());
261261
262262
263/** @type {number} */263/** @type {number} */
264const server_port = cliArguments.port ?? getConfigValue('port', DEFAULT_PORT);264const server_port = cliArguments.port ?? getConfigValue('port', DEFAULT_PORT, 'number');
265/** @type {boolean} */265/** @type {boolean} */
266const autorun = (cliArguments.autorun ?? getConfigValue('autorun', DEFAULT_AUTORUN)) && !cliArguments.ssl;266const autorun = (cliArguments.autorun ?? getConfigValue('autorun', DEFAULT_AUTORUN, 'boolean')) && !cliArguments.ssl;
267/** @type {boolean} */267/** @type {boolean} */
268const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN);268const listen = cliArguments.listen ?? getConfigValue('listen', DEFAULT_LISTEN, 'boolean');
269/** @type {string} */269/** @type {string} */
270const listenAddressIPv6 = cliArguments.listenAddressIPv6 ?? getConfigValue('listenAddress.ipv6', DEFAULT_LISTEN_ADDRESS_IPV6);270const listenAddressIPv6 = cliArguments.listenAddressIPv6 ?? getConfigValue('listenAddress.ipv6', DEFAULT_LISTEN_ADDRESS_IPV6);
271/** @type {string} */271/** @type {string} */
272const listenAddressIPv4 = cliArguments.listenAddressIPv4 ?? getConfigValue('listenAddress.ipv4', DEFAULT_LISTEN_ADDRESS_IPV4);272const listenAddressIPv4 = cliArguments.listenAddressIPv4 ?? getConfigValue('listenAddress.ipv4', DEFAULT_LISTEN_ADDRESS_IPV4);
273/** @type {boolean} */273/** @type {boolean} */
274const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY);274const enableCorsProxy = cliArguments.corsProxy ?? getConfigValue('enableCorsProxy', DEFAULT_CORS_PROXY, 'boolean');
275const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST);275const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode', DEFAULT_WHITELIST, 'boolean');
276/** @type {string} */276/** @type {string} */
277globalThis.DATA_ROOT = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');277globalThis.DATA_ROOT = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');
278/** @type {boolean} */278/** @type {boolean} */
279const disableCsrf = cliArguments.disableCsrf ?? getConfigValue('disableCsrfProtection', DEFAULT_CSRF_DISABLED);279const disableCsrf = cliArguments.disableCsrf ?? getConfigValue('disableCsrfProtection', DEFAULT_CSRF_DISABLED, 'boolean');
280const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', DEFAULT_BASIC_AUTH);280const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', DEFAULT_BASIC_AUTH, 'boolean');
281const perUserBasicAuth = getConfigValue('perUserBasicAuth', DEFAULT_PER_USER_BASIC_AUTH);281const perUserBasicAuth = getConfigValue('perUserBasicAuth', DEFAULT_PER_USER_BASIC_AUTH, 'boolean');
282/** @type {boolean} */282/** @type {boolean} */
283const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);283const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS, 'boolean');
284284
285const uploadsPath = path.join(globalThis.DATA_ROOT, UPLOADS_DIRECTORY);285const uploadsPath = path.join(globalThis.DATA_ROOT, UPLOADS_DIRECTORY);
286286
@@ -293,15 +293,15 @@ let enableIPv4 = stringToBool(cliArguments.enableIPv4) ?? getConfigValue('protoc
293/** @type {string} */293/** @type {string} */
294const autorunHostname = cliArguments.autorunHostname ?? getConfigValue('autorunHostname', DEFAULT_AUTORUN_HOSTNAME);294const autorunHostname = cliArguments.autorunHostname ?? getConfigValue('autorunHostname', DEFAULT_AUTORUN_HOSTNAME);
295/** @type {number} */295/** @type {number} */
296const autorunPortOverride = cliArguments.autorunPortOverride ?? getConfigValue('autorunPortOverride', DEFAULT_AUTORUN_PORT);296const autorunPortOverride = cliArguments.autorunPortOverride ?? getConfigValue('autorunPortOverride', DEFAULT_AUTORUN_PORT, 'number');
297297
298/** @type {boolean} */298/** @type {boolean} */
299const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv6', DEFAULT_PREFER_IPV6);299const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv6', DEFAULT_PREFER_IPV6, 'boolean');
300300
301/** @type {boolean} */301/** @type {boolean} */
302const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST);302const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST, 'boolean');
303303
304const proxyEnabled = cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', DEFAULT_PROXY_ENABLED);304const proxyEnabled = cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', DEFAULT_PROXY_ENABLED, 'boolean');
305const proxyUrl = cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', DEFAULT_PROXY_URL);305const proxyUrl = cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', DEFAULT_PROXY_URL);
306const proxyBypass = cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', DEFAULT_PROXY_BYPASS);306const proxyBypass = cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', DEFAULT_PROXY_BYPASS);
307307
@@ -395,7 +395,7 @@ if (enableCorsProxy) {
395395
396function getSessionCookieAge() {396function getSessionCookieAge() {
397 // Defaults to "no expiration" if not set397 // Defaults to "no expiration" if not set
398 const configValue = getConfigValue('sessionTimeout', -1);398 const configValue = getConfigValue('sessionTimeout', -1, 'number');
399399
400 // Convert to milliseconds400 // Convert to milliseconds
401 if (configValue > 0) {401 if (configValue > 0) {
@@ -924,7 +924,7 @@ function setWindowTitle(title) {
924function logSecurityAlert(message) {924function logSecurityAlert(message) {
925 if (basicAuthMode || enableWhitelist) return; // safe!925 if (basicAuthMode || enableWhitelist) return; // safe!
926 console.error(color.red(message));926 console.error(color.red(message));
927 if (getConfigValue('securityOverride', false)) {927 if (getConfigValue('securityOverride', false, 'boolean')) {
928 console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));928 console.warn(color.red('Security has been overridden. If it\'s not a trusted network, change the settings.'));
929 return;929 return;
930 }930 }
src/endpoints/backends/chat-completions.js+4 -4
@@ -106,8 +106,8 @@ async function sendClaudeRequest(request, response) {
106 const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();106 const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();
107 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);107 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);
108 const divider = '-'.repeat(process.stdout.columns);108 const divider = '-'.repeat(process.stdout.columns);
109 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false) && request.body.model.startsWith('claude-3');109 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false, 'boolean') && request.body.model.startsWith('claude-3');
110 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);110 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
111 // Disabled if not an integer or negative, or if the model doesn't support it111 // Disabled if not an integer or negative, or if the model doesn't support it
112 if (!Number.isInteger(cachingAtDepth) || cachingAtDepth < 0 || !request.body.model.startsWith('claude-3')) {112 if (!Number.isInteger(cachingAtDepth) || cachingAtDepth < 0 || !request.body.model.startsWith('claude-3')) {
113 cachingAtDepth = -1;113 cachingAtDepth = -1;
@@ -969,7 +969,7 @@ router.post('/generate', jsonParser, function (request, response) {
969 bodyParams.logprobs = true;969 bodyParams.logprobs = true;
970 }970 }
971971
972 if (getConfigValue('openai.randomizeUserId', false)) {972 if (getConfigValue('openai.randomizeUserId', false, 'boolean')) {
973 bodyParams['user'] = uuidv4();973 bodyParams['user'] = uuidv4();
974 }974 }
975 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) {975 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.OPENROUTER) {
@@ -1008,7 +1008,7 @@ router.post('/generate', jsonParser, function (request, response) {
1008 bodyParams['include_reasoning'] = true;1008 bodyParams['include_reasoning'] = true;
1009 }1009 }
10101010
1011 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);1011 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
1012 if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {1012 if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {
1013 cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);1013 cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);
1014 }1014 }
src/endpoints/backends/text-completions.js+2 -2
@@ -372,8 +372,8 @@ router.post('/generate', jsonParser, async function (request, response) {
372 }372 }
373373
374 if (request.body.api_type === TEXTGEN_TYPES.OLLAMA) {374 if (request.body.api_type === TEXTGEN_TYPES.OLLAMA) {
375 const keepAlive = getConfigValue('ollama.keepAlive', -1);375 const keepAlive = getConfigValue('ollama.keepAlive', -1, 'number');
376 const numBatch = getConfigValue('ollama.batchSize', -1);376 const numBatch = getConfigValue('ollama.batchSize', -1, 'number');
377 if (numBatch > 0) {377 if (numBatch > 0) {
378 request.body['num_batch'] = numBatch;378 request.body['num_batch'] = numBatch;
379 }379 }
src/endpoints/characters.js+1 -1
@@ -24,7 +24,7 @@ import { importRisuSprites } from './sprites.js';
24const defaultAvatarPath = './public/img/ai4.png';24const defaultAvatarPath = './public/img/ai4.png';
2525
26// KV-store for parsed character data26// KV-store for parsed character data
27const cacheCapacity = Number(getConfigValue('cardsCacheCapacity', 100)); // MB27const cacheCapacity = getConfigValue('cardsCacheCapacity', 100, 'number'); // MB
28// With 100 MB limit it would take roughly 3000 characters to reach this limit28// With 100 MB limit it would take roughly 3000 characters to reach this limit
29const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity);29const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity);
30// Some Android devices require tighter memory management30// Some Android devices require tighter memory management
src/endpoints/chats.js+3 -3
@@ -19,9 +19,9 @@ import {
19 formatBytes,19 formatBytes,
20} from '../util.js';20} from '../util.js';
2121
22const isBackupEnabled = !!getConfigValue('backups.chat.enabled', true);22const isBackupEnabled = getConfigValue('backups.chat.enabled', true, 'boolean');
23const maxTotalChatBackups = Number(getConfigValue('backups.chat.maxTotalBackups', -1));23const maxTotalChatBackups = getConfigValue('backups.chat.maxTotalBackups', -1, 'number');
24const throttleInterval = Number(getConfigValue('backups.chat.throttleInterval', 10_000));24const throttleInterval = getConfigValue('backups.chat.throttleInterval', 10_000, 'number');
2525
26/**26/**
27 * Saves a chat to the backups directory.27 * Saves a chat to the backups directory.
src/endpoints/content-manager.js+1 -1
@@ -165,7 +165,7 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
165 */165 */
166export async function checkForNewContent(directoriesList, forceCategories = []) {166export async function checkForNewContent(directoriesList, forceCategories = []) {
167 try {167 try {
168 const contentCheckSkip = getConfigValue('skipContentCheck', false);168 const contentCheckSkip = getConfigValue('skipContentCheck', false, 'boolean');
169 if (contentCheckSkip && forceCategories?.length === 0) {169 if (contentCheckSkip && forceCategories?.length === 0) {
170 return;170 return;
171 }171 }
src/endpoints/secrets.js+2 -2
@@ -183,7 +183,7 @@ router.post('/read', jsonParser, (request, response) => {
183});183});
184184
185router.post('/view', jsonParser, async (request, response) => {185router.post('/view', jsonParser, async (request, response) => {
186 const allowKeysExposure = getConfigValue('allowKeysExposure', false);186 const allowKeysExposure = getConfigValue('allowKeysExposure', false, 'boolean');
187187
188 if (!allowKeysExposure) {188 if (!allowKeysExposure) {
189 console.error('secrets.json could not be viewed unless the value of allowKeysExposure in config.yaml is set to true');189 console.error('secrets.json could not be viewed unless the value of allowKeysExposure in config.yaml is set to true');
@@ -205,7 +205,7 @@ router.post('/view', jsonParser, async (request, response) => {
205});205});
206206
207router.post('/find', jsonParser, (request, response) => {207router.post('/find', jsonParser, (request, response) => {
208 const allowKeysExposure = getConfigValue('allowKeysExposure', false);208 const allowKeysExposure = getConfigValue('allowKeysExposure', false, 'boolean');
209 const key = request.body.key;209 const key = request.body.key;
210210
211 if (!allowKeysExposure && !EXPORTABLE_KEYS.includes(key)) {211 if (!allowKeysExposure && !EXPORTABLE_KEYS.includes(key)) {
src/endpoints/settings.js+3 -3
@@ -11,9 +11,9 @@ import { jsonParser } from '../express-common.js';
11import { getAllUserHandles, getUserDirectories } from '../users.js';11import { getAllUserHandles, getUserDirectories } from '../users.js';
12import { getFileNameValidationFunction } from '../middleware/validateFileName.js';12import { getFileNameValidationFunction } from '../middleware/validateFileName.js';
1313
14const ENABLE_EXTENSIONS = !!getConfigValue('extensions.enabled', true);14const ENABLE_EXTENSIONS = getConfigValue('extensions.enabled', true, 'boolean');
15const ENABLE_EXTENSIONS_AUTO_UPDATE = !!getConfigValue('extensions.autoUpdate', true);15const ENABLE_EXTENSIONS_AUTO_UPDATE = getConfigValue('extensions.autoUpdate', true, 'boolean');
16const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);16const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean');
1717
18// 10 minutes18// 10 minutes
19const AUTOSAVE_INTERVAL = 10 * 60 * 1000;19const AUTOSAVE_INTERVAL = 10 * 60 * 1000;
src/endpoints/thumbnails.js+2 -2
@@ -12,8 +12,8 @@ import { getAllUserHandles, getUserDirectories } from '../users.js';
12import { getConfigValue } from '../util.js';12import { getConfigValue } from '../util.js';
13import { jsonParser } from '../express-common.js';13import { jsonParser } from '../express-common.js';
1414
15const thumbnailsEnabled = !!getConfigValue('thumbnails.enabled', true);15const thumbnailsEnabled = !!getConfigValue('thumbnails.enabled', true, 'boolean');
16const quality = Math.min(100, Math.max(1, parseInt(getConfigValue('thumbnails.quality', 95))));16const quality = Math.min(100, Math.max(1, parseInt(getConfigValue('thumbnails.quality', 95, 'number'))));
17const pngFormat = String(getConfigValue('thumbnails.format', 'jpg')).toLowerCase().trim() === 'png';17const pngFormat = String(getConfigValue('thumbnails.format', 'jpg')).toLowerCase().trim() === 'png';
1818
19/** @type {Record<string, number[]>} */19/** @type {Record<string, number[]>} */
src/endpoints/tokenizers.js+1 -1
@@ -56,7 +56,7 @@ export const TEXT_COMPLETION_MODELS = [
56];56];
5757
58const CHARS_PER_TOKEN = 3.35;58const CHARS_PER_TOKEN = 3.35;
59const IS_DOWNLOAD_ALLOWED = getConfigValue('enableDownloadableTokenizers', true);59const IS_DOWNLOAD_ALLOWED = getConfigValue('enableDownloadableTokenizers', true, 'boolean');
6060
61/**61/**
62 * Gets a path to the tokenizer model. Downloads the model if it's a URL.62 * Gets a path to the tokenizer model. Downloads the model if it's a URL.
src/endpoints/users-public.js+1 -1
@@ -7,7 +7,7 @@ import { jsonParser, getIpFromRequest } from '../express-common.js';
7import { color, Cache, getConfigValue } from '../util.js';7import { color, Cache, getConfigValue } from '../util.js';
8import { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } from '../users.js';8import { KEY_PREFIX, getUserAvatar, toKey, getPasswordHash, getPasswordSalt } from '../users.js';
99
10const DISCREET_LOGIN = getConfigValue('enableDiscreetLogin', false);10const DISCREET_LOGIN = getConfigValue('enableDiscreetLogin', false, 'boolean');
11const MFA_CACHE = new Cache(5 * 60 * 1000);11const MFA_CACHE = new Cache(5 * 60 * 1000);
1212
13export const router = express.Router();13export const router = express.Router();
src/middleware/basicAuth.js+2 -2
@@ -7,8 +7,8 @@ import storage from 'node-persist';
7import { getAllUserHandles, toKey, getPasswordHash } from '../users.js';7import { getAllUserHandles, toKey, getPasswordHash } from '../users.js';
8import { getConfig, getConfigValue, safeReadFileSync } from '../util.js';8import { getConfig, getConfigValue, safeReadFileSync } from '../util.js';
99
10const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);10const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean');
11const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);11const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean');
1212
13const basicAuthMiddleware = async function (request, response, callback) {13const basicAuthMiddleware = async function (request, response, callback) {
14 const unauthorizedWebpage = safeReadFileSync('./public/error/unauthorized.html') ?? '';14 const unauthorizedWebpage = safeReadFileSync('./public/error/unauthorized.html') ?? '';
src/middleware/whitelist.js+1 -1
@@ -8,7 +8,7 @@ import { getIpFromRequest } from '../express-common.js';
8import { color, getConfigValue, safeReadFileSync } from '../util.js';8import { color, getConfigValue, safeReadFileSync } from '../util.js';
99
10const whitelistPath = path.join(process.cwd(), './whitelist.txt');10const whitelistPath = path.join(process.cwd(), './whitelist.txt');
11const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false);11const enableForwardedWhitelist = getConfigValue('enableForwardedWhitelist', false, 'boolean');
12let whitelist = getConfigValue('whitelist', []);12let whitelist = getConfigValue('whitelist', []);
13let knownIPs = new Set();13let knownIPs = new Set();
1414
src/plugin-loader.js+2 -2
@@ -7,8 +7,8 @@ import { default as git } from 'simple-git';
7import { sync as commandExistsSync } from 'command-exists';7import { sync as commandExistsSync } from 'command-exists';
8import { getConfigValue, color } from './util.js';8import { getConfigValue, color } from './util.js';
99
10const enableServerPlugins = !!getConfigValue('enableServerPlugins', false);10const enableServerPlugins = getConfigValue('enableServerPlugins', false, 'boolean');
11const enableServerPluginsAutoUpdate = !!getConfigValue('enableServerPluginsAutoUpdate', true);11const enableServerPluginsAutoUpdate = getConfigValue('enableServerPluginsAutoUpdate', true, 'boolean');
1212
13/**13/**
14 * Map of loaded plugins.14 * Map of loaded plugins.
src/prompt-converters.js+1 -1
@@ -572,7 +572,7 @@ export function convertMistralMessages(messages, names) {
572 }572 }
573573
574 // Make the last assistant message a prefill574 // Make the last assistant message a prefill
575 const prefixEnabled = getConfigValue('mistral.enablePrefix', false);575 const prefixEnabled = getConfigValue('mistral.enablePrefix', false, 'boolean');
576 const lastMsg = messages[messages.length - 1];576 const lastMsg = messages[messages.length - 1];
577 if (prefixEnabled && messages.length > 0 && lastMsg?.role === 'assistant') {577 if (prefixEnabled && messages.length > 0 && lastMsg?.role === 'assistant') {
578 lastMsg.prefix = true;578 lastMsg.prefix = true;
src/transformers.js+1 -1
@@ -132,7 +132,7 @@ export async function getPipeline(task, forceModel = '') {
132132
133 const cacheDir = path.join(globalThis.DATA_ROOT, '_cache');133 const cacheDir = path.join(globalThis.DATA_ROOT, '_cache');
134 const model = forceModel || getModelForTask(task);134 const model = forceModel || getModelForTask(task);
135 const localOnly = !getConfigValue('extensions.models.autoDownload', true);135 const localOnly = !getConfigValue('extensions.models.autoDownload', true, 'boolean');
136 console.log('Initializing transformers.js pipeline for task', task, 'with model', model);136 console.log('Initializing transformers.js pipeline for task', task, 'with model', model);
137 const instance = await pipeline(task, model, { cache_dir: cacheDir, quantized: tasks[task].quantized ?? true, local_files_only: localOnly });137 const instance = await pipeline(task, model, { cache_dir: cacheDir, quantized: tasks[task].quantized ?? true, local_files_only: localOnly });
138 tasks[task].pipeline = instance;138 tasks[task].pipeline = instance;
src/users.js+3 -3
@@ -21,9 +21,9 @@ import { getContentOfType } from './endpoints/content-manager.js';
2121
22export const KEY_PREFIX = 'user:';22export const KEY_PREFIX = 'user:';
23const AVATAR_PREFIX = 'avatar:';23const AVATAR_PREFIX = 'avatar:';
24const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);24const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean');
25const AUTHELIA_AUTH = getConfigValue('autheliaAuth', false);25const AUTHELIA_AUTH = getConfigValue('autheliaAuth', false, 'boolean');
26const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);26const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean');
27const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');27const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');
2828
29/**29/**
src/util.js+38 -3
@@ -54,13 +54,16 @@ export function getConfig() {
54 }54 }
55}55}
5656
57
57/**58/**
58 * Returns the value for the given key from the config object.59 * Returns the value for the given key from the config object.
59 * @param {string} key - Key to get from the config object60 * @param {string} key - Key to get from the config object
60 * @param {any} defaultValue - Default value to return if the key is not found61 * @param {any} defaultValue - Default value to return if the key is not found
62 * @param {'number'|'boolean'|null} typeConverter - Type to convert the value to
61 * @returns {any} Value for the given key63 * @returns {any} Value for the given key
62 */64 */
63export function getConfigValue(key, defaultValue = null) {65export function getConfigValue(key, defaultValue = null, typeConverter = null) {
66 function _getValue() {
64 const envKey = keyToEnv(key);67 const envKey = keyToEnv(key);
65 if (envKey in process.env) {68 if (envKey in process.env) {
66 return process.env[envKey];69 return process.env[envKey];
@@ -69,6 +72,17 @@ export function getConfigValue(key, defaultValue = null) {
69 return _.get(config, key, defaultValue);72 return _.get(config, key, defaultValue);
70 }73 }
7174
75 const value = _getValue();
76 switch (typeConverter) {
77 case 'number':
78 return Number(value);
79 case 'boolean':
80 return toBoolean(value);
81 default:
82 return value;
83 }
84}
85
72/**86/**
73 * Encodes the Basic Auth header value for the given user and password.87 * Encodes the Basic Auth header value for the given user and password.
74 * @param {string} auth username:password88 * @param {string} auth username:password
@@ -392,7 +406,7 @@ export function generateTimestamp() {
392 * @param {number?} limit Maximum number of backups to keep. If null, the limit is determined by the `backups.common.numberOfBackups` config value.406 * @param {number?} limit Maximum number of backups to keep. If null, the limit is determined by the `backups.common.numberOfBackups` config value.
393 */407 */
394export function removeOldBackups(directory, prefix, limit = null) {408export function removeOldBackups(directory, prefix, limit = null) {
395 const MAX_BACKUPS = limit ?? Number(getConfigValue('backups.common.numberOfBackups', 50));409 const MAX_BACKUPS = limit ?? Number(getConfigValue('backups.common.numberOfBackups', 50, 'number'));
396410
397 let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));411 let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
398 if (files.length > MAX_BACKUPS) {412 if (files.length > MAX_BACKUPS) {
@@ -745,6 +759,27 @@ export async function canResolve(name, useIPv6 = true, useIPv4 = true) {
745 }759 }
746}760}
747761
762/**
763 * Converts various JavaScript primitives to boolean values.
764 * Handles special case for "true"/"false" strings (case-insensitive)
765 *
766 * @param {any} value - The value to convert to boolean
767 * @returns {boolean} - The boolean representation of the value
768 */
769export function toBoolean(value) {
770 // Handle string values case-insensitively
771 if (typeof value === 'string') {
772 // Trim and convert to lowercase for case-insensitive comparison
773 const trimmedLower = value.trim().toLowerCase();
774
775 // Handle explicit "true"/"false" strings
776 if (trimmedLower === 'true') return true;
777 if (trimmedLower === 'false') return false;
778 }
779
780 // Handle all other JavaScript values based on their "truthiness"
781 return Boolean(value);
782}
748783
749/**784/**
750 * converts string to boolean accepts 'true' or 'false' else it returns the string put in785 * converts string to boolean accepts 'true' or 'false' else it returns the string put in
@@ -761,7 +796,7 @@ export function stringToBool(str) {
761 * Setup the minimum log level796 * Setup the minimum log level
762 */797 */
763export function setupLogLevel() {798export function setupLogLevel() {
764 const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG);799 const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG, 'number');
765800
766 globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};801 globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};
767 globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};802 globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};