shinroute / src /lib /db /settings.ts
shinmentakezo07
Add OmniRoute codebase without binary assets
9e4583c
Raw
History Blame Contribute Delete
12.4 kB
/**
* db/settings.js — Settings, pricing, and proxy config.
*/
import { getDbInstance } from "./core";
import { backupDbFile } from "./backup";
import { PROVIDER_ID_TO_ALIAS } from "@omniroute/open-sse/config/providerModels.ts";
// ──────────────── Settings ────────────────
export async function getSettings() {
const db = getDbInstance();
const rows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'settings'").all();
const settings: Record<string, any> = {
cloudEnabled: false,
stickyRoundRobinLimit: 3,
requireLogin: true,
};
for (const row of rows) {
settings[row.key] = JSON.parse(row.value);
}
// Auto-complete onboarding for pre-configured deployments (Docker/VM)
// If INITIAL_PASSWORD is set via env, this is a headless deploy — skip the wizard
if (!settings.setupComplete && process.env.INITIAL_PASSWORD) {
settings.setupComplete = true;
settings.requireLogin = true;
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('settings', 'setupComplete', 'true')"
).run();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('settings', 'requireLogin', 'true')"
).run();
}
return settings;
}
export async function updateSettings(updates: Record<string, unknown>) {
const db = getDbInstance();
const insert = db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('settings', ?, ?)"
);
const tx = db.transaction(() => {
for (const [key, value] of Object.entries(updates)) {
insert.run(key, JSON.stringify(value));
}
});
tx();
backupDbFile("pre-write");
return getSettings();
}
export async function isCloudEnabled() {
const settings = await getSettings();
return settings.cloudEnabled === true;
}
// ──────────────── Pricing ────────────────
export async function getPricing() {
const db = getDbInstance();
const rows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'pricing'").all();
const userPricing: Record<string, any> = {};
for (const row of rows) {
userPricing[row.key] = JSON.parse(row.value);
}
const { getDefaultPricing } = await import("@/shared/constants/pricing");
const defaultPricing = getDefaultPricing();
const mergedPricing: Record<string, any> = {};
for (const [provider, models] of Object.entries(defaultPricing) as [string, any][]) {
mergedPricing[provider] = { ...models };
if (userPricing[provider]) {
for (const [model, pricing] of Object.entries(userPricing[provider])) {
mergedPricing[provider][model] = mergedPricing[provider][model]
? { ...mergedPricing[provider][model], ...(pricing as any) }
: pricing;
}
}
}
for (const [provider, models] of Object.entries(userPricing)) {
if (!mergedPricing[provider]) {
mergedPricing[provider] = { ...models };
} else {
for (const [model, pricing] of Object.entries(models)) {
if (!mergedPricing[provider][model]) {
mergedPricing[provider][model] = pricing;
}
}
}
}
return mergedPricing;
}
export async function getPricingForModel(provider: string, model: string) {
const pricing = await getPricing();
if (pricing[provider]?.[model]) return pricing[provider][model];
const { PROVIDER_ID_TO_ALIAS } = await import("@omniroute/open-sse/config/providerModels");
const alias = PROVIDER_ID_TO_ALIAS[provider];
if (alias && pricing[alias]) return pricing[alias][model] || null;
const np = provider?.replace(/-cn$/, "");
if (np && np !== provider && pricing[np]) return pricing[np][model] || null;
return null;
}
export async function updatePricing(pricingData: Record<string, any>) {
const db = getDbInstance();
const insert = db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('pricing', ?, ?)"
);
const rows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'pricing'").all();
const existing: Record<string, any> = {};
for (const row of rows) existing[row.key] = JSON.parse(row.value);
const tx = db.transaction(() => {
for (const [provider, models] of Object.entries(pricingData)) {
insert.run(provider, JSON.stringify({ ...(existing[provider] || {}), ...models }));
}
});
tx();
backupDbFile("pre-write");
const updated = {};
const allRows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'pricing'").all();
for (const row of allRows) updated[row.key] = JSON.parse(row.value);
return updated;
}
export async function resetPricing(provider: string, model?: string) {
const db = getDbInstance();
if (model) {
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'pricing' AND key = ?")
.get(provider);
if (row) {
const models = JSON.parse(row.value);
delete models[model];
if (Object.keys(models).length === 0) {
db.prepare("DELETE FROM key_value WHERE namespace = 'pricing' AND key = ?").run(provider);
} else {
db.prepare("UPDATE key_value SET value = ? WHERE namespace = 'pricing' AND key = ?").run(
JSON.stringify(models),
provider
);
}
}
} else {
db.prepare("DELETE FROM key_value WHERE namespace = 'pricing' AND key = ?").run(provider);
}
backupDbFile("pre-write");
const allRows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'pricing'").all();
const result = {};
for (const row of allRows) result[row.key] = JSON.parse(row.value);
return result;
}
export async function resetAllPricing() {
const db = getDbInstance();
db.prepare("DELETE FROM key_value WHERE namespace = 'pricing'").run();
backupDbFile("pre-write");
return {};
}
// ──────────────── Proxy Config ────────────────
const DEFAULT_PROXY_CONFIG = { global: null, providers: {}, combos: {}, keys: {} };
const ALIAS_TO_PROVIDER_ID = Object.entries(PROVIDER_ID_TO_ALIAS).reduce(
(acc, [providerId, alias]) => {
if (alias) acc[alias] = providerId;
acc[providerId] = providerId;
return acc;
},
{}
) as Record<string, string>;
function resolveProviderAliasOrId(providerOrAlias: string): string {
if (typeof providerOrAlias !== "string") return providerOrAlias;
return ALIAS_TO_PROVIDER_ID[providerOrAlias] || providerOrAlias;
}
function getComboModelProvider(modelEntry: any): string | null {
if (modelEntry && typeof modelEntry.provider === "string") {
return resolveProviderAliasOrId(modelEntry.provider);
}
const modelValue =
typeof modelEntry === "string"
? modelEntry
: typeof modelEntry?.model === "string"
? modelEntry.model
: null;
if (!modelValue) return null;
const [providerOrAlias] = modelValue.split("/", 1);
if (!providerOrAlias) return null;
return resolveProviderAliasOrId(providerOrAlias);
}
function migrateProxyEntry(value: any) {
if (!value) return null;
if (typeof value === "object" && value.type) return value;
if (typeof value !== "string") return null;
try {
const url = new URL(value);
return {
type: url.protocol.replace(":", "").replace("//", "") || "http",
host: url.hostname,
port: url.port || (url.protocol === "socks5:" ? "1080" : "8080"),
username: url.username || "",
password: url.password || "",
};
} catch {
const parts = value.split(":");
return {
type: "http",
host: parts[0] || value,
port: parts[1] || "8080",
username: "",
password: "",
};
}
}
export async function getProxyConfig() {
const db = getDbInstance();
const rows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'proxyConfig'").all();
const raw = { ...DEFAULT_PROXY_CONFIG };
for (const row of rows) raw[row.key] = JSON.parse(row.value);
let migrated = false;
if (raw.global && typeof raw.global === "string") {
raw.global = migrateProxyEntry(raw.global);
migrated = true;
}
if (raw.providers) {
for (const [k, v] of Object.entries(raw.providers)) {
if (typeof v === "string") {
raw.providers[k] = migrateProxyEntry(v);
migrated = true;
}
}
}
if (migrated) {
const insert = db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('proxyConfig', ?, ?)"
);
if (raw.global !== undefined) insert.run("global", JSON.stringify(raw.global));
if (raw.providers) insert.run("providers", JSON.stringify(raw.providers));
}
return raw;
}
export async function getProxyForLevel(level: string, id?: string | null) {
const config = await getProxyConfig();
if (level === "global") return config.global || null;
const map = config[level + "s"] || config[level] || {};
return (id ? map[id] : null) || null;
}
export async function setProxyForLevel(level: string, id: string | null, proxy: any) {
const db = getDbInstance();
const config = await getProxyConfig();
if (level === "global") {
config.global = proxy || null;
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('proxyConfig', 'global', ?)"
).run(JSON.stringify(config.global));
} else {
const mapKey = level + "s";
if (!config[mapKey]) config[mapKey] = {};
if (proxy) {
config[mapKey][id] = proxy;
} else {
delete config[mapKey][id];
}
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('proxyConfig', ?, ?)"
).run(mapKey, JSON.stringify(config[mapKey]));
}
backupDbFile("pre-write");
return config;
}
export async function deleteProxyForLevel(level, id) {
return setProxyForLevel(level, id, null);
}
export async function resolveProxyForConnection(connectionId: string) {
const config = await getProxyConfig();
if (connectionId && config.keys?.[connectionId]) {
return { proxy: config.keys[connectionId], level: "key", levelId: connectionId };
}
const db = getDbInstance();
const connection = db
.prepare("SELECT provider FROM provider_connections WHERE id = ?")
.get(connectionId);
if (connection) {
if (config.combos && Object.keys(config.combos).length > 0) {
const combos = db.prepare("SELECT id, data FROM combos").all();
for (const comboRow of combos) {
if (config.combos[comboRow.id]) {
try {
const combo = JSON.parse(comboRow.data);
const usesProvider = (combo.models || []).some(
(entry) => getComboModelProvider(entry) === connection.provider
);
if (usesProvider) {
return { proxy: config.combos[comboRow.id], level: "combo", levelId: comboRow.id };
}
} catch {
// Ignore malformed combo records during proxy resolution.
}
}
}
}
if (config.providers?.[connection.provider]) {
return {
proxy: config.providers[connection.provider],
level: "provider",
levelId: connection.provider,
};
}
}
if (config.global) {
return { proxy: config.global, level: "global", levelId: null };
}
return { proxy: null, level: "direct", levelId: null };
}
export async function setProxyConfig(config: any) {
if (config.level !== undefined) {
return setProxyForLevel(config.level, config.id || null, config.proxy);
}
const db = getDbInstance();
const current = await getProxyConfig();
const insert = db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('proxyConfig', ?, ?)"
);
const tx = db.transaction(() => {
if (config.global !== undefined) {
current.global = config.global || null;
insert.run("global", JSON.stringify(current.global));
}
for (const mapKey of ["providers", "combos", "keys"]) {
if (config[mapKey]) {
current[mapKey] = { ...(current[mapKey] || {}), ...config[mapKey] };
for (const [k, v] of Object.entries(current[mapKey])) {
if (!v) delete current[mapKey][k];
}
insert.run(mapKey, JSON.stringify(current[mapKey]));
}
}
});
tx();
backupDbFile("pre-write");
return current;
}