forked from trent_larson/crowd-funder-for-time-pwa
fix: Resolve TypeScript linting warnings in CapacitorPlatformService
- Replace 'any' type assertions with specific types in migration name extraction
* Change '(row as any).name' to '(row as { name: string }).name'
* Add proper null checks and 'in' operator for property access
- Fix database integrity check type safety
* Change '(col: any)' to '(col: unknown)' with type guards
* Use specific type assertion for column name checking
Resolves: @typescript-eslint/no-explicit-any warnings (2 instances)
Impact: Improves type safety without changing functionality
This commit is contained in:
@@ -239,42 +239,42 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
|
||||
/**
|
||||
* Execute database migrations for the Capacitor platform
|
||||
*
|
||||
*
|
||||
* This method orchestrates the database migration process specifically for
|
||||
* Capacitor-based platforms (mobile and Electron). It provides the platform-specific
|
||||
* SQL execution functions to the migration service and handles Capacitor SQLite
|
||||
* plugin integration.
|
||||
*
|
||||
*
|
||||
* ## Migration Process:
|
||||
*
|
||||
*
|
||||
* 1. **SQL Execution Setup**: Creates platform-specific SQL execution functions
|
||||
* that properly handle the Capacitor SQLite plugin's API
|
||||
*
|
||||
*
|
||||
* 2. **Parameter Handling**: Ensures proper parameter binding for prepared statements
|
||||
* using the correct Capacitor SQLite methods (run vs execute)
|
||||
*
|
||||
*
|
||||
* 3. **Result Parsing**: Provides extraction functions that understand the
|
||||
* Capacitor SQLite result format
|
||||
*
|
||||
*
|
||||
* 4. **Migration Execution**: Delegates to the migration service for the actual
|
||||
* migration logic and tracking
|
||||
*
|
||||
*
|
||||
* 5. **Integrity Verification**: Runs post-migration integrity checks to ensure
|
||||
* the database is in the expected state
|
||||
*
|
||||
*
|
||||
* ## Error Handling:
|
||||
*
|
||||
*
|
||||
* The method includes comprehensive error handling for:
|
||||
* - Database connection issues
|
||||
* - SQL execution failures
|
||||
* - Migration tracking problems
|
||||
* - Schema validation errors
|
||||
*
|
||||
*
|
||||
* Even if migrations fail, the integrity check still runs to assess the
|
||||
* current database state and provide debugging information.
|
||||
*
|
||||
*
|
||||
* ## Logging:
|
||||
*
|
||||
*
|
||||
* Detailed logging is provided throughout the process using emoji-tagged
|
||||
* console messages that appear in the Electron DevTools console. This
|
||||
* includes:
|
||||
@@ -282,10 +282,10 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
* - Parameter values for debugging
|
||||
* - Migration success/failure status
|
||||
* - Database integrity check results
|
||||
*
|
||||
*
|
||||
* @throws {Error} If database is not initialized or migrations fail critically
|
||||
* @private Internal method called during database initialization
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Called automatically during platform service initialization
|
||||
@@ -299,98 +299,110 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
|
||||
/**
|
||||
* SQL execution function for Capacitor SQLite plugin
|
||||
*
|
||||
*
|
||||
* This function handles the execution of SQL statements (INSERT, UPDATE, CREATE, etc.)
|
||||
* through the Capacitor SQLite plugin. It automatically chooses the appropriate
|
||||
* method based on whether parameters are provided.
|
||||
*
|
||||
*
|
||||
* @param sql - SQL statement to execute
|
||||
* @param params - Optional parameters for prepared statements
|
||||
* @returns Promise resolving to execution results
|
||||
*/
|
||||
const sqlExec = async (sql: string, params?: unknown[]): Promise<capSQLiteChanges> => {
|
||||
console.log(`🔧 [CapacitorMigration] Executing SQL:`, sql);
|
||||
console.log(`📋 [CapacitorMigration] With params:`, params);
|
||||
|
||||
const sqlExec = async (
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
): Promise<capSQLiteChanges> => {
|
||||
logger.log(`🔧 [CapacitorMigration] Executing SQL:`, sql);
|
||||
logger.log(`📋 [CapacitorMigration] With params:`, params);
|
||||
|
||||
if (params && params.length > 0) {
|
||||
// Use run method for parameterized queries (prepared statements)
|
||||
// This is essential for proper parameter binding and SQL injection prevention
|
||||
const result = await this.db!.run(sql, params);
|
||||
console.log(`✅ [CapacitorMigration] Run result:`, result);
|
||||
logger.log(`✅ [CapacitorMigration] Run result:`, result);
|
||||
return result;
|
||||
} else {
|
||||
// Use execute method for non-parameterized queries
|
||||
// This is more efficient for simple DDL statements
|
||||
const result = await this.db!.execute(sql);
|
||||
console.log(`✅ [CapacitorMigration] Execute result:`, result);
|
||||
logger.log(`✅ [CapacitorMigration] Execute result:`, result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL query function for Capacitor SQLite plugin
|
||||
*
|
||||
*
|
||||
* This function handles the execution of SQL queries (SELECT statements)
|
||||
* through the Capacitor SQLite plugin. It returns the raw result data
|
||||
* that can be processed by the migration service.
|
||||
*
|
||||
*
|
||||
* @param sql - SQL query to execute
|
||||
* @param params - Optional parameters for prepared statements
|
||||
* @returns Promise resolving to query results
|
||||
*/
|
||||
const sqlQuery = async (sql: string, params?: unknown[]): Promise<DBSQLiteValues> => {
|
||||
console.log(`🔍 [CapacitorMigration] Querying SQL:`, sql);
|
||||
console.log(`📋 [CapacitorMigration] With params:`, params);
|
||||
|
||||
const sqlQuery = async (
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
): Promise<DBSQLiteValues> => {
|
||||
logger.log(`🔍 [CapacitorMigration] Querying SQL:`, sql);
|
||||
logger.log(`📋 [CapacitorMigration] With params:`, params);
|
||||
|
||||
const result = await this.db!.query(sql, params);
|
||||
console.log(`📊 [CapacitorMigration] Query result:`, result);
|
||||
logger.log(`📊 [CapacitorMigration] Query result:`, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract migration names from Capacitor SQLite query results
|
||||
*
|
||||
*
|
||||
* This function parses the result format returned by the Capacitor SQLite
|
||||
* plugin and extracts migration names. It handles the specific data structure
|
||||
* used by the plugin, which can vary between different result formats.
|
||||
*
|
||||
*
|
||||
* ## Result Format Handling:
|
||||
*
|
||||
*
|
||||
* The Capacitor SQLite plugin can return results in different formats:
|
||||
* - Object format: `{ name: "migration_name" }`
|
||||
* - Array format: `["migration_name", "timestamp"]`
|
||||
*
|
||||
*
|
||||
* This function handles both formats to ensure robust migration name extraction.
|
||||
*
|
||||
*
|
||||
* @param result - Query result from Capacitor SQLite plugin
|
||||
* @returns Set of migration names found in the result
|
||||
*/
|
||||
const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
|
||||
console.log(`🔍 [CapacitorMigration] Extracting migration names from:`, result);
|
||||
|
||||
logger.log(
|
||||
`🔍 [CapacitorMigration] Extracting migration names from:`,
|
||||
result,
|
||||
);
|
||||
|
||||
// Handle the Capacitor SQLite result format
|
||||
const names = result.values?.map((row: any) => {
|
||||
// The row could be an object with 'name' property or an array where name is first element
|
||||
if (typeof row === 'object' && row.name !== undefined) {
|
||||
return row.name;
|
||||
} else if (Array.isArray(row) && row.length > 0) {
|
||||
return row[0];
|
||||
}
|
||||
return null;
|
||||
}).filter(name => name !== null) || [];
|
||||
|
||||
console.log(`📋 [CapacitorMigration] Extracted names:`, names);
|
||||
const names =
|
||||
result.values
|
||||
?.map((row: unknown) => {
|
||||
// The row could be an object with 'name' property or an array where name is first element
|
||||
if (typeof row === "object" && row !== null && "name" in row) {
|
||||
return (row as { name: string }).name;
|
||||
} else if (Array.isArray(row) && row.length > 0) {
|
||||
return row[0];
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((name) => name !== null) || [];
|
||||
|
||||
logger.log(`📋 [CapacitorMigration] Extracted names:`, names);
|
||||
return new Set(names);
|
||||
};
|
||||
|
||||
try {
|
||||
// Execute the migration process
|
||||
await runMigrations(sqlExec, sqlQuery, extractMigrationNames);
|
||||
|
||||
await runMigrations(sqlExec, sqlQuery, extractMigrationNames);
|
||||
|
||||
// After migrations, run integrity check to verify database state
|
||||
await this.verifyDatabaseIntegrity();
|
||||
} catch (error) {
|
||||
console.error(`❌ [CapacitorMigration] Migration failed:`, error);
|
||||
logger.error(`❌ [CapacitorMigration] Migration failed:`, error);
|
||||
// Still try to verify what we have for debugging purposes
|
||||
await this.verifyDatabaseIntegrity();
|
||||
throw error;
|
||||
@@ -399,50 +411,50 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
|
||||
/**
|
||||
* Verify database integrity and migration status
|
||||
*
|
||||
*
|
||||
* This method performs comprehensive validation of the database structure
|
||||
* and migration state. It's designed to help identify issues with the
|
||||
* migration process and provide detailed debugging information.
|
||||
*
|
||||
*
|
||||
* ## Validation Steps:
|
||||
*
|
||||
*
|
||||
* 1. **Migration Records**: Checks which migrations are recorded as applied
|
||||
* 2. **Table Existence**: Verifies all expected core tables exist
|
||||
* 3. **Schema Validation**: Checks table schemas including column presence
|
||||
* 4. **Data Integrity**: Validates basic data counts and structure
|
||||
*
|
||||
*
|
||||
* ## Core Tables Validated:
|
||||
*
|
||||
*
|
||||
* - `accounts`: User identity and cryptographic keys
|
||||
* - `secret`: Application secrets and encryption keys
|
||||
* - `settings`: Configuration and user preferences
|
||||
* - `contacts`: Contact network and trust relationships
|
||||
* - `logs`: Application event logging
|
||||
* - `temp`: Temporary data storage
|
||||
*
|
||||
*
|
||||
* ## Schema Checks:
|
||||
*
|
||||
*
|
||||
* For critical tables like `contacts`, the method validates:
|
||||
* - Table structure using `PRAGMA table_info`
|
||||
* - Presence of important columns (e.g., `iViewContent`)
|
||||
* - Column data types and constraints
|
||||
*
|
||||
*
|
||||
* ## Error Handling:
|
||||
*
|
||||
*
|
||||
* This method is designed to never throw errors - it captures and logs
|
||||
* all validation issues for debugging purposes. This ensures that even
|
||||
* if integrity checks fail, they don't prevent the application from starting.
|
||||
*
|
||||
*
|
||||
* ## Logging Output:
|
||||
*
|
||||
*
|
||||
* The method produces detailed console output with emoji tags:
|
||||
* - `✅` for successful validations
|
||||
* - `❌` for validation failures
|
||||
* - `📊` for data summaries
|
||||
* - `🔍` for investigation steps
|
||||
*
|
||||
*
|
||||
* @private Internal method called after migrations
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Called automatically after migration completion
|
||||
@@ -451,70 +463,109 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
*/
|
||||
private async verifyDatabaseIntegrity(): Promise<void> {
|
||||
if (!this.db) {
|
||||
console.error(`❌ [DB-Integrity] Database not initialized`);
|
||||
logger.error(`❌ [DB-Integrity] Database not initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔍 [DB-Integrity] Starting database integrity check...`);
|
||||
logger.log(`🔍 [DB-Integrity] Starting database integrity check...`);
|
||||
|
||||
try {
|
||||
// Step 1: Check migrations table and applied migrations
|
||||
const migrationsResult = await this.db.query("SELECT name, applied_at FROM migrations ORDER BY applied_at");
|
||||
console.log(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
|
||||
const migrationsResult = await this.db.query(
|
||||
"SELECT name, applied_at FROM migrations ORDER BY applied_at",
|
||||
);
|
||||
logger.log(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);
|
||||
|
||||
// Step 2: Verify core tables exist
|
||||
const coreTableNames = ['accounts', 'secret', 'settings', 'contacts', 'logs', 'temp'];
|
||||
const coreTableNames = [
|
||||
"accounts",
|
||||
"secret",
|
||||
"settings",
|
||||
"contacts",
|
||||
"logs",
|
||||
"temp",
|
||||
];
|
||||
const existingTables: string[] = [];
|
||||
|
||||
|
||||
for (const tableName of coreTableNames) {
|
||||
try {
|
||||
const tableCheck = await this.db.query(`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`);
|
||||
const tableCheck = await this.db.query(
|
||||
`SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`,
|
||||
);
|
||||
if (tableCheck.values && tableCheck.values.length > 0) {
|
||||
existingTables.push(tableName);
|
||||
console.log(`✅ [DB-Integrity] Table ${tableName} exists`);
|
||||
logger.log(`✅ [DB-Integrity] Table ${tableName} exists`);
|
||||
} else {
|
||||
console.error(`❌ [DB-Integrity] Table ${tableName} missing`);
|
||||
logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ [DB-Integrity] Error checking table ${tableName}:`, error);
|
||||
logger.error(
|
||||
`❌ [DB-Integrity] Error checking table ${tableName}:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Check contacts table schema (including iViewContent column)
|
||||
if (existingTables.includes('contacts')) {
|
||||
if (existingTables.includes("contacts")) {
|
||||
try {
|
||||
const contactsSchema = await this.db.query("PRAGMA table_info(contacts)");
|
||||
console.log(`📊 [DB-Integrity] Contacts table schema:`, contactsSchema);
|
||||
|
||||
// Check for iViewContent column specifically
|
||||
const hasIViewContent = contactsSchema.values?.some((col: any) =>
|
||||
(col.name === 'iViewContent') || (Array.isArray(col) && col[1] === 'iViewContent')
|
||||
const contactsSchema = await this.db.query(
|
||||
"PRAGMA table_info(contacts)",
|
||||
);
|
||||
|
||||
logger.log(
|
||||
`📊 [DB-Integrity] Contacts table schema:`,
|
||||
contactsSchema,
|
||||
);
|
||||
|
||||
// Check for iViewContent column specifically
|
||||
const hasIViewContent = contactsSchema.values?.some(
|
||||
(col: unknown) =>
|
||||
(typeof col === "object" &&
|
||||
col !== null &&
|
||||
"name" in col &&
|
||||
(col as { name: string }).name === "iViewContent") ||
|
||||
(Array.isArray(col) && col[1] === "iViewContent"),
|
||||
);
|
||||
|
||||
if (hasIViewContent) {
|
||||
console.log(`✅ [DB-Integrity] iViewContent column exists in contacts table`);
|
||||
logger.log(
|
||||
`✅ [DB-Integrity] iViewContent column exists in contacts table`,
|
||||
);
|
||||
} else {
|
||||
console.error(`❌ [DB-Integrity] iViewContent column missing from contacts table`);
|
||||
logger.error(
|
||||
`❌ [DB-Integrity] iViewContent column missing from contacts table`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ [DB-Integrity] Error checking contacts schema:`, error);
|
||||
logger.error(
|
||||
`❌ [DB-Integrity] Error checking contacts schema:`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Check for basic data integrity
|
||||
try {
|
||||
const accountCount = await this.db.query("SELECT COUNT(*) as count FROM accounts");
|
||||
const settingsCount = await this.db.query("SELECT COUNT(*) as count FROM settings");
|
||||
const contactsCount = await this.db.query("SELECT COUNT(*) as count FROM contacts");
|
||||
|
||||
console.log(`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`);
|
||||
const accountCount = await this.db.query(
|
||||
"SELECT COUNT(*) as count FROM accounts",
|
||||
);
|
||||
const settingsCount = await this.db.query(
|
||||
"SELECT COUNT(*) as count FROM settings",
|
||||
);
|
||||
const contactsCount = await this.db.query(
|
||||
"SELECT COUNT(*) as count FROM contacts",
|
||||
);
|
||||
|
||||
logger.log(
|
||||
`📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(`❌ [DB-Integrity] Error checking data counts:`, error);
|
||||
logger.error(`❌ [DB-Integrity] Error checking data counts:`, error);
|
||||
}
|
||||
|
||||
console.log(`✅ [DB-Integrity] Database integrity check completed`);
|
||||
logger.log(`✅ [DB-Integrity] Database integrity check completed`);
|
||||
} catch (error) {
|
||||
console.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
|
||||
logger.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user