Spaces:
Running
Running
import fs from 'node:fs'; | |
import path from 'node:path'; | |
import yaml from 'yaml'; | |
import color from 'chalk'; | |
import _ from 'lodash'; | |
import { serverDirectory } from './server-directory.js'; | |
import { keyToEnv, setConfigFilePath } from './util.js'; | |
const keyMigrationMap = [ | |
{ | |
oldKey: 'disableThumbnails', | |
newKey: 'thumbnails.enabled', | |
migrate: (value) => !value, | |
}, | |
{ | |
oldKey: 'thumbnailsQuality', | |
newKey: 'thumbnails.quality', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'avatarThumbnailsPng', | |
newKey: 'thumbnails.format', | |
migrate: (value) => (value ? 'png' : 'jpg'), | |
}, | |
{ | |
oldKey: 'disableChatBackup', | |
newKey: 'backups.chat.enabled', | |
migrate: (value) => !value, | |
}, | |
{ | |
oldKey: 'numberOfBackups', | |
newKey: 'backups.common.numberOfBackups', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'maxTotalChatBackups', | |
newKey: 'backups.chat.maxTotalBackups', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'chatBackupThrottleInterval', | |
newKey: 'backups.chat.throttleInterval', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'enableExtensions', | |
newKey: 'extensions.enabled', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'enableExtensionsAutoUpdate', | |
newKey: 'extensions.autoUpdate', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'extras.disableAutoDownload', | |
newKey: 'extensions.models.autoDownload', | |
migrate: (value) => !value, | |
}, | |
{ | |
oldKey: 'extras.classificationModel', | |
newKey: 'extensions.models.classification', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'extras.captioningModel', | |
newKey: 'extensions.models.captioning', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'extras.embeddingModel', | |
newKey: 'extensions.models.embedding', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'extras.speechToTextModel', | |
newKey: 'extensions.models.speechToText', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'extras.textToSpeechModel', | |
newKey: 'extensions.models.textToSpeech', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'minLogLevel', | |
newKey: 'logging.minLogLevel', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'cardsCacheCapacity', | |
newKey: 'performance.memoryCacheCapacity', | |
migrate: (value) => `${value}mb`, | |
}, | |
{ | |
oldKey: 'cookieSecret', | |
newKey: 'cookieSecret', | |
migrate: () => void 0, | |
remove: true, | |
}, | |
{ | |
oldKey: 'autorun', | |
newKey: 'browserLaunch.enabled', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'autorunHostname', | |
newKey: 'browserLaunch.hostname', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'autorunPortOverride', | |
newKey: 'browserLaunch.port', | |
migrate: (value) => value, | |
}, | |
{ | |
oldKey: 'avoidLocalhost', | |
newKey: 'browserLaunch.avoidLocalhost', | |
migrate: (value) => value, | |
}, | |
]; | |
/** | |
* Gets all keys from an object recursively. | |
* @param {object} obj Object to get all keys from | |
* @param {string} prefix Prefix to prepend to all keys | |
* @returns {string[]} Array of all keys in the object | |
*/ | |
function getAllKeys(obj, prefix = '') { | |
if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) { | |
return []; | |
} | |
return _.flatMap(Object.keys(obj), key => { | |
const newPrefix = prefix ? `${prefix}.${key}` : key; | |
if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { | |
return getAllKeys(obj[key], newPrefix); | |
} else { | |
return [newPrefix]; | |
} | |
}); | |
} | |
/** | |
* Compares the current config.yaml with the default config.yaml and adds any missing values. | |
* @param {string} configPath Path to config.yaml | |
*/ | |
export function addMissingConfigValues(configPath) { | |
try { | |
const defaultConfig = yaml.parse(fs.readFileSync(path.join(serverDirectory, './default/config.yaml'), 'utf8')); | |
let config = yaml.parse(fs.readFileSync(configPath, 'utf8')); | |
// Migrate old keys to new keys | |
const migratedKeys = []; | |
for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) { | |
// Migrate environment variables | |
const oldEnvKey = keyToEnv(oldKey); | |
const newEnvKey = keyToEnv(newKey); | |
if (process.env[oldEnvKey] && !process.env[newEnvKey]) { | |
const oldValue = process.env[oldEnvKey]; | |
const newValue = migrate(oldValue); | |
process.env[newEnvKey] = newValue; | |
delete process.env[oldEnvKey]; | |
console.warn(color.yellow(`Warning: Using a deprecated environment variable: ${oldEnvKey}. Please use ${newEnvKey} instead.`)); | |
console.log(`Redirecting ${color.blue(oldEnvKey)}=${oldValue} -> ${color.blue(newEnvKey)}=${newValue}`); | |
} | |
if (_.has(config, oldKey)) { | |
if (remove) { | |
_.unset(config, oldKey); | |
migratedKeys.push({ | |
oldKey, | |
newValue: void 0, | |
}); | |
continue; | |
} | |
const oldValue = _.get(config, oldKey); | |
const newValue = migrate(oldValue); | |
_.set(config, newKey, newValue); | |
_.unset(config, oldKey); | |
migratedKeys.push({ | |
oldKey, | |
newKey, | |
oldValue, | |
newValue, | |
}); | |
} | |
} | |
// Get all keys from the original config | |
const originalKeys = getAllKeys(config); | |
// Use lodash's defaultsDeep function to recursively apply default properties | |
config = _.defaultsDeep(config, defaultConfig); | |
// Get all keys from the updated config | |
const updatedKeys = getAllKeys(config); | |
// Find the keys that were added | |
const addedKeys = _.difference(updatedKeys, originalKeys); | |
if (addedKeys.length === 0 && migratedKeys.length === 0) { | |
return; | |
} | |
if (addedKeys.length > 0) { | |
console.log('Adding missing config values to config.yaml:', addedKeys); | |
} | |
if (migratedKeys.length > 0) { | |
console.log('Migrating config values in config.yaml:', migratedKeys); | |
} | |
fs.writeFileSync(configPath, yaml.stringify(config)); | |
} catch (error) { | |
console.error(color.red('FATAL: Could not add missing config values to config.yaml'), error); | |
} | |
} | |
/** | |
* Performs early initialization tasks before the server starts. | |
* @param {string} configPath Path to config.yaml | |
*/ | |
export function initConfig(configPath) { | |
setConfigFilePath(configPath); | |
addMissingConfigValues(configPath); | |
} | |