diff --git a/-1748433586226.log b/-1748433586226.log deleted file mode 100644 index c460a7d7..00000000 --- a/-1748433586226.log +++ /dev/null @@ -1,101 +0,0 @@ -VM5:29 [Preload] Preload script starting... -VM5:29 [Preload] Preload script completed successfully -main.common-DiOUyXe7.js:27 Platform Object -error @ main.common-DiOUyXe7.js:27 -main.common-DiOUyXe7.js:27 PWA enabled Object -error @ main.common-DiOUyXe7.js:27 -main.common-DiOUyXe7.js:27 [Web] PWA enabled Object -error @ main.common-DiOUyXe7.js:27 -main.common-DiOUyXe7.js:27 [Web] Platform Object -error @ main.common-DiOUyXe7.js:27 -main.common-DiOUyXe7.js:29 Opened! -main.common-DiOUyXe7.js:2552 Failed to log to database: Error: no such column: value - at E.handleError (main.common-DiOUyXe7.js:27:21133) - at E.exec (main.common-DiOUyXe7.js:27:19785) - at Rc.processQueue (main.common-DiOUyXe7.js:2379:2368) -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Original message: PWA enabled - [{"pwa_enabled":false}] -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Failed to log to database: Error: no such column: value - at E.handleError (main.common-DiOUyXe7.js:27:21133) - at E.exec (main.common-DiOUyXe7.js:27:19785) - at Rc.processQueue (main.common-DiOUyXe7.js:2379:2368) - at main.common-DiOUyXe7.js:2379:2816 - at new Promise () - at Rc.queueOperation (main.common-DiOUyXe7.js:2379:2685) - at Rc.query (main.common-DiOUyXe7.js:2379:3378) - at async F7 (main.common-DiOUyXe7.js:2552:117) -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Original message: [Web] PWA enabled - [{"pwa_enabled":false}] -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Failed to log to database: Error: no such column: value - at E.handleError (main.common-DiOUyXe7.js:27:21133) - at E.exec (main.common-DiOUyXe7.js:27:19785) - at Rc.processQueue (main.common-DiOUyXe7.js:2379:2368) - at main.common-DiOUyXe7.js:2379:2816 - at new Promise () - at Rc.queueOperation (main.common-DiOUyXe7.js:2379:2685) - at Rc.query (main.common-DiOUyXe7.js:2379:3378) - at async F7 (main.common-DiOUyXe7.js:2552:117) -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Original message: [Web] Platform - [{"platform":"web"}] -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Failed to log to database: Error: no such column: value - at E.handleError (main.common-DiOUyXe7.js:27:21133) - at E.exec (main.common-DiOUyXe7.js:27:19785) - at Rc.processQueue (main.common-DiOUyXe7.js:2379:2368) - at main.common-DiOUyXe7.js:2379:2816 - at new Promise () - at Rc.queueOperation (main.common-DiOUyXe7.js:2379:2685) - at Rc.query (main.common-DiOUyXe7.js:2379:3378) - at async F7 (main.common-DiOUyXe7.js:2552:117) -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2552 Original message: Platform - [{"platform":"web"}] -F7 @ main.common-DiOUyXe7.js:2552 -main.common-DiOUyXe7.js:2100 - - - GET https://api.endorser.ch/api/report/rateLimits 400 (Bad Request) -(anonymous) @ main.common-DiOUyXe7.js:2100 -xhr @ main.common-DiOUyXe7.js:2100 -p6 @ main.common-DiOUyXe7.js:2102 -_request @ main.common-DiOUyXe7.js:2103 -request @ main.common-DiOUyXe7.js:2102 -Yc. @ main.common-DiOUyXe7.js:2103 -(anonymous) @ main.common-DiOUyXe7.js:2098 -dJ @ main.common-DiOUyXe7.js:2295 -main.common-DiOUyXe7.js:2100 - - - GET https://api.endorser.ch/api/report/rateLimits 400 (Bad Request) -(anonymous) @ main.common-DiOUyXe7.js:2100 -xhr @ main.common-DiOUyXe7.js:2100 -p6 @ main.common-DiOUyXe7.js:2102 -_request @ main.common-DiOUyXe7.js:2103 -request @ main.common-DiOUyXe7.js:2102 -Yc. @ main.common-DiOUyXe7.js:2103 -(anonymous) @ main.common-DiOUyXe7.js:2098 -dJ @ main.common-DiOUyXe7.js:2295 -await in dJ -checkRegistrationStatus @ HomeView-DJMSCuMg.js:1 -mounted @ HomeView-DJMSCuMg.js:1 -XMLHttpRequest.send -(anonymous) @ main.common-DiOUyXe7.js:2100 -xhr @ main.common-DiOUyXe7.js:2100 -p6 @ main.common-DiOUyXe7.js:2102 -_request @ main.common-DiOUyXe7.js:2103 -request @ main.common-DiOUyXe7.js:2102 -Yc. @ main.common-DiOUyXe7.js:2103 -(anonymous) @ main.common-DiOUyXe7.js:2098 -ZG @ main.common-DiOUyXe7.js:2295 -await in ZG -initializeIdentity @ HomeView-DJMSCuMg.js:1 -XMLHttpRequest.send -(anonymous) @ main.common-DiOUyXe7.js:2100 -xhr @ main.common-DiOUyXe7.js:2100 -p6 @ main.common-DiOUyXe7.js:2102 -_request @ main.common-DiOUyXe7.js:2103 -request @ main.common-DiOUyXe7.js:2102 -Yc. @ main.common-DiOUyXe7.js:2103 -(anonymous) @ main.common-DiOUyXe7.js:2098 -dJ @ main.common-DiOUyXe7.js:2295 diff --git a/src/db-sql/migration.ts b/src/db-sql/migration.ts index 7a99724f..9cadd260 100644 --- a/src/db-sql/migration.ts +++ b/src/db-sql/migration.ts @@ -1,5 +1,4 @@ import migrationService from "../services/migrationService"; -import type { QueryExecResult } from "../interfaces/database"; import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app"; import { arrayBufferToBase64 } from "@/libs/crypto"; @@ -119,16 +118,21 @@ const MIGRATIONS = [ }, ]; -export async function registerMigrations(): Promise { - // Register all migrations +/** + * @param sqlExec - A function that executes a SQL statement and returns the result + * @param extractMigrationNames - A function that extracts the names (string array) from "select name from migrations" + */ +export async function runMigrations( + sqlExec: (sql: string) => Promise, + sqlQuery: (sql: string) => Promise, + extractMigrationNames: (result: T) => Set, +): Promise { for (const migration of MIGRATIONS) { - await migrationService.registerMigration(migration); + migrationService.registerMigration(migration); } -} - -export async function runMigrations( - sqlExec: (sql: string, params?: unknown[]) => Promise>, -): Promise { - await registerMigrations(); - await migrationService.runMigrations(sqlExec); + await migrationService.runMigrations( + sqlExec, + sqlQuery, + extractMigrationNames, + ); } diff --git a/src/main.common.ts b/src/main.common.ts index ce7b6495..93891f01 100644 --- a/src/main.common.ts +++ b/src/main.common.ts @@ -13,8 +13,8 @@ import { logger } from "./utils/logger"; const platform = process.env.VITE_PLATFORM; const pwa_enabled = process.env.VITE_PWA_ENABLED === "true"; -logger.error("Platform", { platform }); -logger.error("PWA enabled", { pwa_enabled }); +logger.error("Platform", JSON.stringify({ platform })); +logger.error("PWA enabled", JSON.stringify({ pwa_enabled })); // Global Error Handler function setupGlobalErrorHandler(app: VueApp) { diff --git a/src/services/AbsurdSqlDatabaseService.ts b/src/services/AbsurdSqlDatabaseService.ts index 878dbea5..6d7dd9c9 100644 --- a/src/services/AbsurdSqlDatabaseService.ts +++ b/src/services/AbsurdSqlDatabaseService.ts @@ -100,10 +100,27 @@ class AbsurdSqlDatabaseService implements DatabaseService { // An error is thrown without this pragma: "File has invalid page size. (the first block of a new file must be written first)" await this.db.exec(`PRAGMA journal_mode=MEMORY;`); - const sqlExec = this.db.exec.bind(this.db); + const sqlExec = this.db.run.bind(this.db); + const sqlQuery = this.db.exec.bind(this.db); + + // Extract the migration names for the absurd-sql format + const extractMigrationNames: (result: QueryExecResult[]) => Set = ( + result, + ) => { + const queryResult = result as QueryExecResult[]; + // Even with the "select name" query, the QueryExecResult may be [] (which doesn't make sense to me). + if (queryResult.length > 0) { + const singleResult = queryResult[0]; + const executedMigrations: Set = new Set( + singleResult.values.map((row) => row[0] as string), + ); + return executedMigrations; + } + return new Set(); + }; // Run migrations - await runMigrations(sqlExec); + await runMigrations(sqlExec, sqlQuery, extractMigrationNames); this.initialized = true; diff --git a/src/services/migrationService.ts b/src/services/migrationService.ts index d571f1ba..8d90b0f4 100644 --- a/src/services/migrationService.ts +++ b/src/services/migrationService.ts @@ -1,6 +1,3 @@ -import { logger } from "@/utils/logger"; -import { QueryExecResult } from "../interfaces/database"; - interface Migration { name: string; sql: string; @@ -19,46 +16,55 @@ export class MigrationService { return MigrationService.instance; } - async registerMigration(migration: Migration): Promise { + registerMigration(migration: Migration) { this.migrations.push(migration); } - async runMigrations( - sqlExec: ( - sql: string, - params?: unknown[], - ) => Promise>, + /** + * @param sqlExec - A function that executes a SQL statement and returns some update result + * @param sqlQuery - A function that executes a SQL query and returns the result in some format + * @param extractMigrationNames - A function that extracts the names (string array) from a "select name from migrations" query + */ + async runMigrations( + // note that this does not take parameters because the Capacitor SQLite 'execute' is different + sqlExec: (sql: string) => Promise, + sqlQuery: (sql: string) => Promise, + extractMigrationNames: (result: T) => Set, ): Promise { + // eslint-disable-next-line no-console + console.log("Will run migrations"); + // Create migrations table if it doesn't exist - await sqlExec(` + const result0 = await sqlExec(` CREATE TABLE IF NOT EXISTS migrations ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `); + // eslint-disable-next-line no-console + console.log("Created migrations table", JSON.stringify(result0)); // Get list of executed migrations - const result: QueryExecResult[] = await sqlExec( - "SELECT name FROM migrations;", + const result1: T = await sqlQuery("SELECT name FROM migrations;"); + const executedMigrations = extractMigrationNames(result1); + // eslint-disable-next-line no-console + console.log( + "Executed migration select", + JSON.stringify(executedMigrations), ); - let executedMigrations: Set = new Set(); - // Even with that query, the QueryExecResult may be [] (which doesn't make sense to me). - if (result.length > 0) { - const singleResult = result[0]; - executedMigrations = new Set( - singleResult.values.map((row: unknown[]) => row[0]), - ); - } // Run pending migrations in order for (const migration of this.migrations) { if (!executedMigrations.has(migration.name)) { - await sqlExec(migration.sql); - await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ - migration.name, - ]); - logger.log(`Migration ${migration.name} executed successfully`); + const result2 = await sqlExec(migration.sql); + // eslint-disable-next-line no-console + console.log("Executed migration", JSON.stringify(result2)); + const result3 = await sqlExec( + `INSERT INTO migrations (name) VALUES ('${migration.name}')`, + ); + // eslint-disable-next-line no-console + console.log("Updated migrations table", JSON.stringify(result3)); } } } diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index cd237dfe..cbe6a639 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1,8 +1,3 @@ -import { - ImageResult, - PlatformService, - PlatformCapabilities, -} from "../PlatformService"; import { Filesystem, Directory, Encoding } from "@capacitor/filesystem"; import { Camera, CameraResultType, CameraSource } from "@capacitor/camera"; import { Share } from "@capacitor/share"; @@ -10,15 +5,18 @@ import { SQLiteConnection, SQLiteDBConnection, CapacitorSQLite, + capSQLiteChanges, + DBSQLiteValues, } from "@capacitor-community/sqlite"; -import { logger } from "../../utils/logger"; -import { QueryExecResult } from "@/interfaces/database"; -import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app"; -interface Migration { - name: string; - sql: string; -} +import { runMigrations } from "@/db-sql/migration"; +import { QueryExecResult } from "@/interfaces/database"; +import { + ImageResult, + PlatformService, + PlatformCapabilities, +} from "../PlatformService"; +import { logger } from "../../utils/logger"; interface QueuedOperation { type: "run" | "query" | "getOneRow" | "getAll"; @@ -39,7 +37,7 @@ interface QueuedOperation { export class CapacitorPlatformService implements PlatformService { private sqlite: SQLiteConnection; private db: SQLiteDBConnection | null = null; - private dbName = "timesafari.db"; + private dbName = "timesafari.sqlite"; private initialized = false; private initializationPromise: Promise | null = null; private operationQueue: Array = []; @@ -95,7 +93,7 @@ export class CapacitorPlatformService implements PlatformService { // await this.db.execute("PRAGMA journal_mode=WAL;"); // Run migrations - await this.runMigrations(); + await this.runCapacitorMigrations(); this.initialized = true; logger.log( @@ -170,7 +168,9 @@ export class CapacitorPlatformService implements PlatformService { } operation.resolve(result); } catch (error) { - logger.error( + // make sure you don't try to log to the DB... infinite loop! + // eslint-disable-next-line no-console + console.error( "[CapacitorPlatformService] Error while processing SQL queue:", error, " ... for sql:", @@ -231,123 +231,23 @@ export class CapacitorPlatformService implements PlatformService { } } - private async runMigrations(): Promise { + private async runCapacitorMigrations(): Promise { if (!this.db) { throw new Error("Database not initialized"); } - // Create migrations table if it doesn't exist - await this.db.execute(` - CREATE TABLE IF NOT EXISTS migrations ( - 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.db.query("SELECT name FROM migrations;"); - const executedMigrations = new Set( - result.values?.map((row) => row[0]) || [], - ); - - // Run pending migrations in order - const migrations: Migration[] = [ - { - name: "001_initial", - sql: ` - CREATE TABLE IF NOT EXISTS accounts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - dateCreated TEXT NOT NULL, - derivationPath TEXT, - did TEXT NOT NULL, - identityEncrBase64 TEXT, - mnemonicEncrBase64 TEXT, - passkeyCredIdHex TEXT, - publicKeyHex TEXT NOT NULL - ); - - CREATE INDEX IF NOT EXISTS idx_accounts_did ON accounts(did); - - CREATE TABLE IF NOT EXISTS secret ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - secretBase64 TEXT NOT NULL - ); - - CREATE TABLE IF NOT EXISTS settings ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - accountDid TEXT, - activeDid TEXT, - apiServer TEXT, - filterFeedByNearby BOOLEAN, - filterFeedByVisible BOOLEAN, - finishedOnboarding BOOLEAN, - firstName TEXT, - hideRegisterPromptOnNewContact BOOLEAN, - isRegistered BOOLEAN, - lastName TEXT, - lastAckedOfferToUserJwtId TEXT, - lastAckedOfferToUserProjectsJwtId TEXT, - lastNotifiedClaimId TEXT, - lastViewedClaimId TEXT, - notifyingNewActivityTime TEXT, - notifyingReminderMessage TEXT, - notifyingReminderTime TEXT, - partnerApiServer TEXT, - passkeyExpirationMinutes INTEGER, - profileImageUrl TEXT, - searchBoxes TEXT, - showContactGivesInline BOOLEAN, - showGeneralAdvanced BOOLEAN, - showShortcutBvc BOOLEAN, - vapid TEXT, - warnIfProdServer BOOLEAN, - warnIfTestServer BOOLEAN, - webPushServer TEXT - ); - - CREATE INDEX IF NOT EXISTS idx_settings_accountDid ON settings(accountDid); - - INSERT INTO settings (id, apiServer) VALUES (1, '${DEFAULT_ENDORSER_API_SERVER}'); - - CREATE TABLE IF NOT EXISTS contacts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - did TEXT NOT NULL, - name TEXT, - contactMethods TEXT, - nextPubKeyHashB64 TEXT, - notes TEXT, - profileImageUrl TEXT, - publicKeyBase64 TEXT, - seesMe BOOLEAN, - registered BOOLEAN - ); - - CREATE INDEX IF NOT EXISTS idx_contacts_did ON contacts(did); - CREATE INDEX IF NOT EXISTS idx_contacts_name ON contacts(name); - - CREATE TABLE IF NOT EXISTS logs ( - date TEXT, - message TEXT NOT NULL - ); - - CREATE TABLE IF NOT EXISTS temp ( - id TEXT PRIMARY KEY, - blobB64 TEXT - ); - `, - }, - ]; - - for (const migration of migrations) { - if (!executedMigrations.has(migration.name)) { - await this.db.execute(migration.sql); - await this.db.run("INSERT INTO migrations (name) VALUES (?)", [ - migration.name, - ]); - logger.log(`Migration ${migration.name} executed successfully`); - } - } + const extractMigrationNames: (result: DBSQLiteValues) => Set = ( + result, + ) => { + const names = + result.values?.map((row: unknown[]) => row[0] as string) || []; + return new Set(names); + }; + const sqlExec: (sql: string) => Promise = + this.db.execute.bind(this.db); + const sqlQuery: (sql: string) => Promise = + this.db.query.bind(this.db); + runMigrations(sqlExec, sqlQuery, extractMigrationNames); } /**