|
|
@ -75,27 +75,30 @@ try { |
|
|
|
const getAppDataPath = async (): Promise<string> => { |
|
|
|
try { |
|
|
|
// Read config file directly
|
|
|
|
const configPath = path.join(__dirname, '..', 'capacitor.config.json'); |
|
|
|
const configContent = await fs.promises.readFile(configPath, 'utf-8'); |
|
|
|
const configPath = path.join(__dirname, "..", "capacitor.config.json"); |
|
|
|
const configContent = await fs.promises.readFile(configPath, "utf-8"); |
|
|
|
const config = JSON.parse(configContent); |
|
|
|
const linuxPath = config?.plugins?.CapacitorSQLite?.electronLinuxLocation; |
|
|
|
|
|
|
|
|
|
|
|
if (linuxPath) { |
|
|
|
// Expand ~ to home directory
|
|
|
|
const expandedPath = linuxPath.replace(/^~/, process.env.HOME || ''); |
|
|
|
const expandedPath = linuxPath.replace(/^~/, process.env.HOME || ""); |
|
|
|
logger.info("[Electron] Using configured database path:", expandedPath); |
|
|
|
return expandedPath; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Fallback to app.getPath if config path is not available
|
|
|
|
const userDataPath = app.getPath('userData'); |
|
|
|
const userDataPath = app.getPath("userData"); |
|
|
|
logger.info("[Electron] Using fallback user data path:", userDataPath); |
|
|
|
return userDataPath; |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Error getting app data path:", error); |
|
|
|
// Fallback to app.getPath if anything fails
|
|
|
|
const userDataPath = app.getPath('userData'); |
|
|
|
logger.info("[Electron] Using fallback user data path after error:", userDataPath); |
|
|
|
const userDataPath = app.getPath("userData"); |
|
|
|
logger.info( |
|
|
|
"[Electron] Using fallback user data path after error:", |
|
|
|
userDataPath, |
|
|
|
); |
|
|
|
return userDataPath; |
|
|
|
} |
|
|
|
}; |
|
|
@ -103,30 +106,32 @@ const getAppDataPath = async (): Promise<string> => { |
|
|
|
const validateAndNormalizePath = async (filePath: string): Promise<string> => { |
|
|
|
// Resolve any relative paths
|
|
|
|
const resolvedPath = path.resolve(filePath); |
|
|
|
|
|
|
|
|
|
|
|
// Ensure it's an absolute path
|
|
|
|
if (!path.isAbsolute(resolvedPath)) { |
|
|
|
throw new Error(`Database path must be absolute: ${resolvedPath}`); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ensure it's within the app data directory
|
|
|
|
const appDataPath = await getAppDataPath(); |
|
|
|
if (!resolvedPath.startsWith(appDataPath)) { |
|
|
|
throw new Error(`Database path must be within app data directory: ${resolvedPath}`); |
|
|
|
throw new Error( |
|
|
|
`Database path must be within app data directory: ${resolvedPath}`, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Normalize the path
|
|
|
|
const normalizedPath = path.normalize(resolvedPath); |
|
|
|
|
|
|
|
|
|
|
|
logger.info("[Electron] Validated database path:", { |
|
|
|
original: filePath, |
|
|
|
resolved: resolvedPath, |
|
|
|
normalized: normalizedPath, |
|
|
|
appDataPath, |
|
|
|
isAbsolute: path.isAbsolute(normalizedPath), |
|
|
|
isWithinAppData: normalizedPath.startsWith(appDataPath) |
|
|
|
isWithinAppData: normalizedPath.startsWith(appDataPath), |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return normalizedPath; |
|
|
|
}; |
|
|
|
|
|
|
@ -134,34 +139,46 @@ const ensureDirectoryExists = async (dirPath: string): Promise<void> => { |
|
|
|
try { |
|
|
|
// Normalize the path first
|
|
|
|
const normalizedPath = path.normalize(dirPath); |
|
|
|
|
|
|
|
|
|
|
|
// Check if directory exists
|
|
|
|
if (!fs.existsSync(normalizedPath)) { |
|
|
|
logger.info("[Electron] Creating database directory:", normalizedPath); |
|
|
|
await fs.promises.mkdir(normalizedPath, { recursive: true }); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Verify directory permissions
|
|
|
|
try { |
|
|
|
await fs.promises.access(normalizedPath, fs.constants.R_OK | fs.constants.W_OK); |
|
|
|
logger.info("[Electron] Database directory permissions verified:", normalizedPath); |
|
|
|
await fs.promises.access( |
|
|
|
normalizedPath, |
|
|
|
fs.constants.R_OK | fs.constants.W_OK, |
|
|
|
); |
|
|
|
logger.info( |
|
|
|
"[Electron] Database directory permissions verified:", |
|
|
|
normalizedPath, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Database directory permission error:", error); |
|
|
|
throw new Error(`Database directory not accessible: ${normalizedPath}`); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Test write permissions
|
|
|
|
const testFile = path.join(normalizedPath, '.write-test'); |
|
|
|
const testFile = path.join(normalizedPath, ".write-test"); |
|
|
|
try { |
|
|
|
await fs.promises.writeFile(testFile, 'test'); |
|
|
|
await fs.promises.writeFile(testFile, "test"); |
|
|
|
await fs.promises.unlink(testFile); |
|
|
|
logger.info("[Electron] Database directory write test passed:", normalizedPath); |
|
|
|
logger.info( |
|
|
|
"[Electron] Database directory write test passed:", |
|
|
|
normalizedPath, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Database directory write test failed:", error); |
|
|
|
throw new Error(`Database directory not writable: ${normalizedPath}`); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Failed to ensure database directory exists:", error); |
|
|
|
logger.error( |
|
|
|
"[Electron] Failed to ensure database directory exists:", |
|
|
|
error, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}; |
|
|
@ -177,35 +194,43 @@ const initializeDatabasePaths = async (): Promise<void> => { |
|
|
|
if (dbPathInitializationPromise) { |
|
|
|
return dbPathInitializationPromise; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (dbPathInitialized) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
dbPathInitializationPromise = (async () => { |
|
|
|
try { |
|
|
|
// Get the base directory from config
|
|
|
|
dbDir = await getAppDataPath(); |
|
|
|
logger.info("[Electron] Database directory:", dbDir); |
|
|
|
|
|
|
|
|
|
|
|
// Ensure the directory exists and is writable
|
|
|
|
await ensureDirectoryExists(dbDir); |
|
|
|
|
|
|
|
|
|
|
|
// Construct the database path
|
|
|
|
dbPath = await validateAndNormalizePath(path.join(dbDir, 'timesafari.db')); |
|
|
|
dbPath = await validateAndNormalizePath( |
|
|
|
path.join(dbDir, "timesafari.db"), |
|
|
|
); |
|
|
|
logger.info("[Electron] Database path initialized:", dbPath); |
|
|
|
|
|
|
|
|
|
|
|
// Verify the database file if it exists
|
|
|
|
if (fs.existsSync(dbPath)) { |
|
|
|
try { |
|
|
|
await fs.promises.access(dbPath, fs.constants.R_OK | fs.constants.W_OK); |
|
|
|
logger.info("[Electron] Existing database file permissions verified:", dbPath); |
|
|
|
await fs.promises.access( |
|
|
|
dbPath, |
|
|
|
fs.constants.R_OK | fs.constants.W_OK, |
|
|
|
); |
|
|
|
logger.info( |
|
|
|
"[Electron] Existing database file permissions verified:", |
|
|
|
dbPath, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Database file permission error:", error); |
|
|
|
throw new Error(`Database file not accessible: ${dbPath}`); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
dbPathInitialized = true; |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Failed to initialize database paths:", error); |
|
|
@ -214,7 +239,7 @@ const initializeDatabasePaths = async (): Promise<void> => { |
|
|
|
dbPathInitializationPromise = null; |
|
|
|
} |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
return dbPathInitializationPromise; |
|
|
|
}; |
|
|
|
|
|
|
@ -228,27 +253,27 @@ async function initializeSQLite() { |
|
|
|
if (sqliteInitializationPromise) { |
|
|
|
return sqliteInitializationPromise; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (sqliteInitialized) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sqliteInitializationPromise = (async () => { |
|
|
|
try { |
|
|
|
logger.info("[Electron] Initializing SQLite plugin..."); |
|
|
|
sqlitePlugin = new CapacitorSQLite(); |
|
|
|
|
|
|
|
|
|
|
|
// Initialize database paths first
|
|
|
|
await initializeDatabasePaths(); |
|
|
|
|
|
|
|
|
|
|
|
if (!dbPath) { |
|
|
|
throw new Error("Database path not initialized"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Test the plugin
|
|
|
|
const echoResult = await sqlitePlugin.echo({ value: "test" }); |
|
|
|
logger.info("[Electron] SQLite plugin echo test:", echoResult); |
|
|
|
|
|
|
|
|
|
|
|
// Initialize database connection using validated dbPath
|
|
|
|
const connectionOptions = { |
|
|
|
database: dbPath, |
|
|
@ -256,34 +281,42 @@ async function initializeSQLite() { |
|
|
|
readOnly: false, |
|
|
|
encryption: "no-encryption", |
|
|
|
useNative: true, |
|
|
|
mode: "rwc" // Force read-write-create mode
|
|
|
|
mode: "rwc", // Force read-write-create mode
|
|
|
|
}; |
|
|
|
|
|
|
|
logger.info("[Electron] Creating initial connection with options:", connectionOptions); |
|
|
|
|
|
|
|
|
|
|
|
logger.info( |
|
|
|
"[Electron] Creating initial connection with options:", |
|
|
|
connectionOptions, |
|
|
|
); |
|
|
|
|
|
|
|
// Log the actual path being used
|
|
|
|
logger.info("[Electron] Using database path:", dbPath); |
|
|
|
logger.info("[Electron] Path exists:", fs.existsSync(dbPath)); |
|
|
|
logger.info("[Electron] Path is absolute:", path.isAbsolute(dbPath)); |
|
|
|
|
|
|
|
|
|
|
|
const db = await sqlitePlugin.createConnection(connectionOptions); |
|
|
|
|
|
|
|
if (!db || typeof db !== 'object') { |
|
|
|
throw new Error(`Failed to create database connection - invalid response. Path used: ${dbPath}`); |
|
|
|
|
|
|
|
if (!db || typeof db !== "object") { |
|
|
|
throw new Error( |
|
|
|
`Failed to create database connection - invalid response. Path used: ${dbPath}`, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Wait a moment for the connection to be fully established
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100)); |
|
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100)); |
|
|
|
|
|
|
|
// Verify the connection is working
|
|
|
|
try { |
|
|
|
const result = await db.query("PRAGMA journal_mode;"); |
|
|
|
logger.info("[Electron] Database connection verified:", result); |
|
|
|
} catch (error) { |
|
|
|
logger.error("[Electron] Database connection verification failed:", error); |
|
|
|
logger.error( |
|
|
|
"[Electron] Database connection verification failed:", |
|
|
|
error, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sqliteInitialized = true; |
|
|
|
logger.info("[Electron] SQLite plugin initialized successfully"); |
|
|
|
} catch (error) { |
|
|
@ -293,42 +326,45 @@ async function initializeSQLite() { |
|
|
|
sqliteInitializationPromise = null; |
|
|
|
} |
|
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
return sqliteInitializationPromise; |
|
|
|
} |
|
|
|
|
|
|
|
// Initialize app when ready
|
|
|
|
app.whenReady().then(async () => { |
|
|
|
logger.info("App is ready, starting initialization..."); |
|
|
|
|
|
|
|
|
|
|
|
// Create window first
|
|
|
|
const mainWindow = createWindow(); |
|
|
|
|
|
|
|
|
|
|
|
// Initialize database in background
|
|
|
|
initializeSQLite().catch((error) => { |
|
|
|
logger.error("[Electron] Database initialization failed, but continuing:", error); |
|
|
|
logger.error( |
|
|
|
"[Electron] Database initialization failed, but continuing:", |
|
|
|
error, |
|
|
|
); |
|
|
|
// Notify renderer about database status
|
|
|
|
if (!mainWindow.isDestroyed()) { |
|
|
|
mainWindow.webContents.send('database-status', { |
|
|
|
status: 'error', |
|
|
|
error: error.message |
|
|
|
mainWindow.webContents.send("database-status", { |
|
|
|
status: "error", |
|
|
|
error: error.message, |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// Handle window close
|
|
|
|
mainWindow.on('closed', () => { |
|
|
|
mainWindow.on("closed", () => { |
|
|
|
logger.info("[Electron] Main window closed"); |
|
|
|
}); |
|
|
|
|
|
|
|
// Handle window close request
|
|
|
|
mainWindow.on('close', (event) => { |
|
|
|
mainWindow.on("close", (event) => { |
|
|
|
logger.info("[Electron] Window close requested"); |
|
|
|
// Prevent immediate close if we're in the middle of something
|
|
|
|
if (mainWindow.webContents.isLoading()) { |
|
|
|
event.preventDefault(); |
|
|
|
logger.info("[Electron] Deferring window close due to loading state"); |
|
|
|
mainWindow.webContents.once('did-finish-load', () => { |
|
|
|
mainWindow.webContents.once("did-finish-load", () => { |
|
|
|
mainWindow.close(); |
|
|
|
}); |
|
|
|
} |
|
|
@ -378,25 +414,32 @@ function createWindow(): BrowserWindow { |
|
|
|
sandbox: false, |
|
|
|
preload: preloadPath, |
|
|
|
webSecurity: true, |
|
|
|
allowRunningInsecureContent: false |
|
|
|
allowRunningInsecureContent: false, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
// Show window when ready
|
|
|
|
mainWindow.once('ready-to-show', () => { |
|
|
|
mainWindow.once("ready-to-show", () => { |
|
|
|
logger.info("[Electron] Window ready to show"); |
|
|
|
mainWindow.show(); |
|
|
|
}); |
|
|
|
|
|
|
|
// Handle window errors
|
|
|
|
mainWindow.webContents.on('render-process-gone', (_event, details) => { |
|
|
|
mainWindow.webContents.on("render-process-gone", (_event, details) => { |
|
|
|
logger.error("[Electron] Render process gone:", details); |
|
|
|
}); |
|
|
|
|
|
|
|
mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription) => { |
|
|
|
logger.error("[Electron] Page failed to load:", errorCode, errorDescription); |
|
|
|
logger.error("[Electron] Failed URL:", mainWindow.webContents.getURL()); |
|
|
|
}); |
|
|
|
mainWindow.webContents.on( |
|
|
|
"did-fail-load", |
|
|
|
(_event, errorCode, errorDescription) => { |
|
|
|
logger.error( |
|
|
|
"[Electron] Page failed to load:", |
|
|
|
errorCode, |
|
|
|
errorDescription, |
|
|
|
); |
|
|
|
logger.error("[Electron] Failed URL:", mainWindow.webContents.getURL()); |
|
|
|
}, |
|
|
|
); |
|
|
|
|
|
|
|
// Load the index.html
|
|
|
|
let indexPath: string; |
|
|
@ -405,11 +448,15 @@ function createWindow(): BrowserWindow { |
|
|
|
if (app.isPackaged) { |
|
|
|
indexPath = path.join(process.resourcesPath, "www", "index.html"); |
|
|
|
fileUrl = `file://${indexPath}`; |
|
|
|
logger.info("[Electron] App is packaged. Using process.resourcesPath for index.html"); |
|
|
|
logger.info( |
|
|
|
"[Electron] App is packaged. Using process.resourcesPath for index.html", |
|
|
|
); |
|
|
|
} else { |
|
|
|
indexPath = path.resolve(__dirname, "www", "index.html"); |
|
|
|
fileUrl = `file://${indexPath}`; |
|
|
|
logger.info("[Electron] App is not packaged. Using __dirname for index.html"); |
|
|
|
logger.info( |
|
|
|
"[Electron] App is not packaged. Using __dirname for index.html", |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
logger.info("[Electron] Resolved index.html path:", indexPath); |
|
|
@ -417,14 +464,16 @@ function createWindow(): BrowserWindow { |
|
|
|
|
|
|
|
// Load the index.html with retry logic
|
|
|
|
const loadIndexHtml = async (retryCount = 0): Promise<void> => { |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (mainWindow.isDestroyed()) { |
|
|
|
logger.error("[Electron] Window was destroyed before loading index.html"); |
|
|
|
logger.error( |
|
|
|
"[Electron] Window was destroyed before loading index.html", |
|
|
|
); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const exists = fs.existsSync(indexPath); |
|
|
|
logger.info(`[Electron] fs.existsSync for index.html: ${exists}`); |
|
|
|
const exists = fs.existsSync(indexPath); |
|
|
|
logger.info(`[Electron] fs.existsSync for index.html: ${exists}`); |
|
|
|
|
|
|
|
if (!exists) { |
|
|
|
throw new Error(`index.html not found at path: ${indexPath}`); |
|
|
@ -436,7 +485,7 @@ function createWindow(): BrowserWindow { |
|
|
|
size: stats.size, |
|
|
|
mode: stats.mode, |
|
|
|
uid: stats.uid, |
|
|
|
gid: stats.gid |
|
|
|
gid: stats.gid, |
|
|
|
}); |
|
|
|
|
|
|
|
// Try loadURL first
|
|
|
@ -445,22 +494,28 @@ function createWindow(): BrowserWindow { |
|
|
|
await mainWindow.loadURL(fileUrl); |
|
|
|
logger.info("[Electron] Successfully loaded index.html via loadURL"); |
|
|
|
} catch (loadUrlError) { |
|
|
|
logger.warn("[Electron] loadURL failed, trying loadFile:", loadUrlError); |
|
|
|
logger.warn( |
|
|
|
"[Electron] loadURL failed, trying loadFile:", |
|
|
|
loadUrlError, |
|
|
|
); |
|
|
|
// Fallback to loadFile
|
|
|
|
await mainWindow.loadFile(indexPath); |
|
|
|
logger.info("[Electron] Successfully loaded index.html via loadFile"); |
|
|
|
} |
|
|
|
} catch (error: unknown) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; |
|
|
|
const errorMessage = |
|
|
|
error instanceof Error ? error.message : "Unknown error occurred"; |
|
|
|
logger.error("[Electron] Error loading index.html:", errorMessage); |
|
|
|
|
|
|
|
|
|
|
|
// Retry logic
|
|
|
|
if (retryCount < 3 && !mainWindow.isDestroyed()) { |
|
|
|
logger.info(`[Electron] Retrying index.html load (attempt ${retryCount + 1})`); |
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
|
|
|
|
logger.info( |
|
|
|
`[Electron] Retrying index.html load (attempt ${retryCount + 1})`, |
|
|
|
); |
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
|
|
|
|
return loadIndexHtml(retryCount + 1); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If we've exhausted retries, show error in window
|
|
|
|
if (!mainWindow.isDestroyed()) { |
|
|
|
const errorHtml = ` |
|
|
@ -472,7 +527,9 @@ function createWindow(): BrowserWindow { |
|
|
|
</body> |
|
|
|
</html> |
|
|
|
`;
|
|
|
|
await mainWindow.loadURL(`data:text/html,${encodeURIComponent(errorHtml)}`); |
|
|
|
await mainWindow.loadURL( |
|
|
|
`data:text/html,${encodeURIComponent(errorHtml)}`, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
@ -520,7 +577,12 @@ ipcMain.handle("sqlite-echo", async (_event, value) => { |
|
|
|
try { |
|
|
|
return await sqlitePlugin.echo({ value }); |
|
|
|
} catch (error) { |
|
|
|
logger.error("Error in sqlite-echo:", error, JSON.stringify(error), (error as any)?.stack); |
|
|
|
logger.error( |
|
|
|
"Error in sqlite-echo:", |
|
|
|
error, |
|
|
|
JSON.stringify(error), |
|
|
|
(error as any)?.stack, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}); |
|
|
@ -529,11 +591,11 @@ ipcMain.handle("sqlite-create-connection", async (_event, options) => { |
|
|
|
try { |
|
|
|
// Ensure database is initialized
|
|
|
|
await initializeSQLite(); |
|
|
|
|
|
|
|
|
|
|
|
if (!dbPath) { |
|
|
|
throw new Error("Database path not initialized"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Override any provided database path with our resolved path
|
|
|
|
const connectionOptions = { |
|
|
|
...options, |
|
|
@ -541,31 +603,40 @@ ipcMain.handle("sqlite-create-connection", async (_event, options) => { |
|
|
|
readOnly: false, |
|
|
|
mode: "rwc", // Force read-write-create mode
|
|
|
|
encryption: "no-encryption", |
|
|
|
useNative: true |
|
|
|
useNative: true, |
|
|
|
}; |
|
|
|
|
|
|
|
logger.info("[Electron] Creating database connection with options:", connectionOptions); |
|
|
|
|
|
|
|
logger.info( |
|
|
|
"[Electron] Creating database connection with options:", |
|
|
|
connectionOptions, |
|
|
|
); |
|
|
|
const result = await sqlitePlugin.createConnection(connectionOptions); |
|
|
|
|
|
|
|
if (!result || typeof result !== 'object') { |
|
|
|
throw new Error("Failed to create database connection - invalid response"); |
|
|
|
|
|
|
|
if (!result || typeof result !== "object") { |
|
|
|
throw new Error( |
|
|
|
"Failed to create database connection - invalid response", |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Wait a moment for the connection to be fully established
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100)); |
|
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100)); |
|
|
|
|
|
|
|
try { |
|
|
|
// Verify connection is not read-only
|
|
|
|
const testResult = await result.query({ statement: "PRAGMA journal_mode;" }); |
|
|
|
const testResult = await result.query({ |
|
|
|
statement: "PRAGMA journal_mode;", |
|
|
|
}); |
|
|
|
if (testResult?.values?.[0]?.journal_mode === "off") { |
|
|
|
logger.error("[Electron] Connection opened in read-only mode despite options"); |
|
|
|
logger.error( |
|
|
|
"[Electron] Connection opened in read-only mode despite options", |
|
|
|
); |
|
|
|
throw new Error("Database connection opened in read-only mode"); |
|
|
|
} |
|
|
|
} catch (queryError) { |
|
|
|
logger.error("[Electron] Error verifying connection:", queryError); |
|
|
|
throw queryError; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
logger.info("[Electron] Database connection created successfully"); |
|
|
|
return result; |
|
|
|
} catch (error) { |
|
|
@ -578,7 +649,12 @@ ipcMain.handle("sqlite-execute", async (_event, options) => { |
|
|
|
try { |
|
|
|
return await sqlitePlugin.execute(options); |
|
|
|
} catch (error) { |
|
|
|
logger.error("Error in sqlite-execute:", error, JSON.stringify(error), (error as any)?.stack); |
|
|
|
logger.error( |
|
|
|
"Error in sqlite-execute:", |
|
|
|
error, |
|
|
|
JSON.stringify(error), |
|
|
|
(error as any)?.stack, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}); |
|
|
@ -587,7 +663,12 @@ ipcMain.handle("sqlite-query", async (_event, options) => { |
|
|
|
try { |
|
|
|
return await sqlitePlugin.query(options); |
|
|
|
} catch (error) { |
|
|
|
logger.error("Error in sqlite-query:", error, JSON.stringify(error), (error as any)?.stack); |
|
|
|
logger.error( |
|
|
|
"Error in sqlite-query:", |
|
|
|
error, |
|
|
|
JSON.stringify(error), |
|
|
|
(error as any)?.stack, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}); |
|
|
@ -596,7 +677,12 @@ ipcMain.handle("sqlite-close-connection", async (_event, options) => { |
|
|
|
try { |
|
|
|
return await sqlitePlugin.closeConnection(options); |
|
|
|
} catch (error) { |
|
|
|
logger.error("Error in sqlite-close-connection:", error, JSON.stringify(error), (error as any)?.stack); |
|
|
|
logger.error( |
|
|
|
"Error in sqlite-close-connection:", |
|
|
|
error, |
|
|
|
JSON.stringify(error), |
|
|
|
(error as any)?.stack, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}); |
|
|
|