fix(db): improve error handling and null checks

- Added null checks for undefined/null values in database operations
- Improved error handling for corrupt JSON recovery
- Added schema migration support for missing keys
- Target: database stability and data integrity
This commit is contained in:
ram/haidar
2026-02-06 20:54:42 +07:00
parent b275dfdc9c
commit e6ef8528fc

View File

@@ -54,6 +54,60 @@ const defaultData = {
pricing: {} // NEW: pricing configuration
};
function cloneDefaultData() {
return {
providerConnections: [],
providerNodes: [],
modelAliases: {},
combos: [],
apiKeys: [],
settings: {
cloudEnabled: false,
stickyRoundRobinLimit: 3,
requireLogin: true,
},
pricing: {},
};
}
function ensureDbShape(data) {
const defaults = cloneDefaultData();
const next = data && typeof data === "object" ? data : {};
let changed = false;
for (const [key, defaultValue] of Object.entries(defaults)) {
if (next[key] === undefined || next[key] === null) {
next[key] = defaultValue;
changed = true;
continue;
}
if (
key === "settings" &&
(typeof next.settings !== "object" || Array.isArray(next.settings))
) {
next.settings = { ...defaultValue };
changed = true;
continue;
}
if (
key === "settings" &&
typeof next.settings === "object" &&
!Array.isArray(next.settings)
) {
for (const [settingKey, settingDefault] of Object.entries(defaultValue)) {
if (next.settings[settingKey] === undefined) {
next.settings[settingKey] = settingDefault;
changed = true;
}
}
}
}
return { data: next, changed };
}
// Singleton instance
let dbInstance = null;
@@ -64,35 +118,43 @@ export async function getDb() {
if (isCloud) {
// Return in-memory DB for Workers
if (!dbInstance) {
dbInstance = new Low({ read: async () => {}, write: async () => {} }, defaultData);
dbInstance.data = defaultData;
const data = cloneDefaultData();
dbInstance = new Low({ read: async () => {}, write: async () => {} }, data);
dbInstance.data = data;
}
return dbInstance;
}
if (!dbInstance) {
const adapter = new JSONFile(DB_FILE);
dbInstance = new Low(adapter, defaultData);
dbInstance = new Low(adapter, cloneDefaultData());
}
// Try to read DB with error recovery for corrupt JSON
try {
await dbInstance.read();
} catch (error) {
if (error instanceof SyntaxError) {
console.warn('[DB] Corrupt JSON detected, resetting to defaults...');
dbInstance.data = defaultData;
await dbInstance.write();
} else {
throw error;
}
// Always read latest disk state to avoid stale singleton data across route workers.
try {
await dbInstance.read();
} catch (error) {
if (error instanceof SyntaxError) {
console.warn('[DB] Corrupt JSON detected, resetting to defaults...');
dbInstance.data = cloneDefaultData();
await dbInstance.write();
} else {
throw error;
}
}
// Initialize with default data if empty
if (!dbInstance.data) {
dbInstance.data = defaultData;
// Initialize/migrate missing keys for older DB schema versions.
if (!dbInstance.data) {
dbInstance.data = cloneDefaultData();
await dbInstance.write();
} else {
const { data, changed } = ensureDbShape(dbInstance.data);
dbInstance.data = data;
if (changed) {
await dbInstance.write();
}
}
return dbInstance;
}