Files
moreminimore-vibe/src/main/settings.ts
2025-06-19 10:42:51 -07:00

147 lines
4.5 KiB
TypeScript

import fs from "node:fs";
import path from "node:path";
import { getUserDataPath } from "../paths/paths";
import { UserSettingsSchema, type UserSettings, Secret } from "../lib/schemas";
import { safeStorage } from "electron";
import { v4 as uuidv4 } from "uuid";
// IF YOU NEED TO UPDATE THIS, YOU'RE PROBABLY DOING SOMETHING WRONG!
// Need to maintain backwards compatibility!
const DEFAULT_SETTINGS: UserSettings = {
selectedModel: {
name: "auto",
provider: "auto",
},
providerSettings: {},
telemetryConsent: "unset",
telemetryUserId: uuidv4(),
hasRunBefore: false,
experiments: {},
enableProLazyEditsMode: true,
enableProSmartFilesContextMode: true,
selectedChatMode: "build",
};
const SETTINGS_FILE = "user-settings.json";
function getSettingsFilePath(): string {
return path.join(getUserDataPath(), SETTINGS_FILE);
}
export function readSettings(): UserSettings {
try {
const filePath = getSettingsFilePath();
if (!fs.existsSync(filePath)) {
fs.writeFileSync(filePath, JSON.stringify(DEFAULT_SETTINGS, null, 2));
return DEFAULT_SETTINGS;
}
const rawSettings = JSON.parse(fs.readFileSync(filePath, "utf-8"));
const combinedSettings: UserSettings = {
...DEFAULT_SETTINGS,
...rawSettings,
};
const supabase = combinedSettings.supabase;
if (supabase) {
if (supabase.refreshToken) {
const encryptionType = supabase.refreshToken.encryptionType;
if (encryptionType) {
supabase.refreshToken = {
value: decrypt(supabase.refreshToken),
encryptionType,
};
}
}
if (supabase.accessToken) {
const encryptionType = supabase.accessToken.encryptionType;
if (encryptionType) {
supabase.accessToken = {
value: decrypt(supabase.accessToken),
encryptionType,
};
}
}
}
if (combinedSettings.githubAccessToken) {
const encryptionType = combinedSettings.githubAccessToken.encryptionType;
combinedSettings.githubAccessToken = {
value: decrypt(combinedSettings.githubAccessToken),
encryptionType,
};
}
for (const provider in combinedSettings.providerSettings) {
if (combinedSettings.providerSettings[provider].apiKey) {
const encryptionType =
combinedSettings.providerSettings[provider].apiKey.encryptionType;
combinedSettings.providerSettings[provider].apiKey = {
value: decrypt(combinedSettings.providerSettings[provider].apiKey),
encryptionType,
};
}
}
// Validate and merge with defaults
const validatedSettings = UserSettingsSchema.parse(combinedSettings);
return validatedSettings;
} catch (error) {
console.error("Error reading settings:", error);
return DEFAULT_SETTINGS;
}
}
export function writeSettings(settings: Partial<UserSettings>): void {
try {
const filePath = getSettingsFilePath();
const currentSettings = readSettings();
const newSettings = { ...currentSettings, ...settings };
if (newSettings.githubAccessToken) {
newSettings.githubAccessToken = encrypt(
newSettings.githubAccessToken.value,
);
}
if (newSettings.supabase) {
if (newSettings.supabase.accessToken) {
newSettings.supabase.accessToken = encrypt(
newSettings.supabase.accessToken.value,
);
}
if (newSettings.supabase.refreshToken) {
newSettings.supabase.refreshToken = encrypt(
newSettings.supabase.refreshToken.value,
);
}
}
for (const provider in newSettings.providerSettings) {
if (newSettings.providerSettings[provider].apiKey) {
newSettings.providerSettings[provider].apiKey = encrypt(
newSettings.providerSettings[provider].apiKey.value,
);
}
}
const validatedSettings = UserSettingsSchema.parse(newSettings);
fs.writeFileSync(filePath, JSON.stringify(validatedSettings, null, 2));
} catch (error) {
console.error("Error writing settings:", error);
}
}
export function encrypt(data: string): Secret {
if (safeStorage.isEncryptionAvailable()) {
return {
value: safeStorage.encryptString(data).toString("base64"),
encryptionType: "electron-safe-storage",
};
}
return {
value: data,
encryptionType: "plaintext",
};
}
export function decrypt(data: Secret): string {
if (data.encryptionType === "electron-safe-storage") {
return safeStorage.decryptString(Buffer.from(data.value, "base64"));
}
return data.value;
}