feat: Add function to configure SQLite database

This commit is contained in:
Peifan Li
2025-12-27 13:13:45 -05:00
parent 3252629988
commit 0ec84785e6
3 changed files with 51 additions and 6 deletions

View File

@@ -10,14 +10,38 @@ fs.ensureDirSync(DATA_DIR);
const dbPath = path.join(DATA_DIR, "mytube.db");
/**
* Configure SQLite database for compatibility with NTFS and other FUSE-based filesystems
* This is critical for environments like iStoreOS/OpenWrt where data may be on NTFS partitions
*
* @param db - The SQLite database instance to configure
*/
export function configureDatabase(db: Database.Database): void {
// Disable WAL mode - NTFS/FUSE doesn't support atomic operations required by WAL
// Use DELETE journal mode instead, which is more compatible with FUSE filesystems
db.pragma("journal_mode = DELETE");
// Set synchronous mode to NORMAL for better performance while maintaining data integrity
// FULL is safer but slower, NORMAL is a good balance for most use cases
db.pragma("synchronous = NORMAL");
// Set busy timeout to handle concurrent access better
db.pragma("busy_timeout = 5000");
// Enable foreign keys
db.pragma("foreign_keys = ON");
}
// Create database connection with getters that auto-reopen if closed
let sqliteInstance: Database.Database = new Database(dbPath);
configureDatabase(sqliteInstance);
let dbInstance = drizzle(sqliteInstance, { schema });
// Helper to ensure connection is open
function ensureConnection(): void {
if (!sqliteInstance.open) {
sqliteInstance = new Database(dbPath);
configureDatabase(sqliteInstance);
dbInstance = drizzle(sqliteInstance, { schema });
}
}
@@ -51,5 +75,6 @@ export function reinitializeDatabase(): void {
sqliteInstance.close();
}
sqliteInstance = new Database(dbPath);
configureDatabase(sqliteInstance);
dbInstance = drizzle(sqliteInstance, { schema });
}

View File

@@ -1,7 +1,7 @@
import { migrate } from 'drizzle-orm/better-sqlite3/migrator';
import path from 'path';
import { ROOT_DIR } from '../config/paths';
import { db } from './index';
import { db, sqlite, configureDatabase } from './index';
export async function runMigrations() {
try {
@@ -14,6 +14,13 @@ export async function runMigrations() {
migrate(db, { migrationsFolder });
console.log('Database migrations completed successfully.');
// Re-apply database configuration after migration
// This ensures journal_mode is set to DELETE even if migration changed it
// or if the database file already existed with WAL mode
// This is critical for NTFS/FUSE filesystem compatibility
configureDatabase(sqlite);
console.log('Database configuration applied (NTFS/FUSE compatible mode).');
// Check for legacy data files and run data migration if found
const { runMigration: runDataMigration } = await import('../services/migrationService');

View File

@@ -1,6 +1,6 @@
import { DatabaseError } from "../../errors/DownloadErrors";
import { db } from "../../db";
import { settings } from "../../db/schema";
import { DatabaseError } from "../../errors/DownloadErrors";
import { logger } from "../../utils/logger";
export function getSettings(): Record<string, any> {
@@ -18,7 +18,10 @@ export function getSettings(): Record<string, any> {
return settingsMap;
} catch (error) {
logger.error("Error getting settings", error instanceof Error ? error : new Error(String(error)));
logger.error(
"Error getting settings",
error instanceof Error ? error : new Error(String(error))
);
// Return empty object for backward compatibility
return {};
}
@@ -28,20 +31,30 @@ export function saveSettings(newSettings: Record<string, any>): void {
try {
db.transaction(() => {
for (const [key, value] of Object.entries(newSettings)) {
// Skip undefined values - they should not be saved
// This prevents "No values to set" error from drizzle-orm
if (value === undefined) {
continue;
}
const stringifiedValue = JSON.stringify(value);
db.insert(settings)
.values({
key,
value: JSON.stringify(value),
value: stringifiedValue,
})
.onConflictDoUpdate({
target: settings.key,
set: { value: JSON.stringify(value) },
set: { value: stringifiedValue },
})
.run();
}
});
} catch (error) {
logger.error("Error saving settings", error instanceof Error ? error : new Error(String(error)));
logger.error(
"Error saving settings",
error instanceof Error ? error : new Error(String(error))
);
throw new DatabaseError(
"Failed to save settings",
error instanceof Error ? error : new Error(String(error)),