feat: Add function to configure SQLite database
This commit is contained in:
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user