refactor: consolidate Electron API type definitions
- Create unified ElectronAPI interface in debug-electron.ts - Export SQLiteAPI, IPCRenderer, and ElectronEnv interfaces - Update Window.electron type declarations to use shared interface - Fix type conflicts between debug-electron.ts and ElectronPlatformService.ts This change improves type safety and maintainability by centralizing Electron API type definitions in a single location.
This commit is contained in:
@@ -26,6 +26,7 @@ export type {
|
||||
export type {
|
||||
// From records.ts
|
||||
PlanSummaryRecord,
|
||||
GiveSummaryRecord,
|
||||
} from "./records";
|
||||
|
||||
export type {
|
||||
|
||||
@@ -13,53 +13,33 @@ 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();
|
||||
|
||||
/**
|
||||
* SQLite initialization configuration
|
||||
* Defines timeouts and retry settings for database operations
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
*/
|
||||
const SQLITE_CONFIG = {
|
||||
INITIALIZATION: {
|
||||
TIMEOUT_MS: 10000, // 10 seconds for initial setup
|
||||
RETRY_ATTEMPTS: 3,
|
||||
RETRY_DELAY_MS: 1000
|
||||
},
|
||||
OPERATIONS: {
|
||||
TIMEOUT_MS: 5000, // 5 seconds for regular operations
|
||||
RETRY_ATTEMPTS: 2,
|
||||
RETRY_DELAY_MS: 500
|
||||
},
|
||||
CONNECTION: {
|
||||
MAX_CONNECTIONS: 5,
|
||||
IDLE_TIMEOUT_MS: 30000
|
||||
}
|
||||
};
|
||||
|
||||
// Create a promise that resolves when SQLite is ready
|
||||
const sqliteReady = new Promise<void>((resolve, reject) => {
|
||||
let retryCount = 0;
|
||||
let initializationTimeout: NodeJS.Timeout;
|
||||
|
||||
|
||||
const attemptInitialization = () => {
|
||||
// Clear any existing timeout
|
||||
if (initializationTimeout) {
|
||||
clearTimeout(initializationTimeout);
|
||||
}
|
||||
|
||||
|
||||
// Set timeout for this attempt
|
||||
initializationTimeout = setTimeout(() => {
|
||||
if (retryCount < 3) { // Use same retry count as ElectronPlatformService
|
||||
if (retryCount < 3) {
|
||||
// Use same retry count as ElectronPlatformService
|
||||
retryCount++;
|
||||
logger.warn(`[Main Electron] SQLite initialization attempt ${retryCount} timed out, retrying...`);
|
||||
logger.warn(
|
||||
`[Main Electron] SQLite initialization attempt ${retryCount} timed out, retrying...`,
|
||||
);
|
||||
setTimeout(attemptInitialization, 1000); // Use same delay as ElectronPlatformService
|
||||
} else {
|
||||
logger.error("[Main Electron] SQLite initialization failed after all retries");
|
||||
logger.error(
|
||||
"[Main Electron] SQLite initialization failed after all retries",
|
||||
);
|
||||
reject(new Error("SQLite initialization timeout after all retries"));
|
||||
}
|
||||
}, 10000); // Use same timeout as ElectronPlatformService
|
||||
@@ -71,134 +51,198 @@ const sqliteReady = new Promise<void>((resolve, reject) => {
|
||||
setTimeout(checkElectronBridge, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// At this point we know ipcRenderer exists
|
||||
const ipcRenderer = window.electron.ipcRenderer;
|
||||
|
||||
|
||||
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', () => {
|
||||
logger.debug(
|
||||
"[Main Electron] [IPC:sqlite-ready] Registering listener for SQLite ready signal",
|
||||
);
|
||||
ipcRenderer.once("sqlite-ready", () => {
|
||||
clearTimeout(initializationTimeout);
|
||||
logger.info("[Main Electron] [IPC:sqlite-ready] 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[]) => {
|
||||
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] [IPC:database-status] Database error:", {
|
||||
error: status.error,
|
||||
channel: 'database-status'
|
||||
});
|
||||
reject(new Error(status.error || 'Database initialization failed'));
|
||||
if (status.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')
|
||||
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] [IPC:sqlite-is-available] SQLite is 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] [IPC:get-path] Database path received:", { dbPath });
|
||||
|
||||
logger.debug(
|
||||
"[Main Electron] [IPC:get-path] Requesting database path",
|
||||
);
|
||||
const dbPath = await ipcRenderer.invoke("get-path");
|
||||
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.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");
|
||||
|
||||
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.debug(
|
||||
"[Main Electron] [IPC:sqlite-open] Opening database",
|
||||
);
|
||||
await ipcRenderer.invoke("sqlite-open", {
|
||||
database: "timesafari",
|
||||
});
|
||||
logger.info("[Main Electron] [IPC:sqlite-open] 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.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 });
|
||||
|
||||
logger.info(
|
||||
"[Main Electron] [IPC:sqlite-is-db-open] Database open status:",
|
||||
{ isOpen },
|
||||
);
|
||||
|
||||
if (!isOpen) {
|
||||
throw new Error('Database failed to open');
|
||||
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 1 as test;' // Safe test query
|
||||
}) as SQLiteQueryResult;
|
||||
logger.info("[Main Electron] [IPC:sqlite-query] Test query successful:", {
|
||||
hasResults: Boolean(testQuery?.values),
|
||||
resultCount: testQuery?.values?.length
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
"[Main Electron] [IPC:sqlite-query] Executing test query",
|
||||
);
|
||||
const testQuery = (await ipcRenderer.invoke("sqlite-query", {
|
||||
database: "timesafari",
|
||||
statement: "SELECT 1 as test;", // Safe test query
|
||||
})) as SQLiteQueryResult;
|
||||
logger.info(
|
||||
"[Main Electron] [IPC:sqlite-query] Test query successful:",
|
||||
{
|
||||
hasResults: Boolean(testQuery?.values),
|
||||
resultCount: testQuery?.values?.length,
|
||||
},
|
||||
);
|
||||
|
||||
// Signal that SQLite is ready - database stays open
|
||||
logger.debug("[Main Electron] [IPC:sqlite-status] Sending SQLite ready status");
|
||||
await ipcRenderer.invoke('sqlite-status', {
|
||||
status: 'ready',
|
||||
database: 'timesafari',
|
||||
timestamp: Date.now()
|
||||
logger.debug(
|
||||
"[Main Electron] [IPC:sqlite-status] Sending SQLite ready status",
|
||||
);
|
||||
await ipcRenderer.invoke("sqlite-status", {
|
||||
status: "ready",
|
||||
database: "timesafari",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
logger.info("[Main Electron] SQLite ready status sent, database connection maintained");
|
||||
logger.info(
|
||||
"[Main Electron] SQLite ready status sent, database connection maintained",
|
||||
);
|
||||
|
||||
// Remove the close operations - database stays open for component use
|
||||
// Database will be closed during app shutdown
|
||||
} catch (error) {
|
||||
logger.error("[Main Electron] [IPC:*] SQLite test operation failed:", {
|
||||
error,
|
||||
lastOperation: 'sqlite-test-query',
|
||||
database: 'timesafari'
|
||||
});
|
||||
|
||||
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((closeError) => {
|
||||
logger.warn("[Main Electron] [IPC:sqlite-close] Failed to close database during cleanup:", closeError);
|
||||
});
|
||||
|
||||
await ipcRenderer.invoke('sqlite-close-connection', {
|
||||
database: 'timesafari'
|
||||
}).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");
|
||||
logger.debug(
|
||||
"[Main Electron] [IPC:cleanup] Attempting database cleanup after error",
|
||||
);
|
||||
await ipcRenderer
|
||||
.invoke("sqlite-close", {
|
||||
database: "timesafari",
|
||||
})
|
||||
.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((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] [IPC:cleanup] Failed to cleanup database:", {
|
||||
error: closeError,
|
||||
database: 'timesafari'
|
||||
});
|
||||
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] [IPC:sqlite-is-available] Failed to check SQLite availability:", {
|
||||
error,
|
||||
channel: 'sqlite-is-available'
|
||||
});
|
||||
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
|
||||
});
|
||||
};
|
||||
@@ -215,20 +259,22 @@ const sqliteReady = new Promise<void>((resolve, reject) => {
|
||||
sqliteReady
|
||||
.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);
|
||||
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) => {
|
||||
logger.error("[Main Electron] Failed to initialize SQLite:", error instanceof Error ? error.message : 'Unknown error');
|
||||
logger.error(
|
||||
"[Main Electron] Failed to initialize SQLite:",
|
||||
error instanceof Error ? error.message : "Unknown error",
|
||||
);
|
||||
// Show error to user with retry option
|
||||
const errorDiv = document.createElement("div");
|
||||
errorDiv.style.cssText =
|
||||
@@ -241,7 +287,7 @@ sqliteReady
|
||||
<li>Insufficient permissions to access the database</li>
|
||||
<li>Database file is corrupted</li>
|
||||
</ul>
|
||||
<p>Error details: ${error instanceof Error ? error.message : 'Unknown error'}</p>
|
||||
<p>Error details: ${error instanceof Error ? error.message : "Unknown error"}</p>
|
||||
<div style="margin-top: 15px;">
|
||||
<button onclick="window.location.reload()" style="margin: 0 5px; padding: 8px 16px; background: #c62828; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Retry
|
||||
|
||||
@@ -115,7 +115,10 @@ export interface PlatformService {
|
||||
* @param params Query parameters
|
||||
* @returns Query results with columns and values
|
||||
*/
|
||||
dbQuery<T = unknown>(sql: string, params?: unknown[]): Promise<QueryExecResult<T>>;
|
||||
dbQuery<T = unknown>(
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
): Promise<QueryExecResult<T>>;
|
||||
|
||||
/**
|
||||
* Executes a create/update/delete on the database.
|
||||
|
||||
@@ -17,7 +17,10 @@ export class DatabaseConnectionPool {
|
||||
|
||||
private constructor() {
|
||||
// Start cleanup interval
|
||||
this.cleanupInterval = setInterval(() => this.cleanup(), this.CLEANUP_INTERVAL);
|
||||
this.cleanupInterval = setInterval(
|
||||
() => this.cleanup(),
|
||||
this.CLEANUP_INTERVAL,
|
||||
);
|
||||
}
|
||||
|
||||
public static getInstance(): DatabaseConnectionPool {
|
||||
@@ -29,14 +32,16 @@ export class DatabaseConnectionPool {
|
||||
|
||||
public async getConnection(
|
||||
dbName: string,
|
||||
createConnection: () => Promise<SQLiteDBConnection>
|
||||
createConnection: () => Promise<SQLiteDBConnection>,
|
||||
): Promise<SQLiteDBConnection> {
|
||||
// Check if we have an existing connection
|
||||
const existing = this.connections.get(dbName);
|
||||
if (existing && !existing.inUse) {
|
||||
existing.inUse = true;
|
||||
existing.lastUsed = Date.now();
|
||||
logger.debug(`[ConnectionPool] Reusing existing connection for ${dbName}`);
|
||||
logger.debug(
|
||||
`[ConnectionPool] Reusing existing connection for ${dbName}`,
|
||||
);
|
||||
return existing.connection;
|
||||
}
|
||||
|
||||
@@ -52,12 +57,15 @@ export class DatabaseConnectionPool {
|
||||
this.connections.set(dbName, {
|
||||
connection,
|
||||
lastUsed: Date.now(),
|
||||
inUse: true
|
||||
inUse: true,
|
||||
});
|
||||
logger.debug(`[ConnectionPool] Created new connection for ${dbName}`);
|
||||
return connection;
|
||||
} catch (error) {
|
||||
logger.error(`[ConnectionPool] Failed to create connection for ${dbName}:`, error);
|
||||
logger.error(
|
||||
`[ConnectionPool] Failed to create connection for ${dbName}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -89,9 +97,14 @@ export class DatabaseConnectionPool {
|
||||
try {
|
||||
await state.connection.close();
|
||||
this.connections.delete(dbName);
|
||||
logger.debug(`[ConnectionPool] Cleaned up idle connection for ${dbName}`);
|
||||
logger.debug(
|
||||
`[ConnectionPool] Cleaned up idle connection for ${dbName}`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.warn(`[ConnectionPool] Error closing idle connection for ${dbName}:`, error);
|
||||
logger.warn(
|
||||
`[ConnectionPool] Error closing idle connection for ${dbName}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,9 +121,12 @@ export class DatabaseConnectionPool {
|
||||
await state.connection.close();
|
||||
logger.debug(`[ConnectionPool] Closed connection for ${dbName}`);
|
||||
} catch (error) {
|
||||
logger.warn(`[ConnectionPool] Error closing connection for ${dbName}:`, error);
|
||||
logger.warn(
|
||||
`[ConnectionPool] Error closing connection for ${dbName}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
this.connections.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,31 +6,17 @@ import {
|
||||
} from "../PlatformService";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
||||
import { ElectronAPI } from "../../utils/debug-electron";
|
||||
|
||||
import { verifyElectronAPI, testSQLiteOperations } from "../../utils/debug-electron";
|
||||
import {
|
||||
verifyElectronAPI,
|
||||
testSQLiteOperations,
|
||||
} from "../../utils/debug-electron";
|
||||
|
||||
// Type for the electron window object
|
||||
// Extend the global Window interface
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: {
|
||||
ipcRenderer?: {
|
||||
on: (channel: string, func: (...args: unknown[]) => void) => void;
|
||||
once: (channel: string, func: (...args: unknown[]) => void) => void;
|
||||
send: (channel: string, data: unknown) => void;
|
||||
invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
sqlite: {
|
||||
isAvailable: () => Promise<boolean>;
|
||||
execute: (method: string, ...args: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
getPath: (pathType: string) => Promise<string>;
|
||||
send: (channel: string, data: unknown) => void;
|
||||
receive: (channel: string, func: (...args: unknown[]) => void) => void;
|
||||
env: {
|
||||
platform: string;
|
||||
};
|
||||
getBasePath: () => Promise<string>;
|
||||
};
|
||||
electron: ElectronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +33,7 @@ export interface SQLiteQueryResult {
|
||||
/**
|
||||
* Shared SQLite initialization state
|
||||
* Used to coordinate initialization between main and service
|
||||
*
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
*/
|
||||
export interface SQLiteInitState {
|
||||
@@ -61,9 +47,32 @@ export interface SQLiteInitState {
|
||||
const sqliteInitState: SQLiteInitState = {
|
||||
isReady: false,
|
||||
isInitializing: false,
|
||||
lastReadyCheck: 0
|
||||
lastReadyCheck: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining SQLite database operations
|
||||
* @author Matthew Raymer
|
||||
*/
|
||||
interface SQLiteOperations {
|
||||
createConnection: (options: {
|
||||
database: string;
|
||||
encrypted: boolean;
|
||||
mode: string;
|
||||
}) => Promise<void>;
|
||||
query: (options: {
|
||||
database: string;
|
||||
statement: string;
|
||||
values?: unknown[];
|
||||
}) => Promise<{ values?: unknown[] }>;
|
||||
execute: (options: { database: string; statements: string }) => Promise<void>;
|
||||
run: (options: {
|
||||
database: string;
|
||||
statement: string;
|
||||
values?: unknown[];
|
||||
}) => Promise<{ changes?: { changes: number; lastId?: number } }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform service implementation for Electron (desktop) platform.
|
||||
* Provides native desktop functionality through Electron and Capacitor plugins for:
|
||||
@@ -71,11 +80,11 @@ const sqliteInitState: SQLiteInitState = {
|
||||
* - Camera integration (TODO)
|
||||
* - SQLite database operations
|
||||
* - System-level features (TODO)
|
||||
*
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
*/
|
||||
export class ElectronPlatformService implements PlatformService {
|
||||
private sqlite: any;
|
||||
private sqlite: SQLiteOperations | null = null;
|
||||
private dbName = "timesafari";
|
||||
private isInitialized = false;
|
||||
private dbFatalError = false;
|
||||
@@ -85,17 +94,17 @@ export class ElectronPlatformService implements PlatformService {
|
||||
// SQLite initialization configuration
|
||||
private static readonly SQLITE_CONFIG = {
|
||||
INITIALIZATION: {
|
||||
TIMEOUT_MS: 10000, // 10 seconds for initial setup
|
||||
TIMEOUT_MS: 10000, // 10 seconds for initial setup
|
||||
RETRY_ATTEMPTS: 3,
|
||||
RETRY_DELAY_MS: 1000,
|
||||
READY_CHECK_INTERVAL_MS: 100 // How often to check if SQLite is already ready
|
||||
}
|
||||
READY_CHECK_INTERVAL_MS: 100, // How often to check if SQLite is already ready
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.sqliteReadyPromise = new Promise<void>((resolve, reject) => {
|
||||
let retryCount = 0;
|
||||
|
||||
|
||||
const cleanup = () => {
|
||||
if (this.initializationTimeout) {
|
||||
clearTimeout(this.initializationTimeout);
|
||||
@@ -110,18 +119,25 @@ export class ElectronPlatformService implements PlatformService {
|
||||
}
|
||||
|
||||
// Check if SQLite is already available
|
||||
const isAvailable = await window.electron.ipcRenderer.invoke('sqlite-is-available');
|
||||
const isAvailable = await window.electron.ipcRenderer.invoke(
|
||||
"sqlite-is-available",
|
||||
);
|
||||
if (!isAvailable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if database is already open
|
||||
const isOpen = await window.electron.ipcRenderer.invoke('sqlite-is-db-open', {
|
||||
database: this.dbName
|
||||
});
|
||||
const isOpen = await window.electron.ipcRenderer.invoke(
|
||||
"sqlite-is-db-open",
|
||||
{
|
||||
database: this.dbName,
|
||||
},
|
||||
);
|
||||
|
||||
if (isOpen) {
|
||||
logger.info('[ElectronPlatformService] SQLite is already ready and database is open');
|
||||
logger.info(
|
||||
"[ElectronPlatformService] SQLite is already ready and database is open",
|
||||
);
|
||||
sqliteInitState.isReady = true;
|
||||
sqliteInitState.isInitializing = false;
|
||||
sqliteInitState.lastReadyCheck = Date.now();
|
||||
@@ -130,7 +146,10 @@ export class ElectronPlatformService implements PlatformService {
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.warn('[ElectronPlatformService] Error checking existing readiness:', error);
|
||||
logger.warn(
|
||||
"[ElectronPlatformService] Error checking existing readiness:",
|
||||
error,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -147,49 +166,75 @@ export class ElectronPlatformService implements PlatformService {
|
||||
|
||||
// If someone else is initializing, wait for them
|
||||
if (sqliteInitState.isInitializing) {
|
||||
logger.info('[ElectronPlatformService] Another initialization in progress, waiting...');
|
||||
setTimeout(attemptInitialization, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.READY_CHECK_INTERVAL_MS);
|
||||
logger.info(
|
||||
"[ElectronPlatformService] Another initialization in progress, waiting...",
|
||||
);
|
||||
setTimeout(
|
||||
attemptInitialization,
|
||||
ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION
|
||||
.READY_CHECK_INTERVAL_MS,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
sqliteInitState.isInitializing = true;
|
||||
|
||||
|
||||
// Verify Electron API exposure first
|
||||
await verifyElectronAPI();
|
||||
logger.info('[ElectronPlatformService] Electron API verification successful');
|
||||
logger.info(
|
||||
"[ElectronPlatformService] Electron API verification successful",
|
||||
);
|
||||
|
||||
if (!window.electron?.ipcRenderer) {
|
||||
logger.warn('[ElectronPlatformService] IPC renderer not available');
|
||||
reject(new Error('IPC renderer not available'));
|
||||
logger.warn("[ElectronPlatformService] IPC renderer not available");
|
||||
reject(new Error("IPC renderer not available"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set timeout for this attempt
|
||||
this.initializationTimeout = setTimeout(() => {
|
||||
if (retryCount < ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.RETRY_ATTEMPTS) {
|
||||
if (
|
||||
retryCount <
|
||||
ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION
|
||||
.RETRY_ATTEMPTS
|
||||
) {
|
||||
retryCount++;
|
||||
logger.warn(`[ElectronPlatformService] SQLite initialization attempt ${retryCount} timed out, retrying...`);
|
||||
setTimeout(attemptInitialization, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.RETRY_DELAY_MS);
|
||||
logger.warn(
|
||||
`[ElectronPlatformService] SQLite initialization attempt ${retryCount} timed out, retrying...`,
|
||||
);
|
||||
setTimeout(
|
||||
attemptInitialization,
|
||||
ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION
|
||||
.RETRY_DELAY_MS,
|
||||
);
|
||||
} else {
|
||||
cleanup();
|
||||
sqliteInitState.isInitializing = false;
|
||||
sqliteInitState.error = new Error('SQLite initialization timeout after all retries');
|
||||
logger.error('[ElectronPlatformService] SQLite initialization failed after all retries');
|
||||
sqliteInitState.error = new Error(
|
||||
"SQLite initialization timeout after all retries",
|
||||
);
|
||||
logger.error(
|
||||
"[ElectronPlatformService] SQLite initialization failed after all retries",
|
||||
);
|
||||
reject(sqliteInitState.error);
|
||||
}
|
||||
}, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.TIMEOUT_MS);
|
||||
|
||||
// Set up ready signal handler
|
||||
window.electron.ipcRenderer.once('sqlite-ready', async () => {
|
||||
window.electron.ipcRenderer.once("sqlite-ready", async () => {
|
||||
cleanup();
|
||||
logger.info('[ElectronPlatformService] Received SQLite ready signal');
|
||||
|
||||
logger.info(
|
||||
"[ElectronPlatformService] Received SQLite ready signal",
|
||||
);
|
||||
|
||||
try {
|
||||
// Test SQLite operations after receiving ready signal
|
||||
await testSQLiteOperations();
|
||||
logger.info('[ElectronPlatformService] SQLite operations test successful');
|
||||
|
||||
logger.info(
|
||||
"[ElectronPlatformService] SQLite operations test successful",
|
||||
);
|
||||
|
||||
this.isInitialized = true;
|
||||
sqliteInitState.isReady = true;
|
||||
sqliteInitState.isInitializing = false;
|
||||
@@ -198,28 +243,38 @@ export class ElectronPlatformService implements PlatformService {
|
||||
} catch (error) {
|
||||
sqliteInitState.error = error as Error;
|
||||
sqliteInitState.isInitializing = false;
|
||||
logger.error('[ElectronPlatformService] SQLite operations test failed:', error);
|
||||
logger.error(
|
||||
"[ElectronPlatformService] SQLite operations test failed:",
|
||||
error,
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Set up error handler
|
||||
window.electron.ipcRenderer.once('database-status', (...args: unknown[]) => {
|
||||
cleanup();
|
||||
const status = args[0] as { status: string; error?: string };
|
||||
if (status.status === 'error') {
|
||||
this.dbFatalError = true;
|
||||
sqliteInitState.error = new Error(status.error || 'Database initialization failed');
|
||||
sqliteInitState.isInitializing = false;
|
||||
reject(sqliteInitState.error);
|
||||
}
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.once(
|
||||
"database-status",
|
||||
(...args: unknown[]) => {
|
||||
cleanup();
|
||||
const status = args[0] as { status: string; error?: string };
|
||||
if (status.status === "error") {
|
||||
this.dbFatalError = true;
|
||||
sqliteInitState.error = new Error(
|
||||
status.error || "Database initialization failed",
|
||||
);
|
||||
sqliteInitState.isInitializing = false;
|
||||
reject(sqliteInitState.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
cleanup();
|
||||
sqliteInitState.error = error as Error;
|
||||
sqliteInitState.isInitializing = false;
|
||||
logger.error('[ElectronPlatformService] Initialization failed:', error);
|
||||
logger.error(
|
||||
"[ElectronPlatformService] Initialization failed:",
|
||||
error,
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
@@ -232,26 +287,36 @@ export class ElectronPlatformService implements PlatformService {
|
||||
private async initializeDatabase(): Promise<void> {
|
||||
if (this.isInitialized) return;
|
||||
if (this.sqliteReadyPromise) await this.sqliteReadyPromise;
|
||||
this.sqlite = window.CapacitorSQLite;
|
||||
if (!this.sqlite) throw new Error("CapacitorSQLite not available");
|
||||
|
||||
if (!window.CapacitorSQLite) {
|
||||
throw new Error("CapacitorSQLite not available");
|
||||
}
|
||||
|
||||
this.sqlite = window.CapacitorSQLite as unknown as SQLiteOperations;
|
||||
|
||||
// Create the connection (idempotent)
|
||||
await this.sqlite.createConnection({
|
||||
database: this.dbName,
|
||||
encrypted: false,
|
||||
mode: "no-encryption",
|
||||
readOnly: false,
|
||||
});
|
||||
|
||||
// Optionally, test the connection
|
||||
await this.sqlite.query({
|
||||
database: this.dbName,
|
||||
statement: "SELECT 1"
|
||||
statement: "SELECT 1",
|
||||
});
|
||||
|
||||
// Run migrations if needed
|
||||
await this.runMigrations();
|
||||
logger.info("[ElectronPlatformService] Database initialized successfully");
|
||||
}
|
||||
|
||||
private async runMigrations(): Promise<void> {
|
||||
if (!this.sqlite) {
|
||||
throw new Error("SQLite not initialized");
|
||||
}
|
||||
|
||||
// Create migrations table if it doesn't exist
|
||||
await this.sqlite.execute({
|
||||
database: this.dbName,
|
||||
@@ -259,15 +324,18 @@ export class ElectronPlatformService implements PlatformService {
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);`
|
||||
);`,
|
||||
});
|
||||
|
||||
// Get list of executed migrations
|
||||
const result = await this.sqlite.query({
|
||||
database: this.dbName,
|
||||
statement: "SELECT name FROM migrations;"
|
||||
statement: "SELECT name FROM migrations;",
|
||||
});
|
||||
const executedMigrations = new Set(
|
||||
(result.values as unknown[][])?.map((row: unknown[]) => row[0] as string) || []
|
||||
(result.values as unknown[][])?.map(
|
||||
(row: unknown[]) => row[0] as string,
|
||||
) || [],
|
||||
);
|
||||
// Run pending migrations in order
|
||||
const migrations: Migration[] = [
|
||||
@@ -360,12 +428,12 @@ export class ElectronPlatformService implements PlatformService {
|
||||
if (!executedMigrations.has(migration.name)) {
|
||||
await this.sqlite.execute({
|
||||
database: this.dbName,
|
||||
statements: migration.sql
|
||||
statements: migration.sql,
|
||||
});
|
||||
await this.sqlite.run({
|
||||
database: this.dbName,
|
||||
statement: "INSERT INTO migrations (name) VALUES (?)",
|
||||
values: [migration.name]
|
||||
values: [migration.name],
|
||||
});
|
||||
logger.log(`Migration ${migration.name} executed successfully`);
|
||||
}
|
||||
@@ -477,23 +545,29 @@ export class ElectronPlatformService implements PlatformService {
|
||||
/**
|
||||
* Executes a database query with proper connection lifecycle management.
|
||||
* Opens connection, executes query, and ensures proper cleanup.
|
||||
*
|
||||
*
|
||||
* @param sql - SQL query to execute
|
||||
* @param params - Optional parameters for the query
|
||||
* @returns Promise resolving to query results
|
||||
* @throws Error if database operations fail
|
||||
*/
|
||||
async dbQuery<T = unknown>(sql: string, params: unknown[] = []): Promise<QueryExecResult<T>> {
|
||||
logger.debug("[ElectronPlatformService] [dbQuery] TEMPORARY TEST: Returning empty result for query:", {
|
||||
sql,
|
||||
params,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
async dbQuery<T = unknown>(
|
||||
sql: string,
|
||||
params: unknown[] = [],
|
||||
): Promise<QueryExecResult<T>> {
|
||||
logger.debug(
|
||||
"[ElectronPlatformService] [dbQuery] TEMPORARY TEST: Returning empty result for query:",
|
||||
{
|
||||
sql,
|
||||
params,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
);
|
||||
|
||||
// TEMPORARY TEST: Return empty result
|
||||
return {
|
||||
columns: [],
|
||||
values: []
|
||||
values: [],
|
||||
};
|
||||
|
||||
// Original implementation commented out for testing
|
||||
@@ -579,13 +653,23 @@ export class ElectronPlatformService implements PlatformService {
|
||||
/**
|
||||
* @see PlatformService.dbExec
|
||||
*/
|
||||
async dbExec(sql: string, params?: unknown[]): Promise<{ changes: number; lastId?: number }> {
|
||||
async dbExec(
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
): Promise<{ changes: number; lastId?: number }> {
|
||||
await this.initializeDatabase();
|
||||
if (this.dbFatalError) throw new Error("Database is in a fatal error state. Please restart the app.");
|
||||
if (this.dbFatalError) {
|
||||
throw new Error(
|
||||
"Database is in a fatal error state. Please restart the app.",
|
||||
);
|
||||
}
|
||||
if (!this.sqlite) {
|
||||
throw new Error("SQLite not initialized");
|
||||
}
|
||||
const result = await this.sqlite.run({
|
||||
database: this.dbName,
|
||||
statement: sql,
|
||||
values: params
|
||||
values: params,
|
||||
});
|
||||
return {
|
||||
changes: result.changes?.changes || 0,
|
||||
@@ -597,13 +681,20 @@ export class ElectronPlatformService implements PlatformService {
|
||||
await this.initializeDatabase();
|
||||
}
|
||||
|
||||
async execute(sql: string, params: any[] = []): Promise<void> {
|
||||
async execute(sql: string, params: unknown[] = []): Promise<void> {
|
||||
await this.initializeDatabase();
|
||||
if (this.dbFatalError) throw new Error("Database is in a fatal error state. Please restart the app.");
|
||||
if (this.dbFatalError) {
|
||||
throw new Error(
|
||||
"Database is in a fatal error state. Please restart the app.",
|
||||
);
|
||||
}
|
||||
if (!this.sqlite) {
|
||||
throw new Error("SQLite not initialized");
|
||||
}
|
||||
await this.sqlite.run({
|
||||
database: this.dbName,
|
||||
statement: sql,
|
||||
values: params
|
||||
values: params,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,123 +1,169 @@
|
||||
/**
|
||||
* Debug utilities for Electron integration
|
||||
* Helps verify the context bridge and SQLite functionality
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
*/
|
||||
|
||||
const debugLogger = {
|
||||
log: (...args: unknown[]) => console.log('[Debug]', ...args),
|
||||
error: (...args: unknown[]) => console.error('[Debug]', ...args),
|
||||
info: (...args: unknown[]) => console.info('[Debug]', ...args),
|
||||
warn: (...args: unknown[]) => console.warn('[Debug]', ...args),
|
||||
debug: (...args: unknown[]) => console.debug('[Debug]', ...args)
|
||||
};
|
||||
import { logger } from "./logger";
|
||||
|
||||
// Define the SQLite interface
|
||||
export interface SQLiteAPI {
|
||||
isAvailable: () => Promise<boolean>;
|
||||
echo: (value: string) => Promise<string>;
|
||||
createConnection: (options: {
|
||||
database: string;
|
||||
version: number;
|
||||
readOnly: boolean;
|
||||
}) => Promise<void>;
|
||||
closeConnection: (options: { database: string }) => Promise<void>;
|
||||
query: (options: { statement: string }) => Promise<unknown>;
|
||||
run: (options: { statement: string }) => Promise<unknown>;
|
||||
execute: (options: {
|
||||
statements: Array<{ statement: string }>;
|
||||
}) => Promise<unknown>;
|
||||
getPlatform: () => Promise<string>;
|
||||
}
|
||||
|
||||
// Define the IPC renderer interface
|
||||
export interface IPCRenderer {
|
||||
on: (channel: string, func: (...args: unknown[]) => void) => void;
|
||||
once: (channel: string, func: (...args: unknown[]) => void) => void;
|
||||
send: (channel: string, ...args: unknown[]) => void;
|
||||
invoke: (channel: string, ...args: unknown[]) => Promise<unknown>;
|
||||
}
|
||||
|
||||
// Define the environment interface
|
||||
export interface ElectronEnv {
|
||||
platform: string;
|
||||
isDev: boolean;
|
||||
}
|
||||
|
||||
// Define the complete electron interface
|
||||
export interface ElectronAPI {
|
||||
sqlite: SQLiteAPI;
|
||||
ipcRenderer: IPCRenderer;
|
||||
env: ElectronEnv;
|
||||
getPath: (pathType: string) => Promise<string>;
|
||||
getBasePath: () => Promise<string>;
|
||||
}
|
||||
|
||||
// Define the window.electron interface
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
export async function verifyElectronAPI(): Promise<void> {
|
||||
debugLogger.info('Verifying Electron API exposure...');
|
||||
logger.info("[Debug] Verifying Electron API exposure...");
|
||||
|
||||
// Check if window.electron exists
|
||||
if (!window.electron) {
|
||||
throw new Error('window.electron is not defined');
|
||||
throw new Error("window.electron is not defined");
|
||||
}
|
||||
debugLogger.info('window.electron is available');
|
||||
logger.info("[Debug] window.electron is available");
|
||||
|
||||
// Verify IPC renderer
|
||||
if (!window.electron.ipcRenderer) {
|
||||
throw new Error('IPC renderer is not available');
|
||||
throw new Error("IPC renderer is not available");
|
||||
}
|
||||
debugLogger.info('IPC renderer is available with methods:', {
|
||||
hasOn: typeof window.electron.ipcRenderer.on === 'function',
|
||||
hasOnce: typeof window.electron.ipcRenderer.once === 'function',
|
||||
hasSend: typeof window.electron.ipcRenderer.send === 'function',
|
||||
hasInvoke: typeof window.electron.ipcRenderer.invoke === 'function'
|
||||
logger.info("[Debug] IPC renderer is available with methods:", {
|
||||
hasOn: typeof window.electron.ipcRenderer.on === "function",
|
||||
hasOnce: typeof window.electron.ipcRenderer.once === "function",
|
||||
hasSend: typeof window.electron.ipcRenderer.send === "function",
|
||||
hasInvoke: typeof window.electron.ipcRenderer.invoke === "function",
|
||||
});
|
||||
|
||||
// Verify SQLite API
|
||||
if (!window.electron.sqlite) {
|
||||
throw new Error('SQLite API is not available');
|
||||
throw new Error("SQLite API is not available");
|
||||
}
|
||||
debugLogger.info('SQLite API is available with methods:', {
|
||||
hasIsAvailable: typeof window.electron.sqlite.isAvailable === 'function',
|
||||
hasEcho: typeof window.electron.sqlite.echo === 'function',
|
||||
hasCreateConnection: typeof window.electron.sqlite.createConnection === 'function',
|
||||
hasCloseConnection: typeof window.electron.sqlite.closeConnection === 'function',
|
||||
hasQuery: typeof window.electron.sqlite.query === 'function',
|
||||
hasRun: typeof window.electron.sqlite.run === 'function',
|
||||
hasExecute: typeof window.electron.sqlite.execute === 'function',
|
||||
hasGetPlatform: typeof window.electron.sqlite.getPlatform === 'function'
|
||||
logger.info("[Debug] SQLite API is available with methods:", {
|
||||
hasIsAvailable: typeof window.electron.sqlite.isAvailable === "function",
|
||||
hasEcho: typeof window.electron.sqlite.echo === "function",
|
||||
hasCreateConnection:
|
||||
typeof window.electron.sqlite.createConnection === "function",
|
||||
hasCloseConnection:
|
||||
typeof window.electron.sqlite.closeConnection === "function",
|
||||
hasQuery: typeof window.electron.sqlite.query === "function",
|
||||
hasRun: typeof window.electron.sqlite.run === "function",
|
||||
hasExecute: typeof window.electron.sqlite.execute === "function",
|
||||
hasGetPlatform: typeof window.electron.sqlite.getPlatform === "function",
|
||||
});
|
||||
|
||||
// Test SQLite availability
|
||||
try {
|
||||
const isAvailable = await window.electron.sqlite.isAvailable();
|
||||
debugLogger.info('SQLite availability check:', { isAvailable });
|
||||
logger.info("[Debug] SQLite availability check:", { isAvailable });
|
||||
} catch (error) {
|
||||
debugLogger.error('SQLite availability check failed:', error);
|
||||
logger.error("[Debug] SQLite availability check failed:", error);
|
||||
}
|
||||
|
||||
// Test echo functionality
|
||||
try {
|
||||
const echoResult = await window.electron.sqlite.echo('test');
|
||||
debugLogger.info('SQLite echo test:', echoResult);
|
||||
const echoResult = await window.electron.sqlite.echo("test");
|
||||
logger.info("[Debug] SQLite echo test:", echoResult);
|
||||
} catch (error) {
|
||||
debugLogger.error('SQLite echo test failed:', error);
|
||||
logger.error("[Debug] SQLite echo test failed:", error);
|
||||
}
|
||||
|
||||
// Verify environment
|
||||
debugLogger.info('Environment:', {
|
||||
logger.info("[Debug] Environment:", {
|
||||
platform: window.electron.env.platform,
|
||||
isDev: window.electron.env.isDev
|
||||
isDev: window.electron.env.isDev,
|
||||
});
|
||||
|
||||
debugLogger.info('Electron API verification complete');
|
||||
logger.info("[Debug] Electron API verification complete");
|
||||
}
|
||||
|
||||
// Export a function to test SQLite operations
|
||||
export async function testSQLiteOperations(): Promise<void> {
|
||||
debugLogger.info('Testing SQLite operations...');
|
||||
logger.info("[Debug] Testing SQLite operations...");
|
||||
|
||||
try {
|
||||
// Test connection creation
|
||||
debugLogger.info('Creating test connection...');
|
||||
logger.info("[Debug] Creating test connection...");
|
||||
await window.electron.sqlite.createConnection({
|
||||
database: 'test',
|
||||
database: "test",
|
||||
version: 1,
|
||||
readOnly: false
|
||||
readOnly: false,
|
||||
});
|
||||
debugLogger.info('Test connection created successfully');
|
||||
logger.info("[Debug] Test connection created successfully");
|
||||
|
||||
// Test query
|
||||
debugLogger.info('Testing query operation...');
|
||||
logger.info("[Debug] Testing query operation...");
|
||||
const queryResult = await window.electron.sqlite.query({
|
||||
statement: 'SELECT 1 as test'
|
||||
statement: "SELECT 1 as test",
|
||||
});
|
||||
debugLogger.info('Query test result:', queryResult);
|
||||
logger.info("[Debug] Query test result:", queryResult);
|
||||
|
||||
// Test run
|
||||
debugLogger.info('Testing run operation...');
|
||||
logger.info("[Debug] Testing run operation...");
|
||||
const runResult = await window.electron.sqlite.run({
|
||||
statement: 'CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY)'
|
||||
statement:
|
||||
"CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY)",
|
||||
});
|
||||
debugLogger.info('Run test result:', runResult);
|
||||
logger.info("[Debug] Run test result:", runResult);
|
||||
|
||||
// Test execute
|
||||
debugLogger.info('Testing execute operation...');
|
||||
logger.info("[Debug] Testing execute operation...");
|
||||
const executeResult = await window.electron.sqlite.execute({
|
||||
statements: [
|
||||
{ statement: 'INSERT INTO test_table (id) VALUES (1)' },
|
||||
{ statement: 'SELECT * FROM test_table' }
|
||||
]
|
||||
{ statement: "INSERT INTO test_table (id) VALUES (1)" },
|
||||
{ statement: "SELECT * FROM test_table" },
|
||||
],
|
||||
});
|
||||
debugLogger.info('Execute test result:', executeResult);
|
||||
logger.info("[Debug] Execute test result:", executeResult);
|
||||
|
||||
// Clean up
|
||||
debugLogger.info('Closing test connection...');
|
||||
await window.electron.sqlite.closeConnection({ database: 'test' });
|
||||
debugLogger.info('Test connection closed');
|
||||
|
||||
logger.info("[Debug] Closing test connection...");
|
||||
await window.electron.sqlite.closeConnection({ database: "test" });
|
||||
logger.info("[Debug] Test connection closed");
|
||||
} catch (error) {
|
||||
debugLogger.error('SQLite operation test failed:', error);
|
||||
logger.error("[Debug] SQLite operation test failed:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
debugLogger.info('SQLite operations test complete');
|
||||
}
|
||||
logger.info("[Debug] SQLite operations test complete");
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ import {
|
||||
import { GiveSummaryRecord } from "../interfaces";
|
||||
import * as serverUtil from "../libs/endorserServer";
|
||||
import { logger } from "../utils/logger";
|
||||
import { GiveRecordWithContactInfo } from "../types";
|
||||
import { GiveRecordWithContactInfo } from "../interfaces/give";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
|
||||
interface Claim {
|
||||
@@ -610,7 +610,10 @@ export default class HomeView extends Vue {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
logConsoleAndDb("[initializeIdentity] Error retrieving settings or feed: " + err, true);
|
||||
logConsoleAndDb(
|
||||
"[initializeIdentity] Error retrieving settings or feed: " + err,
|
||||
true,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -1687,7 +1690,7 @@ export default class HomeView extends Vue {
|
||||
* @param event Event object
|
||||
* @param imageUrl URL of image to cache
|
||||
*/
|
||||
async cacheImageData(event: Event, imageUrl: string) {
|
||||
async cacheImageData(_event: Event, imageUrl: string) {
|
||||
try {
|
||||
// For images that might fail CORS, just store the URL
|
||||
// The Web Share API will handle sharing the URL appropriately
|
||||
@@ -1748,7 +1751,7 @@ export default class HomeView extends Vue {
|
||||
this.axios,
|
||||
);
|
||||
|
||||
if (result.type === "success") {
|
||||
if (result.success) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
|
||||
@@ -187,10 +187,9 @@ export default class IdentitySwitcherView extends Vue {
|
||||
text: "Are you sure you want to erase this identity? (There is no undo. You may want to select it and back it up just in case.)",
|
||||
onYes: async () => {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
await platformService.dbExec(
|
||||
`DELETE FROM accounts WHERE id = ?`,
|
||||
[id],
|
||||
);
|
||||
await platformService.dbExec(`DELETE FROM accounts WHERE id = ?`, [
|
||||
id,
|
||||
]);
|
||||
if (USE_DEXIE_DB) {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
|
||||
Reference in New Issue
Block a user