forked from jsnbuchanan/crowd-funder-for-time-pwa
feature: fleshed out capacitor and electron database operators
This commit is contained in:
@@ -6,8 +6,20 @@ import {
|
|||||||
import { Filesystem, Directory, Encoding } from "@capacitor/filesystem";
|
import { Filesystem, Directory, Encoding } from "@capacitor/filesystem";
|
||||||
import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
|
import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
|
||||||
import { Share } from "@capacitor/share";
|
import { Share } from "@capacitor/share";
|
||||||
|
import {
|
||||||
|
SQLiteConnection,
|
||||||
|
SQLiteDBConnection,
|
||||||
|
CapacitorSQLite,
|
||||||
|
Changes,
|
||||||
|
} from "@capacitor-community/sqlite";
|
||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
import { QueryExecResult } from "@/interfaces/database";
|
import { QueryExecResult, SqlValue } from "@/interfaces/database";
|
||||||
|
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
||||||
|
|
||||||
|
interface Migration {
|
||||||
|
name: string;
|
||||||
|
sql: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform service implementation for Capacitor (mobile) platform.
|
* Platform service implementation for Capacitor (mobile) platform.
|
||||||
@@ -15,8 +27,168 @@ import { QueryExecResult } from "@/interfaces/database";
|
|||||||
* - File system operations
|
* - File system operations
|
||||||
* - Camera and image picker
|
* - Camera and image picker
|
||||||
* - Platform-specific features
|
* - Platform-specific features
|
||||||
|
* - SQLite database operations
|
||||||
*/
|
*/
|
||||||
export class CapacitorPlatformService implements PlatformService {
|
export class CapacitorPlatformService implements PlatformService {
|
||||||
|
private sqlite: SQLiteConnection;
|
||||||
|
private db: SQLiteDBConnection | null = null;
|
||||||
|
private dbName = "timesafari.db";
|
||||||
|
private initialized = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeDatabase(): Promise<void> {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create/Open database
|
||||||
|
this.db = await this.sqlite.createConnection(
|
||||||
|
this.dbName,
|
||||||
|
false,
|
||||||
|
"no-encryption",
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.db.open();
|
||||||
|
|
||||||
|
// Set journal mode to WAL for better performance
|
||||||
|
await this.db.execute("PRAGMA journal_mode=WAL;");
|
||||||
|
|
||||||
|
// Run migrations
|
||||||
|
await this.runMigrations();
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
logger.log("SQLite database initialized successfully");
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error initializing SQLite database:", error);
|
||||||
|
throw new Error("Failed to initialize database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runMigrations(): 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 PRIMARY KEY,
|
||||||
|
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`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the capabilities of the Capacitor platform
|
* Gets the capabilities of the Capacitor platform
|
||||||
* @returns Platform capabilities object
|
* @returns Platform capabilities object
|
||||||
@@ -478,13 +650,54 @@ export class CapacitorPlatformService implements PlatformService {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
/**
|
||||||
throw new Error("Not implemented for " + sql + " with params " + params);
|
* @see PlatformService.dbQuery
|
||||||
|
*/
|
||||||
|
async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
||||||
|
await this.initializeDatabase();
|
||||||
|
if (!this.db) {
|
||||||
|
throw new Error("Database not initialized");
|
||||||
}
|
}
|
||||||
dbExec(
|
|
||||||
|
try {
|
||||||
|
const result = await this.db.query(sql, params || []);
|
||||||
|
const values = result.values || [];
|
||||||
|
return {
|
||||||
|
columns: [], // SQLite plugin doesn't provide column names in query result
|
||||||
|
values: values as SqlValue[][],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error executing query:", error);
|
||||||
|
throw new Error(
|
||||||
|
`Database query failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see PlatformService.dbExec
|
||||||
|
*/
|
||||||
|
async dbExec(
|
||||||
sql: string,
|
sql: string,
|
||||||
params?: unknown[],
|
params?: unknown[],
|
||||||
): Promise<{ changes: number; lastId?: number }> {
|
): Promise<{ changes: number; lastId?: number }> {
|
||||||
throw new Error("Not implemented for " + sql + " with params " + params);
|
await this.initializeDatabase();
|
||||||
|
if (!this.db) {
|
||||||
|
throw new Error("Database not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.db.run(sql, params || []);
|
||||||
|
const changes = result.changes as Changes;
|
||||||
|
return {
|
||||||
|
changes: changes?.changes || 0,
|
||||||
|
lastId: changes?.lastId,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error executing statement:", error);
|
||||||
|
throw new Error(
|
||||||
|
`Database execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,21 +4,188 @@ import {
|
|||||||
PlatformCapabilities,
|
PlatformCapabilities,
|
||||||
} from "../PlatformService";
|
} from "../PlatformService";
|
||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
import { QueryExecResult } from "@/interfaces/database";
|
import { QueryExecResult, SqlValue } from "@/interfaces/database";
|
||||||
|
import {
|
||||||
|
SQLiteConnection,
|
||||||
|
SQLiteDBConnection,
|
||||||
|
CapacitorSQLite,
|
||||||
|
Changes,
|
||||||
|
} from "@capacitor-community/sqlite";
|
||||||
|
import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app";
|
||||||
|
|
||||||
|
interface Migration {
|
||||||
|
name: string;
|
||||||
|
sql: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform service implementation for Electron (desktop) platform.
|
* Platform service implementation for Electron (desktop) platform.
|
||||||
* Note: This is a placeholder implementation with most methods currently unimplemented.
|
* Provides native desktop functionality through Electron and Capacitor plugins for:
|
||||||
* Implements the PlatformService interface but throws "Not implemented" errors for most operations.
|
* - File system operations (TODO)
|
||||||
*
|
* - Camera integration (TODO)
|
||||||
* @remarks
|
* - SQLite database operations
|
||||||
* This service is intended for desktop application functionality through Electron.
|
* - System-level features (TODO)
|
||||||
* Future implementations should provide:
|
|
||||||
* - Native file system access
|
|
||||||
* - Desktop camera integration
|
|
||||||
* - System-level features
|
|
||||||
*/
|
*/
|
||||||
export class ElectronPlatformService implements PlatformService {
|
export class ElectronPlatformService implements PlatformService {
|
||||||
|
private sqlite: SQLiteConnection;
|
||||||
|
private db: SQLiteDBConnection | null = null;
|
||||||
|
private dbName = "timesafari.db";
|
||||||
|
private initialized = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initializeDatabase(): Promise<void> {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create/Open database
|
||||||
|
this.db = await this.sqlite.createConnection(
|
||||||
|
this.dbName,
|
||||||
|
false,
|
||||||
|
"no-encryption",
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.db.open();
|
||||||
|
|
||||||
|
// Set journal mode to WAL for better performance
|
||||||
|
await this.db.execute("PRAGMA journal_mode=WAL;");
|
||||||
|
|
||||||
|
// Run migrations
|
||||||
|
await this.runMigrations();
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
logger.log("SQLite database initialized successfully");
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error initializing SQLite database:", error);
|
||||||
|
throw new Error("Failed to initialize database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runMigrations(): 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 PRIMARY KEY,
|
||||||
|
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`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the capabilities of the Electron platform
|
* Gets the capabilities of the Electron platform
|
||||||
* @returns Platform capabilities object
|
* @returns Platform capabilities object
|
||||||
@@ -56,6 +223,17 @@ export class ElectronPlatformService implements PlatformService {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes content to a file and opens the system share dialog.
|
||||||
|
* @param _fileName - Name of the file to create
|
||||||
|
* @param _content - Content to write to the file
|
||||||
|
* @throws Error with "Not implemented" message
|
||||||
|
* @todo Implement using Electron's dialog and file system APIs
|
||||||
|
*/
|
||||||
|
async writeAndShareFile(_fileName: string, _content: string): Promise<void> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a file from the filesystem.
|
* Deletes a file from the filesystem.
|
||||||
* @param _path - Path to the file to delete
|
* @param _path - Path to the file to delete
|
||||||
@@ -110,13 +288,54 @@ export class ElectronPlatformService implements PlatformService {
|
|||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
/**
|
||||||
throw new Error("Not implemented for " + sql + " with params " + params);
|
* @see PlatformService.dbQuery
|
||||||
|
*/
|
||||||
|
async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
||||||
|
await this.initializeDatabase();
|
||||||
|
if (!this.db) {
|
||||||
|
throw new Error("Database not initialized");
|
||||||
}
|
}
|
||||||
dbExec(
|
|
||||||
|
try {
|
||||||
|
const result = await this.db.query(sql, params || []);
|
||||||
|
const values = result.values || [];
|
||||||
|
return {
|
||||||
|
columns: [], // SQLite plugin doesn't provide column names in query result
|
||||||
|
values: values as SqlValue[][],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error executing query:", error);
|
||||||
|
throw new Error(
|
||||||
|
`Database query failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see PlatformService.dbExec
|
||||||
|
*/
|
||||||
|
async dbExec(
|
||||||
sql: string,
|
sql: string,
|
||||||
params?: unknown[],
|
params?: unknown[],
|
||||||
): Promise<{ changes: number; lastId?: number }> {
|
): Promise<{ changes: number; lastId?: number }> {
|
||||||
throw new Error("Not implemented for " + sql + " with params " + params);
|
await this.initializeDatabase();
|
||||||
|
if (!this.db) {
|
||||||
|
throw new Error("Database not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.db.run(sql, params || []);
|
||||||
|
const changes = result.changes as Changes;
|
||||||
|
return {
|
||||||
|
changes: changes?.changes || 0,
|
||||||
|
lastId: changes?.lastId,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error executing statement:", error);
|
||||||
|
throw new Error(
|
||||||
|
`Database execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user