Blame Raw
· · · 253 lines (7.6 KB)
0 contributors
1import fs from 'node:fs';
2import path from 'node:path';
3import yaml from 'yaml';
4import color from 'chalk';
5import _ from 'lodash';
6import { serverDirectory } from './server-directory.js';
7import { keyToEnv, setConfigFilePath } from './util.js';
8
9const keyMigrationMap = [
10 {
11 oldKey: 'disableThumbnails',
12 newKey: 'thumbnails.enabled',
13 migrate: (value) => !value,
14 },
15 {
16 oldKey: 'thumbnailsQuality',
17 newKey: 'thumbnails.quality',
18 migrate: (value) => value,
19 },
20 {
21 oldKey: 'avatarThumbnailsPng',
22 newKey: 'thumbnails.format',
23 migrate: (value) => (value ? 'png' : 'jpg'),
24 },
25 {
26 oldKey: 'disableChatBackup',
27 newKey: 'backups.chat.enabled',
28 migrate: (value) => !value,
29 },
30 {
31 oldKey: 'numberOfBackups',
32 newKey: 'backups.common.numberOfBackups',
33 migrate: (value) => value,
34 },
35 {
36 oldKey: 'maxTotalChatBackups',
37 newKey: 'backups.chat.maxTotalBackups',
38 migrate: (value) => value,
39 },
40 {
41 oldKey: 'chatBackupThrottleInterval',
42 newKey: 'backups.chat.throttleInterval',
43 migrate: (value) => value,
44 },
45 {
46 oldKey: 'enableExtensions',
47 newKey: 'extensions.enabled',
48 migrate: (value) => value,
49 },
50 {
51 oldKey: 'enableExtensionsAutoUpdate',
52 newKey: 'extensions.autoUpdate',
53 migrate: (value) => value,
54 },
55 {
56 oldKey: 'extras.disableAutoDownload',
57 newKey: 'extensions.models.autoDownload',
58 migrate: (value) => !value,
59 },
60 {
61 oldKey: 'extras.classificationModel',
62 newKey: 'extensions.models.classification',
63 migrate: (value) => value,
64 },
65 {
66 oldKey: 'extras.captioningModel',
67 newKey: 'extensions.models.captioning',
68 migrate: (value) => value,
69 },
70 {
71 oldKey: 'extras.embeddingModel',
72 newKey: 'extensions.models.embedding',
73 migrate: (value) => value,
74 },
75 {
76 oldKey: 'extras.speechToTextModel',
77 newKey: 'extensions.models.speechToText',
78 migrate: (value) => value,
79 },
80 {
81 oldKey: 'extras.textToSpeechModel',
82 newKey: 'extensions.models.textToSpeech',
83 migrate: (value) => value,
84 },
85 {
86 oldKey: 'minLogLevel',
87 newKey: 'logging.minLogLevel',
88 migrate: (value) => value,
89 },
90 {
91 oldKey: 'cardsCacheCapacity',
92 newKey: 'performance.memoryCacheCapacity',
93 migrate: (value) => `${value}mb`,
94 },
95 {
96 oldKey: 'cookieSecret',
97 newKey: 'cookieSecret',
98 migrate: () => void 0,
99 remove: true,
100 },
101 {
102 oldKey: 'autorun',
103 newKey: 'browserLaunch.enabled',
104 migrate: (value) => value,
105 },
106 {
107 oldKey: 'autorunHostname',
108 newKey: 'browserLaunch.hostname',
109 migrate: (value) => value,
110 },
111 {
112 oldKey: 'autorunPortOverride',
113 newKey: 'browserLaunch.port',
114 migrate: (value) => value,
115 },
116 {
117 oldKey: 'avoidLocalhost',
118 newKey: 'browserLaunch.avoidLocalhost',
119 migrate: (value) => value,
120 },
121 {
122 oldKey: 'extras.promptExpansionModel',
123 newKey: 'extras.promptExpansionModel',
124 migrate: () => void 0,
125 remove: true,
126 },
127 {
128 oldKey: 'autheliaAuth',
129 newKey: 'sso.autheliaAuth',
130 migrate: (value) => value,
131 },
132 {
133 oldKey: 'authentikAuth',
134 newKey: 'sso.authentikAuth',
135 migrate: (value) => value,
136 },
137];
138
139/**
140 * Gets all keys from an object recursively.
141 * @param {object} obj Object to get all keys from
142 * @param {string} prefix Prefix to prepend to all keys
143 * @returns {string[]} Array of all keys in the object
144 */
145function getAllKeys(obj, prefix = '') {
146 if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) {
147 return [];
148 }
149
150 return _.flatMap(Object.keys(obj), key => {
151 const newPrefix = prefix ? `${prefix}.${key}` : key;
152 if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
153 return getAllKeys(obj[key], newPrefix);
154 } else {
155 return [newPrefix];
156 }
157 });
158}
159
160/**
161 * Compares the current config.yaml with the default config.yaml and adds any missing values.
162 * @param {string} configPath Path to config.yaml
163 */
164export function addMissingConfigValues(configPath) {
165 try {
166 const defaultConfig = yaml.parse(fs.readFileSync(path.join(serverDirectory, './default/config.yaml'), 'utf8'));
167
168 if (!fs.existsSync(configPath)) {
169 console.warn(color.yellow(`Warning: config.yaml not found at ${configPath}. Creating a new one with default values.`));
170 fs.writeFileSync(configPath, yaml.stringify(defaultConfig));
171 return;
172 }
173
174 let config = yaml.parse(fs.readFileSync(configPath, 'utf8'));
175
176 // Migrate old keys to new keys
177 const migratedKeys = [];
178 for (const { oldKey, newKey, migrate, remove } of keyMigrationMap) {
179 // Migrate environment variables
180 const oldEnvKey = keyToEnv(oldKey);
181 const newEnvKey = keyToEnv(newKey);
182 if (process.env[oldEnvKey] && !process.env[newEnvKey]) {
183 const oldValue = process.env[oldEnvKey];
184 const newValue = migrate(oldValue);
185 process.env[newEnvKey] = newValue;
186 delete process.env[oldEnvKey];
187 console.warn(color.yellow(`Warning: Using a deprecated environment variable: ${oldEnvKey}. Please use ${newEnvKey} instead.`));
188 console.log(`Redirecting ${color.blue(oldEnvKey)}=${oldValue} -> ${color.blue(newEnvKey)}=${newValue}`);
189 }
190
191 if (_.has(config, oldKey)) {
192 if (remove) {
193 _.unset(config, oldKey);
194 migratedKeys.push({
195 oldKey,
196 newValue: void 0,
197 });
198 continue;
199 }
200
201 const oldValue = _.get(config, oldKey);
202 const newValue = migrate(oldValue);
203 _.set(config, newKey, newValue);
204 _.unset(config, oldKey);
205
206 migratedKeys.push({
207 oldKey,
208 newKey,
209 oldValue,
210 newValue,
211 });
212 }
213 }
214
215 // Get all keys from the original config
216 const originalKeys = getAllKeys(config);
217
218 // Use lodash's defaultsDeep function to recursively apply default properties
219 config = _.defaultsDeep(config, defaultConfig);
220
221 // Get all keys from the updated config
222 const updatedKeys = getAllKeys(config);
223
224 // Find the keys that were added
225 const addedKeys = _.difference(updatedKeys, originalKeys);
226
227 if (addedKeys.length === 0 && migratedKeys.length === 0) {
228 return;
229 }
230
231 if (addedKeys.length > 0) {
232 console.log('Adding missing config values to config.yaml:', addedKeys);
233 }
234
235 if (migratedKeys.length > 0) {
236 console.log('Migrating config values in config.yaml:', migratedKeys);
237 }
238
239 fs.writeFileSync(configPath, yaml.stringify(config));
240 } catch (error) {
241 console.warn(color.yellow('Could not add missing config values to config.yaml'), error);
242 }
243}
244
245/**
246 * Performs early initialization tasks before the server starts.
247 * @param {string} configPath Path to config.yaml
248 */
249export function initConfig(configPath) {
250 console.log('Using config path:', color.green(configPath));
251 setConfigFilePath(configPath);
252 addMissingConfigValues(configPath);
253}