@ -1,5 +1,6 @@
import { initializeApp } from "./main.common" ;
import { logger } from "./utils/logger" ;
import { SQLiteQueryResult } from "./services/platforms/ElectronPlatformService" ;
const platform = process . env . VITE_PLATFORM ;
const pwa_enabled = process . env . VITE_PWA_ENABLED === "true" ;
@ -12,6 +13,8 @@ if (pwa_enabled) {
logger . warn ( "[Main Electron] PWA is enabled, but not supported in electron" ) ;
}
let appIsMounted = false ;
// Initialize app and SQLite
const app = initializeApp ( ) ;
@ -72,95 +75,133 @@ const sqliteReady = new Promise<void>((resolve, reject) => {
// At this point we know ipcRenderer exists
const ipcRenderer = window . electron . ipcRenderer ;
logger . info ( "[Main Electron] IPC renderer bridge available" ) ;
logger . info ( "[Main Electron] [IPC:bridge] IPC renderer bridge available" ) ;
// Listen for SQLite ready signal
logger . debug ( "[Main Electron] [IPC:sqlite-ready] Registering listener for SQLite ready signal" ) ;
ipcRenderer . once ( 'sqlite-ready' , ( ) = > {
clearTimeout ( initializationTimeout ) ;
logger . info ( "[Main Electron] Received SQLite ready signal" ) ;
logger . info ( "[Main Electron] [IPC:sqlite-ready] Received SQLite ready signal" ) ;
resolve ( ) ;
} ) ;
// Also listen for database errors
logger . debug ( "[Main Electron] [IPC:database-status] Registering listener for database status" ) ;
ipcRenderer . once ( 'database-status' , ( . . . args : unknown [ ] ) = > {
clearTimeout ( initializationTimeout ) ;
const status = args [ 0 ] as { status : string ; error? : string } ;
if ( status . status === 'error' ) {
logger . error ( "[Main Electron] Database error:" , status . error ) ;
logger . error ( "[Main Electron] [IPC:database-status] Database error:" , {
error : status.error ,
channel : 'database-status'
} ) ;
reject ( new Error ( status . error || 'Database initialization failed' ) ) ;
}
} ) ;
// Check if SQLite is already available
logger . debug ( "[Main Electron] [IPC:sqlite-is-available] Checking SQLite availability" ) ;
ipcRenderer . invoke ( 'sqlite-is-available' )
. then ( async ( result : unknown ) = > {
const isAvailable = Boolean ( result ) ;
if ( isAvailable ) {
logger . info ( "[Main Electron] SQLite is already available" ) ;
logger . info ( "[Main Electron] [IPC:sqlite-is-available] SQLite is available" ) ;
try {
// First create a database connection
logger . debug ( "[Main Electron] [IPC:get-path] Requesting database path" ) ;
const dbPath = await ipcRenderer . invoke ( 'get-path' ) ;
logger . info ( "[Main Electron] Creating database connection :" , { dbPath } ) ;
logger . info ( "[Main Electron] [IPC:get-path] Database path received :" , { dbPath } ) ;
// Create the database connection
logger . debug ( "[Main Electron] [IPC:sqlite-create-connection] Creating database connection" ) ;
await ipcRenderer . invoke ( 'sqlite-create-connection' , {
database : 'timesafari' ,
version : 1
} ) ;
logger . info ( "[Main Electron] [IPC:sqlite-create-connection] Database connection created" ) ;
// Explicitly open the database
logger . debug ( "[Main Electron] [IPC:sqlite-open] Opening database" ) ;
await ipcRenderer . invoke ( 'sqlite-open' , {
database : 'timesafari'
} ) ;
logger . info ( "[Main Electron] Database opened successfully" ) ;
logger . info ( "[Main Electron] [IPC:sqlite-open] Database opened successfully" ) ;
// Verify the database is open
logger . debug ( "[Main Electron] [IPC:sqlite-is-db-open] Verifying database is open" ) ;
const isOpen = await ipcRenderer . invoke ( 'sqlite-is-db-open' , {
database : 'timesafari'
} ) ;
logger . info ( "[Main Electron] [IPC:sqlite-is-db-open] Database open status:" , { isOpen } ) ;
if ( ! isOpen ) {
throw new Error ( 'Database failed to open' ) ;
}
// Now execute the test query
logger . debug ( "[Main Electron] [IPC:sqlite-query] Executing test query" ) ;
const testQuery = await ipcRenderer . invoke ( 'sqlite-query' , {
database : 'timesafari' ,
statement : 'SELECT * FROM secret;'
} ) as SQLiteQueryResult ;
logger . info ( "[Main Electron] [IPC:sqlite-query] Test query successful:" , {
hasResults : Boolean ( testQuery ? . values ) ,
resultCount : testQuery?.values?.length ,
results : testQuery?.values
} ) ;
logger . info ( "[Main Electron] SQLite secret query successful:" , testQuery ) ;
// Close the database
logger . debug ( "[Main Electron] [IPC:sqlite-close] Closing database" ) ;
await ipcRenderer . invoke ( 'sqlite-close' , {
database : 'timesafari'
} ) ;
logger . info ( "[Main Electron] Database closed successfully" ) ;
logger . info ( "[Main Electron] [IPC:sqlite-close] Database closed successfully" ) ;
// Close the connection
logger . debug ( "[Main Electron] [IPC:sqlite-close-connection] Closing database connection" ) ;
await ipcRenderer . invoke ( 'sqlite-close-connection' , {
database : 'timesafari'
} ) ;
logger . info ( "[Main Electron] Database connection closed successfully" ) ;
logger . info ( "[Main Electron] [IPC:sqlite-close-connection] Database connection closed successfully" ) ;
} catch ( error ) {
logger . error ( "[Main Electron] SQLite test operation failed:" , error ) ;
logger . error ( "[Main Electron] [IPC:*] SQLite test operation failed:" , {
error ,
lastOperation : 'sqlite-test-query' ,
database : 'timesafari'
} ) ;
// Try to close everything if anything was opened
try {
logger . debug ( "[Main Electron] [IPC:cleanup] Attempting database cleanup after error" ) ;
await ipcRenderer . invoke ( 'sqlite-close' , {
database : 'timesafari'
} ) . catch ( ( ) = > { } ) ;
} ) . catch ( ( closeError ) = > {
logger . warn ( "[Main Electron] [IPC:sqlite-close] Failed to close database during cleanup:" , closeError ) ;
} ) ;
await ipcRenderer . invoke ( 'sqlite-close-connection' , {
database : 'timesafari'
} ) . catch ( ( ) = > { } ) ;
logger . info ( "[Main Electron] Database cleanup completed after error" ) ;
} ) . catch ( ( closeError ) = > {
logger . warn ( "[Main Electron] [IPC:sqlite-close-connection] Failed to close connection during cleanup:" , closeError ) ;
} ) ;
logger . info ( "[Main Electron] [IPC:cleanup] Database cleanup completed after error" ) ;
} catch ( closeError ) {
logger . error ( "[Main Electron] Failed to cleanup database:" , closeError ) ;
logger . error ( "[Main Electron] [IPC:cleanup] Failed to cleanup database:" , {
error : closeError ,
database : 'timesafari'
} ) ;
}
// Don't reject here - we still want to wait for the ready signal
}
}
} )
. catch ( ( error : Error ) = > {
logger . error ( "[Main Electron] Failed to check SQLite availability:" , error ) ;
logger . error ( "[Main Electron] [IPC:sqlite-is-available] Failed to check SQLite availability:" , {
error ,
channel : 'sqlite-is-available'
} ) ;
// Don't reject here - wait for either ready signal or timeout
} ) ;
} ;
@ -173,11 +214,20 @@ const sqliteReady = new Promise<void>((resolve, reject) => {
attemptInitialization ( ) ;
} ) ;
// Wait for SQLite to be ready before mounting
// Wait for SQLite to be ready before initializing router and mounting app
sqliteReady
. then ( ( ) = > {
logger . info ( "[Main Electron] SQLite ready, mounting app..." ) ;
. then ( async ( ) = > {
logger . info ( "[Main Electron] SQLite ready, initializing router..." ) ;
// Initialize router after SQLite is ready
const router = await import ( './router' ) . then ( m = > m . default ) ;
app . use ( router ) ;
logger . info ( "[Main Electron] Router initialized" ) ;
// Now mount the app
logger . info ( "[Main Electron] Mounting app..." ) ;
app . mount ( "#app" ) ;
appIsMounted = true ;
logger . info ( "[Main Electron] App mounted successfully" ) ;
} )
. catch ( ( error ) = > {