@ -83,20 +83,20 @@ const getAppDataPath = async (): Promise<string> => {
if ( linuxPath ) {
// Expand ~ to home directory
const expandedPath = linuxPath . replace ( /^~/ , process . env . HOME || "" ) ;
logger . info ( "[Electron] Using configured database path:" , expandedPath ) ;
logger . info ( "[Electron Main ] Using configured database path:" , expandedPath ) ;
return expandedPath ;
}
// Fallback to app.getPath if config path is not available
const userDataPath = app . getPath ( "userData" ) ;
logger . info ( "[Electron] Using fallback user data path:" , userDataPath ) ;
logger . info ( "[Electron Main ] Using fallback user data path:" , userDataPath ) ;
return userDataPath ;
} catch ( error ) {
logger . error ( "[Electron] Error getting app data path:" , error ) ;
logger . error ( "[Electron Main ] 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:" ,
"[Electron Main ] Using fallback user data path after error:" ,
userDataPath ,
) ;
return userDataPath ;
@ -123,7 +123,7 @@ const validateAndNormalizePath = async (filePath: string): Promise<string> => {
// Normalize the path
const normalizedPath = path . normalize ( resolvedPath ) ;
logger . info ( "[Electron] Validated database path:" , {
logger . info ( "[Electron Main ] Validated database path:" , {
original : filePath ,
resolved : resolvedPath ,
normalized : normalizedPath ,
@ -142,7 +142,7 @@ const ensureDirectoryExists = async (dirPath: string): Promise<void> => {
// Check if directory exists
if ( ! fs . existsSync ( normalizedPath ) ) {
logger . info ( "[Electron] Creating database directory:" , normalizedPath ) ;
logger . info ( "[Electron Main ] Creating database directory:" , normalizedPath ) ;
await fs . promises . mkdir ( normalizedPath , { recursive : true } ) ;
}
@ -153,11 +153,11 @@ const ensureDirectoryExists = async (dirPath: string): Promise<void> => {
fs . constants . R_OK | fs . constants . W_OK ,
) ;
logger . info (
"[Electron] Database directory permissions verified:" ,
"[Electron Main ] Database directory permissions verified:" ,
normalizedPath ,
) ;
} catch ( error ) {
logger . error ( "[Electron] Database directory permission error:" , error ) ;
logger . error ( "[Electron Main ] Database directory permission error:" , error ) ;
throw new Error ( ` Database directory not accessible: ${ normalizedPath } ` ) ;
}
@ -167,16 +167,16 @@ const ensureDirectoryExists = async (dirPath: string): Promise<void> => {
await fs . promises . writeFile ( testFile , "test" ) ;
await fs . promises . unlink ( testFile ) ;
logger . info (
"[Electron] Database directory write test passed:" ,
"[Electron Main ] Database directory write test passed:" ,
normalizedPath ,
) ;
} catch ( error ) {
logger . error ( "[Electron] Database directory write test failed:" , error ) ;
logger . error ( "[Electron Main ] 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:" ,
"[Electron Main ] Failed to ensure database directory exists:" ,
error ,
) ;
throw error ;
@ -203,7 +203,7 @@ const initializeDatabasePaths = async (): Promise<void> => {
try {
// Get the base directory from config
dbDir = await getAppDataPath ( ) ;
logger . info ( "[Electron] Database directory:" , dbDir ) ;
logger . info ( "[Electron Main ] Database directory:" , dbDir ) ;
// Ensure the directory exists and is writable
await ensureDirectoryExists ( dbDir ) ;
@ -212,7 +212,7 @@ const initializeDatabasePaths = async (): Promise<void> => {
dbPath = await validateAndNormalizePath (
path . join ( dbDir , "timesafari.db" ) ,
) ;
logger . info ( "[Electron] Database path initialized:" , dbPath ) ;
logger . info ( "[Electron Main ] Database path initialized:" , dbPath ) ;
// Verify the database file if it exists
if ( fs . existsSync ( dbPath ) ) {
@ -222,18 +222,18 @@ const initializeDatabasePaths = async (): Promise<void> => {
fs . constants . R_OK | fs . constants . W_OK ,
) ;
logger . info (
"[Electron] Existing database file permissions verified:" ,
"[Electron Main ] Existing database file permissions verified:" ,
dbPath ,
) ;
} catch ( error ) {
logger . error ( "[Electron] Database file permission error:" , error ) ;
logger . error ( "[Electron Main ] 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 ) ;
logger . error ( "[Electron Main ] Failed to initialize database paths:" , error ) ;
throw error ;
} finally {
dbPathInitializationPromise = null ;
@ -260,7 +260,7 @@ async function initializeSQLite() {
sqliteInitializationPromise = ( async ( ) = > {
try {
logger . info ( "[Electron] Initializing SQLite plugin..." ) ;
logger . info ( "[Electron Main ] Initializing SQLite plugin..." ) ;
sqlitePlugin = new CapacitorSQLite ( ) ;
// Initialize database paths first
@ -272,7 +272,7 @@ async function initializeSQLite() {
// Test the plugin
const echoResult = await sqlitePlugin . echo ( { value : "test" } ) ;
logger . info ( "[Electron] SQLite plugin echo test:" , echoResult ) ;
logger . info ( "[Electron Main ] SQLite plugin echo test:" , echoResult ) ;
// Initialize database connection using validated dbPath
const connectionOptions = {
@ -285,14 +285,14 @@ async function initializeSQLite() {
} ;
logger . info (
"[Electron] Creating initial connection with options:" ,
"[Electron Main ] 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 ) ) ;
logger . info ( "[Electron Main ] Using database path:" , dbPath ) ;
logger . info ( "[Electron Main ] Path exists:" , fs . existsSync ( dbPath ) ) ;
logger . info ( "[Electron Main ] Path is absolute:" , path . isAbsolute ( dbPath ) ) ;
const db = await sqlitePlugin . createConnection ( connectionOptions ) ;
@ -308,19 +308,19 @@ async function initializeSQLite() {
// Verify the connection is working
try {
const result = await db . query ( "PRAGMA journal_mode;" ) ;
logger . info ( "[Electron] Database connection verified:" , result ) ;
logger . info ( "[Electron Main ] Database connection verified:" , result ) ;
} catch ( error ) {
logger . error (
"[Electron] Database connection verification failed:" ,
"[Electron Main ] Database connection verification failed:" ,
error ,
) ;
throw error ;
}
sqliteInitialized = true ;
logger . info ( "[Electron] SQLite plugin initialized successfully" ) ;
logger . info ( "[Electron Main ] SQLite plugin initialized successfully" ) ;
} catch ( error ) {
logger . error ( "[Electron] Failed to initialize SQLite plugin:" , error ) ;
logger . error ( "[Electron Main ] Failed to initialize SQLite plugin:" , error ) ;
throw error ;
} finally {
sqliteInitializationPromise = null ;
@ -337,33 +337,53 @@ app.whenReady().then(async () => {
// Create window first
const mainWindow = createWindow ( ) ;
// Initialize database in background
initializeSQLite ( ) . catch ( ( error ) = > {
// Wait for window to be ready
await new Promise < void > ( ( resolve ) = > {
mainWindow . once ( 'ready-to-show' , ( ) = > {
logger . info ( "[Electron Main] Window ready to show" ) ;
mainWindow . show ( ) ;
resolve ( ) ;
} ) ;
} ) ;
// Initialize database after window is ready
try {
await initializeSQLite ( ) ;
logger . info ( "[Electron Main] SQLite plugin initialized successfully" ) ;
// Now send the ready signal since window is ready
if ( ! mainWindow . isDestroyed ( ) ) {
mainWindow . webContents . send ( 'sqlite-ready' ) ;
logger . info ( "[Electron Main] Sent SQLite ready signal to renderer" ) ;
} else {
logger . error ( "[Electron Main] Window was destroyed before sending SQLite ready signal" ) ;
}
} catch ( error ) {
logger . error (
"[Electron] Database initialization failed, but continuing:" ,
"[Electron Main ] Database initialization failed:" ,
error ,
) ;
// Notify renderer about database status
if ( ! mainWindow . isDestroyed ( ) ) {
mainWindow . webContents . send ( "database-status" , {
status : "error" ,
error : error.message ,
error : error instanceof Error ? error . message : 'Unknown error' ,
} ) ;
}
} ) ;
}
// Handle window close
mainWindow . on ( "closed" , ( ) = > {
logger . info ( "[Electron] Main window closed" ) ;
logger . info ( "[Electron Main ] Main window closed" ) ;
} ) ;
// Handle window close request
mainWindow . on ( "close" , ( event ) = > {
logger . info ( "[Electron] Window close requested" ) ;
logger . info ( "[Electron Main ] 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" ) ;
logger . info ( "[Electron Main ] Deferring window close due to loading state" ) ;
mainWindow . webContents . once ( "did-finish-load" , ( ) = > {
mainWindow . close ( ) ;
} ) ;
@ -380,15 +400,15 @@ function createWindow(): BrowserWindow {
? path . join ( process . resourcesPath , "preload.js" )
: path . join ( __dirname , "preload.js" ) ;
logger . log ( "[Electron] Preload path:" , preloadPath ) ;
logger . log ( "[Electron] Preload exists:" , fs . existsSync ( preloadPath ) ) ;
logger . log ( "[Electron Main ] Preload path:" , preloadPath ) ;
logger . log ( "[Electron Main ] Preload exists:" , fs . existsSync ( preloadPath ) ) ;
// Log environment and paths
logger . log ( "[Electron] process.cwd():" , process . cwd ( ) ) ;
logger . log ( "[Electron] __dirname:" , __dirname ) ;
logger . log ( "[Electron] app.getAppPath():" , app . getAppPath ( ) ) ;
logger . log ( "[Electron] app.isPackaged:" , app . isPackaged ) ;
logger . log ( "[Electron] process.resourcesPath:" , process . resourcesPath ) ;
logger . log ( "[Electron Main ] process.cwd():" , process . cwd ( ) ) ;
logger . log ( "[Electron Main ] __dirname:" , __dirname ) ;
logger . log ( "[Electron Main ] app.getAppPath():" , app . getAppPath ( ) ) ;
logger . log ( "[Electron Main ] app.isPackaged:" , app . isPackaged ) ;
logger . log ( "[Electron Main ] process.resourcesPath:" , process . resourcesPath ) ;
// List files in __dirname and __dirname/www
try {
@ -420,24 +440,24 @@ function createWindow(): BrowserWindow {
// Show window when ready
mainWindow . once ( "ready-to-show" , ( ) = > {
logger . info ( "[Electron] Window ready to show" ) ;
logger . info ( "[Electron Main ] Window ready to show" ) ;
mainWindow . show ( ) ;
} ) ;
// Handle window errors
mainWindow . webContents . on ( "render-process-gone" , ( _event , details ) = > {
logger . error ( "[Electron] Render process gone:" , details ) ;
logger . error ( "[Electron Main ] Render process gone:" , details ) ;
} ) ;
mainWindow . webContents . on (
"did-fail-load" ,
( _event , errorCode , errorDescription ) = > {
logger . error (
"[Electron] Page failed to load:" ,
"[Electron Main ] Page failed to load:" ,
errorCode ,
errorDescription ,
) ;
logger . error ( "[Electron] Failed URL:" , mainWindow . webContents . getURL ( ) ) ;
logger . error ( "[Electron Main ] Failed URL:" , mainWindow . webContents . getURL ( ) ) ;
} ,
) ;
@ -449,31 +469,31 @@ function createWindow(): BrowserWindow {
indexPath = path . join ( process . resourcesPath , "www" , "index.html" ) ;
fileUrl = ` file:// ${ indexPath } ` ;
logger . info (
"[Electron] App is packaged. Using process.resourcesPath for index.html" ,
"[Electron Main ] 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" ,
"[Electron Main ] App is not packaged. Using __dirname for index.html" ,
) ;
}
logger . info ( "[Electron] Resolved index.html path:" , indexPath ) ;
logger . info ( "[Electron] Using file URL:" , fileUrl ) ;
logger . info ( "[Electron Main ] Resolved index.html path:" , indexPath ) ;
logger . info ( "[Electron Main ] Using file URL:" , fileUrl ) ;
// Load the index.html with retry logic
const loadIndexHtml = async ( retryCount = 0 ) : Promise < void > = > {
try {
if ( mainWindow . isDestroyed ( ) ) {
logger . error (
"[Electron] Window was destroyed before loading index.html" ,
"[Electron Main ] Window was destroyed before loading index.html" ,
) ;
return ;
}
const exists = fs . existsSync ( indexPath ) ;
logger . info ( ` [Electron] fs.existsSync for index.html: ${ exists } ` ) ;
logger . info ( ` [Electron Main ] fs.existsSync for index.html: ${ exists } ` ) ;
if ( ! exists ) {
throw new Error ( ` index.html not found at path: ${ indexPath } ` ) ;
@ -481,7 +501,7 @@ function createWindow(): BrowserWindow {
// Try to read the file to verify it's accessible
const stats = fs . statSync ( indexPath ) ;
logger . info ( "[Electron] index.html stats:" , {
logger . info ( "[Electron Main ] index.html stats:" , {
size : stats.size ,
mode : stats.mode ,
uid : stats.uid ,
@ -490,27 +510,27 @@ function createWindow(): BrowserWindow {
// Try loadURL first
try {
logger . info ( "[Electron] Attempting to load index.html via loadURL" ) ;
logger . info ( "[Electron Main ] Attempting to load index.html via loadURL" ) ;
await mainWindow . loadURL ( fileUrl ) ;
logger . info ( "[Electron] Successfully loaded index.html via loadURL" ) ;
logger . info ( "[Electron Main ] Successfully loaded index.html via loadURL" ) ;
} catch ( loadUrlError ) {
logger . warn (
"[Electron] loadURL failed, trying loadFile:" ,
"[Electron Main ] loadURL failed, trying loadFile:" ,
loadUrlError ,
) ;
// Fallback to loadFile
await mainWindow . loadFile ( indexPath ) ;
logger . info ( "[Electron] Successfully loaded index.html via loadFile" ) ;
logger . info ( "[Electron Main ] Successfully loaded index.html via loadFile" ) ;
}
} catch ( error : unknown ) {
const errorMessage =
error instanceof Error ? error . message : "Unknown error occurred" ;
logger . error ( "[Electron] Error loading index.html:" , errorMessage ) ;
logger . error ( "[Electron Main ] Error loading index.html:" , errorMessage ) ;
// Retry logic
if ( retryCount < 3 && ! mainWindow . isDestroyed ( ) ) {
logger . info (
` [Electron] Retrying index.html load (attempt ${ retryCount + 1 } ) ` ,
` [Electron Main ] Retrying index.html load (attempt ${ retryCount + 1 } ) ` ,
) ;
await new Promise ( ( resolve ) = > setTimeout ( resolve , 1000 ) ) ; // Wait 1 second
return loadIndexHtml ( retryCount + 1 ) ;
@ -536,7 +556,7 @@ function createWindow(): BrowserWindow {
// Start loading the index.html
loadIndexHtml ( ) . catch ( ( error : unknown ) = > {
logger . error ( "[Electron] Fatal error loading index.html:" , error ) ;
logger . error ( "[Electron Main ] Fatal error loading index.html:" , error ) ;
} ) ;
// Only open DevTools if not in production
@ -607,7 +627,7 @@ ipcMain.handle("sqlite-create-connection", async (_event, options) => {
} ;
logger . info (
"[Electron] Creating database connection with options:" ,
"[Electron Main ] Creating database connection with options:" ,
connectionOptions ,
) ;
const result = await sqlitePlugin . createConnection ( connectionOptions ) ;
@ -628,19 +648,19 @@ ipcMain.handle("sqlite-create-connection", async (_event, options) => {
} ) ;
if ( testResult ? . values ? . [ 0 ] ? . journal_mode === "off" ) {
logger . error (
"[Electron] Connection opened in read-only mode despite options" ,
"[Electron Main ] 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 ) ;
logger . error ( "[Electron Main ] Error verifying connection:" , queryError ) ;
throw queryError ;
}
logger . info ( "[Electron] Database connection created successfully" ) ;
logger . info ( "[Electron Main ] Database connection created successfully" ) ;
return result ;
} catch ( error ) {
logger . error ( "[Electron] Error in sqlite-create-connection:" , error ) ;
logger . error ( "[Electron Main ] Error in sqlite-create-connection:" , error ) ;
throw error ;
}
} ) ;