forked from trent_larson/crowd-funder-for-time-pwa
fix Capacitor to use the same migrations (migrations run but accounts aren't created)
This commit is contained in:
@@ -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 (<anonymous>)
|
||||
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 (<anonymous>)
|
||||
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 (<anonymous>)
|
||||
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.<computed> @ 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.<computed> @ 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.<computed> @ 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.<computed> @ main.common-DiOUyXe7.js:2103
|
||||
(anonymous) @ main.common-DiOUyXe7.js:2098
|
||||
dJ @ main.common-DiOUyXe7.js:2295
|
||||
@@ -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<void> {
|
||||
// Register all migrations
|
||||
for (const migration of MIGRATIONS) {
|
||||
await migrationService.registerMigration(migration);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runMigrations(
|
||||
sqlExec: (sql: string, params?: unknown[]) => Promise<Array<QueryExecResult>>,
|
||||
/**
|
||||
* @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<T>(
|
||||
sqlExec: (sql: string) => Promise<unknown>,
|
||||
sqlQuery: (sql: string) => Promise<T>,
|
||||
extractMigrationNames: (result: T) => Set<string>,
|
||||
): Promise<void> {
|
||||
await registerMigrations();
|
||||
await migrationService.runMigrations(sqlExec);
|
||||
for (const migration of MIGRATIONS) {
|
||||
migrationService.registerMigration(migration);
|
||||
}
|
||||
await migrationService.runMigrations(
|
||||
sqlExec,
|
||||
sqlQuery,
|
||||
extractMigrationNames,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<string> = (
|
||||
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<string> = 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;
|
||||
|
||||
|
||||
@@ -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<void> {
|
||||
registerMigration(migration: Migration) {
|
||||
this.migrations.push(migration);
|
||||
}
|
||||
|
||||
async runMigrations(
|
||||
sqlExec: (
|
||||
sql: string,
|
||||
params?: unknown[],
|
||||
) => Promise<Array<QueryExecResult>>,
|
||||
/**
|
||||
* @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<T>(
|
||||
// note that this does not take parameters because the Capacitor SQLite 'execute' is different
|
||||
sqlExec: (sql: string) => Promise<unknown>,
|
||||
sqlQuery: (sql: string) => Promise<T>,
|
||||
extractMigrationNames: (result: T) => Set<string>,
|
||||
): Promise<void> {
|
||||
// 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<unknown> = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> | null = null;
|
||||
private operationQueue: Array<QueuedOperation> = [];
|
||||
@@ -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<void> {
|
||||
private async runCapacitorMigrations(): Promise<void> {
|
||||
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<string> = (
|
||||
result,
|
||||
) => {
|
||||
const names =
|
||||
result.values?.map((row: unknown[]) => row[0] as string) || [];
|
||||
return new Set(names);
|
||||
};
|
||||
const sqlExec: (sql: string) => Promise<capSQLiteChanges> =
|
||||
this.db.execute.bind(this.db);
|
||||
const sqlQuery: (sql: string) => Promise<DBSQLiteValues> =
|
||||
this.db.query.bind(this.db);
|
||||
runMigrations(sqlExec, sqlQuery, extractMigrationNames);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user