feat(app): initialize new config management

This commit is contained in:
ItzCrazyKns
2025-10-11 18:00:06 +05:30
parent f1e6aa9c1a
commit 3003d44544
2 changed files with 219 additions and 0 deletions

137
src/lib/config/index.ts Normal file
View File

@@ -0,0 +1,137 @@
import path from 'node:path';
import fs from 'fs';
import { Config, ConfigModelProvider, EnvMap, UIConfigSections } from './types';
import ModelRegistry from '../models/registry';
import { hashObj } from '../serverUtils';
class ConfigManager {
configPath: string = path.join(
process.env.DATA_DIR || process.cwd(),
'/data/config.json',
);
configVersion = 1;
currentConfig: Config = {
version: this.configVersion,
general: {},
modelProviders: [],
};
uiConfigSections: UIConfigSections = {
general: [],
modelProviders: [],
};
modelRegistry = new ModelRegistry();
constructor() {
this.initialize();
}
private initialize() {
this.initializeConfig();
this.initializeFromEnv();
}
private saveConfig() {
fs.writeFileSync(
this.configPath,
JSON.stringify(this.currentConfig, null, 2),
);
}
private initializeConfig() {
const exists = fs.existsSync(this.configPath);
if (!exists) {
fs.writeFileSync(
this.configPath,
JSON.stringify(this.currentConfig, null, 2),
);
} else {
try {
this.currentConfig = JSON.parse(
fs.readFileSync(this.configPath, 'utf-8'),
);
} catch (err) {
if (err instanceof SyntaxError) {
console.error(
`Error parsing config file at ${this.configPath}:`,
err,
);
console.log(
'Loading default config and overwriting the existing file.',
);
fs.writeFileSync(
this.configPath,
JSON.stringify(this.currentConfig, null, 2),
);
return;
} else {
console.log('Unknown error reading config file:', err);
}
}
this.currentConfig = this.migrateConfigNeeded(this.currentConfig);
}
}
private migrateConfigNeeded(config: Config): Config {
/* TODO: Add migrations */
return config;
}
private initializeFromEnv() {
const providerConfigSections = this.modelRegistry.getUIConfigSection();
this.uiConfigSections.modelProviders = providerConfigSections;
const newProviders: ConfigModelProvider[] = [];
providerConfigSections.forEach((provider) => {
const newProvider: ConfigModelProvider & { required?: string[] } = {
id: crypto.randomUUID(),
name: `${provider.name} ${Math.floor(Math.random() * 1000)}`,
type: provider.key,
chatModels: [],
embeddingModels: [],
config: {},
required: [],
hash: '',
};
provider.fields.forEach((field) => {
newProvider.config[field.key] =
process.env[field.env!] ||
field.default ||
''; /* Env var must exist for providers */
if (field.required) newProvider.required?.push(field.key);
});
let configured = true;
newProvider.required?.forEach((r) => {
if (!newProvider.config[r]) {
configured = false;
}
});
if (configured) {
const hash = hashObj(newProvider.config);
newProvider.hash = hash;
delete newProvider.required;
const exists = this.currentConfig.modelProviders.find(
(p) => p.hash === hash,
);
if (!exists) {
newProviders.push(newProvider);
}
}
});
this.currentConfig.modelProviders.push(...newProviders);
this.saveConfig();
}
}
new ConfigManager();

82
src/lib/config/types.ts Normal file
View File

@@ -0,0 +1,82 @@
type BaseUIConfigField = {
name: string;
key: string;
required: boolean;
description: string;
scope: 'client' | 'server';
env?: string;
};
type StringUIConfigField = BaseUIConfigField & {
type: 'string';
placeholder?: string;
default?: string;
};
type SelectUIConfigFieldOptions = {
name: string;
key: string;
value: string;
};
type SelectUIConfigField = BaseUIConfigField & {
type: 'select';
default?: string;
options: SelectUIConfigFieldOptions[];
};
type PasswordUIConfigField = BaseUIConfigField & {
type: 'password';
placeholder?: string;
default?: string;
};
type UIConfigField =
| StringUIConfigField
| SelectUIConfigField
| PasswordUIConfigField;
type ConfigModelProvider = {
id: string;
name: string;
type: string;
chatModels: string[];
embeddingModels: string[];
config: { [key: string]: any };
hash: string;
};
type Config = {
version: number;
general: {
[key: string]: any;
};
modelProviders: ConfigModelProvider[];
};
type EnvMap = {
[key: string]: {
fieldKey: string;
providerKey: string;
};
};
type ModelProviderUISection = {
name: string;
key: string;
fields: UIConfigField[];
};
type UIConfigSections = {
general: UIConfigField[];
modelProviders: ModelProviderUISection[];
};
export type {
UIConfigField,
Config,
EnvMap,
UIConfigSections,
ModelProviderUISection,
ConfigModelProvider,
};