Files
eggent/src/lib/storage/settings-store.ts

111 lines
2.9 KiB
TypeScript

import fs from "fs/promises";
import path from "path";
import { AppSettings } from "@/lib/types";
import {
DEFAULT_AUTH_PASSWORD_HASH,
DEFAULT_AUTH_USERNAME,
} from "@/lib/auth/password";
const DATA_DIR = path.join(process.cwd(), "data");
const SETTINGS_DIR = path.join(DATA_DIR, "settings");
const SETTINGS_FILE = path.join(SETTINGS_DIR, "settings.json");
async function ensureDir(dir: string) {
await fs.mkdir(dir, { recursive: true });
}
export const DEFAULT_SETTINGS: AppSettings = {
chatModel: {
provider: "openai",
model: "gpt-4o",
temperature: 0.7,
maxTokens: 4096,
},
utilityModel: {
provider: "openai",
model: "gpt-4o-mini",
temperature: 0.3,
maxTokens: 2048,
},
embeddingsModel: {
provider: "openai",
model: "text-embedding-3-small",
dimensions: 1536,
},
codeExecution: {
enabled: true,
timeout: 600,
maxOutputLength: 120000,
},
memory: {
enabled: true,
similarityThreshold: 0.35,
maxResults: 10,
chunkSize: 400,
},
search: {
enabled: false,
provider: "none",
},
general: {
darkMode: false,
language: "en",
},
auth: {
enabled: true,
username: DEFAULT_AUTH_USERNAME,
passwordHash: DEFAULT_AUTH_PASSWORD_HASH,
mustChangeCredentials: true,
},
};
export async function getSettings(): Promise<AppSettings> {
await ensureDir(SETTINGS_DIR);
try {
const content = await fs.readFile(SETTINGS_FILE, "utf-8");
const saved = JSON.parse(content) as unknown;
if (!saved || typeof saved !== "object" || Array.isArray(saved)) {
return structuredClone(DEFAULT_SETTINGS);
}
// Deep merge with defaults to handle new nested fields.
return deepMerge(
structuredClone(DEFAULT_SETTINGS) as unknown as Record<string, unknown>,
saved as Record<string, unknown>
) as unknown as AppSettings;
} catch {
return structuredClone(DEFAULT_SETTINGS);
}
}
export async function saveSettings(
settings: Partial<AppSettings>
): Promise<AppSettings> {
await ensureDir(SETTINGS_DIR);
const current = await getSettings();
const merged = deepMerge(current as unknown as Record<string, unknown>, settings as unknown as Record<string, unknown>) as unknown as AppSettings;
await fs.writeFile(SETTINGS_FILE, JSON.stringify(merged, null, 2), "utf-8");
return merged;
}
function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
const result = { ...target };
for (const key of Object.keys(source)) {
if (
source[key] &&
typeof source[key] === "object" &&
!Array.isArray(source[key]) &&
target[key] &&
typeof target[key] === "object" &&
!Array.isArray(target[key])
) {
result[key] = deepMerge(
target[key] as Record<string, unknown>,
source[key] as Record<string, unknown>
);
} else {
result[key] = source[key];
}
}
return result;
}