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:
@@ -54,6 +54,60 @@ const defaultData = {
|
|||||||
pricing: {} // NEW: pricing configuration
|
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
|
// Singleton instance
|
||||||
let dbInstance = null;
|
let dbInstance = null;
|
||||||
|
|
||||||
@@ -64,35 +118,43 @@ export async function getDb() {
|
|||||||
if (isCloud) {
|
if (isCloud) {
|
||||||
// Return in-memory DB for Workers
|
// Return in-memory DB for Workers
|
||||||
if (!dbInstance) {
|
if (!dbInstance) {
|
||||||
dbInstance = new Low({ read: async () => {}, write: async () => {} }, defaultData);
|
const data = cloneDefaultData();
|
||||||
dbInstance.data = defaultData;
|
dbInstance = new Low({ read: async () => {}, write: async () => {} }, data);
|
||||||
|
dbInstance.data = data;
|
||||||
}
|
}
|
||||||
return dbInstance;
|
return dbInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dbInstance) {
|
if (!dbInstance) {
|
||||||
const adapter = new JSONFile(DB_FILE);
|
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
|
// Always read latest disk state to avoid stale singleton data across route workers.
|
||||||
try {
|
try {
|
||||||
await dbInstance.read();
|
await dbInstance.read();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof SyntaxError) {
|
if (error instanceof SyntaxError) {
|
||||||
console.warn('[DB] Corrupt JSON detected, resetting to defaults...');
|
console.warn('[DB] Corrupt JSON detected, resetting to defaults...');
|
||||||
dbInstance.data = defaultData;
|
dbInstance.data = cloneDefaultData();
|
||||||
await dbInstance.write();
|
await dbInstance.write();
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize with default data if empty
|
// Initialize/migrate missing keys for older DB schema versions.
|
||||||
if (!dbInstance.data) {
|
if (!dbInstance.data) {
|
||||||
dbInstance.data = defaultData;
|
dbInstance.data = cloneDefaultData();
|
||||||
|
await dbInstance.write();
|
||||||
|
} else {
|
||||||
|
const { data, changed } = ensureDbShape(dbInstance.data);
|
||||||
|
dbInstance.data = data;
|
||||||
|
if (changed) {
|
||||||
await dbInstance.write();
|
await dbInstance.write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dbInstance;
|
return dbInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user