forked from jsnbuchanan/crowd-funder-for-time-pwa
refactor(platforms): create BaseDatabaseService to eliminate code duplication
- Create abstract BaseDatabaseService class with common database operations - Extract 7 duplicate methods from WebPlatformService and CapacitorPlatformService - Ensure consistent database logic across all platform implementations - Fix constructor inheritance issues with proper super() calls - Improve maintainability by centralizing database operations Methods consolidated: - generateInsertStatement - updateDefaultSettings - updateActiveDid - getActiveIdentity - insertNewDidIntoSettings - updateDidSpecificSettings - retrieveSettingsForActiveAccount Architecture: - BaseDatabaseService (abstract base class) - WebPlatformService extends BaseDatabaseService - CapacitorPlatformService extends BaseDatabaseService - ElectronPlatformService extends CapacitorPlatformService Benefits: - Eliminates ~200 lines of duplicate code - Guarantees consistency across platforms - Single point of maintenance for database operations - Prevents platform-specific bugs in database logic Author: Matthew Raymer Timestamp: Wed Oct 22 07:26:38 AM UTC 2025
This commit is contained in:
297
src/services/platforms/BaseDatabaseService.ts
Normal file
297
src/services/platforms/BaseDatabaseService.ts
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Base Database Service for Platform Services
|
||||||
|
* @author Matthew Raymer
|
||||||
|
*
|
||||||
|
* This abstract base class provides common database operations that are
|
||||||
|
* identical across all platform implementations. It eliminates code
|
||||||
|
* duplication and ensures consistency in database operations.
|
||||||
|
*
|
||||||
|
* Key Features:
|
||||||
|
* - Common database utility methods
|
||||||
|
* - Consistent settings management
|
||||||
|
* - Active identity management
|
||||||
|
* - Abstract methods for platform-specific database operations
|
||||||
|
*
|
||||||
|
* Architecture:
|
||||||
|
* - Abstract base class with common implementations
|
||||||
|
* - Platform services extend this class
|
||||||
|
* - Platform-specific database operations remain abstract
|
||||||
|
*
|
||||||
|
* @since 1.1.1-beta
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { logger } from "../../utils/logger";
|
||||||
|
import { QueryExecResult } from "@/interfaces/database";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for platform-specific database services.
|
||||||
|
*
|
||||||
|
* This class provides common database operations that are identical
|
||||||
|
* across all platform implementations (Web, Capacitor, Electron).
|
||||||
|
* Platform-specific services extend this class and implement the
|
||||||
|
* abstract database operation methods.
|
||||||
|
*
|
||||||
|
* Common Operations:
|
||||||
|
* - Settings management (update, retrieve, insert)
|
||||||
|
* - Active identity management
|
||||||
|
* - Database utility methods
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* export class WebPlatformService extends BaseDatabaseService {
|
||||||
|
* async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
|
||||||
|
* // Web-specific implementation
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export abstract class BaseDatabaseService {
|
||||||
|
/**
|
||||||
|
* Generate an INSERT statement for a model object.
|
||||||
|
*
|
||||||
|
* Creates a parameterized INSERT statement with placeholders for
|
||||||
|
* all properties in the model object. This ensures safe SQL
|
||||||
|
* execution and prevents SQL injection.
|
||||||
|
*
|
||||||
|
* @param model - Object containing the data to insert
|
||||||
|
* @param tableName - Name of the target table
|
||||||
|
* @returns Object containing the SQL statement and parameters
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const { sql, params } = this.generateInsertStatement(
|
||||||
|
* { name: 'John', age: 30 },
|
||||||
|
* 'users'
|
||||||
|
* );
|
||||||
|
* // sql: "INSERT INTO users (name, age) VALUES (?, ?)"
|
||||||
|
* // params: ['John', 30]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
generateInsertStatement(
|
||||||
|
model: Record<string, unknown>,
|
||||||
|
tableName: string,
|
||||||
|
): { sql: string; params: unknown[] } {
|
||||||
|
const keys = Object.keys(model);
|
||||||
|
const placeholders = keys.map(() => "?").join(", ");
|
||||||
|
const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
|
||||||
|
const params = keys.map((key) => model[key]);
|
||||||
|
return { sql, params };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update default settings for the currently active account.
|
||||||
|
*
|
||||||
|
* Retrieves the active DID from the active_identity table and updates
|
||||||
|
* the corresponding settings record. This ensures settings are always
|
||||||
|
* updated for the correct account.
|
||||||
|
*
|
||||||
|
* @param settings - Object containing the settings to update
|
||||||
|
* @returns Promise that resolves when settings are updated
|
||||||
|
*
|
||||||
|
* @throws {Error} If no active DID is found or database operation fails
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* await this.updateDefaultSettings({
|
||||||
|
* theme: 'dark',
|
||||||
|
* notifications: true
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async updateDefaultSettings(
|
||||||
|
settings: Record<string, unknown>,
|
||||||
|
): Promise<void> {
|
||||||
|
// Get current active DID and update that identity's settings
|
||||||
|
const activeIdentity = await this.getActiveIdentity();
|
||||||
|
const activeDid = activeIdentity.activeDid;
|
||||||
|
|
||||||
|
if (!activeDid) {
|
||||||
|
logger.warn(
|
||||||
|
"[BaseDatabaseService] No active DID found, cannot update default settings",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = Object.keys(settings);
|
||||||
|
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
||||||
|
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
||||||
|
const params = [...keys.map((key) => settings[key]), activeDid];
|
||||||
|
await this.dbExec(sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the active DID in the active_identity table.
|
||||||
|
*
|
||||||
|
* Sets the active DID and updates the lastUpdated timestamp.
|
||||||
|
* This is used when switching between different accounts/identities.
|
||||||
|
*
|
||||||
|
* @param did - The DID to set as active
|
||||||
|
* @returns Promise that resolves when the update is complete
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* await this.updateActiveDid('did:example:123');
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async updateActiveDid(did: string): Promise<void> {
|
||||||
|
await this.dbExec(
|
||||||
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||||
|
[did],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently active DID from the active_identity table.
|
||||||
|
*
|
||||||
|
* Retrieves the active DID that represents the currently selected
|
||||||
|
* account/identity. This is used throughout the application to
|
||||||
|
* ensure operations are performed on the correct account.
|
||||||
|
*
|
||||||
|
* @returns Promise resolving to object containing the active DID
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const { activeDid } = await this.getActiveIdentity();
|
||||||
|
* console.log('Current active DID:', activeDid);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async getActiveIdentity(): Promise<{ activeDid: string }> {
|
||||||
|
const result = (await this.dbQuery(
|
||||||
|
"SELECT activeDid FROM active_identity WHERE id = 1",
|
||||||
|
)) as QueryExecResult;
|
||||||
|
return {
|
||||||
|
activeDid: (result?.values?.[0]?.[0] as string) || "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a new DID into the settings table with default values.
|
||||||
|
*
|
||||||
|
* Creates a new settings record for a DID with default configuration
|
||||||
|
* values. Uses INSERT OR REPLACE to handle cases where settings
|
||||||
|
* already exist for the DID.
|
||||||
|
*
|
||||||
|
* @param did - The DID to create settings for
|
||||||
|
* @returns Promise that resolves when settings are created
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* await this.insertNewDidIntoSettings('did:example:123');
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async insertNewDidIntoSettings(did: string): Promise<void> {
|
||||||
|
// Import constants dynamically to avoid circular dependencies
|
||||||
|
const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
|
||||||
|
await import("@/constants/app");
|
||||||
|
|
||||||
|
// Use INSERT OR REPLACE to handle case where settings already exist for this DID
|
||||||
|
// This prevents duplicate accountDid entries and ensures data integrity
|
||||||
|
await this.dbExec(
|
||||||
|
"INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
|
||||||
|
[did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update settings for a specific DID.
|
||||||
|
*
|
||||||
|
* Updates settings for a particular DID rather than the active one.
|
||||||
|
* This is useful for bulk operations or when managing multiple accounts.
|
||||||
|
*
|
||||||
|
* @param did - The DID to update settings for
|
||||||
|
* @param settings - Object containing the settings to update
|
||||||
|
* @returns Promise that resolves when settings are updated
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* await this.updateDidSpecificSettings('did:example:123', {
|
||||||
|
* theme: 'light',
|
||||||
|
* notifications: false
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async updateDidSpecificSettings(
|
||||||
|
did: string,
|
||||||
|
settings: Record<string, unknown>,
|
||||||
|
): Promise<void> {
|
||||||
|
const keys = Object.keys(settings);
|
||||||
|
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
||||||
|
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
||||||
|
const params = [...keys.map((key) => settings[key]), did];
|
||||||
|
await this.dbExec(sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve settings for the currently active account.
|
||||||
|
*
|
||||||
|
* Gets the active DID and retrieves all settings for that account.
|
||||||
|
* Excludes the 'id' column from the returned settings object.
|
||||||
|
*
|
||||||
|
* @returns Promise resolving to settings object or null if no active DID
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const settings = await this.retrieveSettingsForActiveAccount();
|
||||||
|
* if (settings) {
|
||||||
|
* console.log('Theme:', settings.theme);
|
||||||
|
* console.log('Notifications:', settings.notifications);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async retrieveSettingsForActiveAccount(): Promise<Record<
|
||||||
|
string,
|
||||||
|
unknown
|
||||||
|
> | null> {
|
||||||
|
// Get current active DID from active_identity table
|
||||||
|
const activeIdentity = await this.getActiveIdentity();
|
||||||
|
const activeDid = activeIdentity.activeDid;
|
||||||
|
|
||||||
|
if (!activeDid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = (await this.dbQuery(
|
||||||
|
"SELECT * FROM settings WHERE accountDid = ?",
|
||||||
|
[activeDid],
|
||||||
|
)) as QueryExecResult;
|
||||||
|
if (result?.values?.[0]) {
|
||||||
|
// Convert the row to an object
|
||||||
|
const row = result.values[0];
|
||||||
|
const columns = result.columns || [];
|
||||||
|
const settings: Record<string, unknown> = {};
|
||||||
|
|
||||||
|
columns.forEach((column: string, index: number) => {
|
||||||
|
if (column !== "id") {
|
||||||
|
// Exclude the id column
|
||||||
|
settings[column] = row[index];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract methods that must be implemented by platform-specific services
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a database query (SELECT operations).
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param sql - SQL query string
|
||||||
|
* @param params - Optional parameters for prepared statements
|
||||||
|
* @returns Promise resolving to query results
|
||||||
|
*/
|
||||||
|
abstract dbQuery(sql: string, params?: unknown[]): Promise<unknown>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a database statement (INSERT, UPDATE, DELETE operations).
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param sql - SQL statement string
|
||||||
|
* @param params - Optional parameters for prepared statements
|
||||||
|
* @returns Promise resolving to execution results
|
||||||
|
*/
|
||||||
|
abstract dbExec(sql: string, params?: unknown[]): Promise<unknown>;
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
PlatformCapabilities,
|
PlatformCapabilities,
|
||||||
} from "../PlatformService";
|
} from "../PlatformService";
|
||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
|
import { BaseDatabaseService } from "./BaseDatabaseService";
|
||||||
|
|
||||||
interface QueuedOperation {
|
interface QueuedOperation {
|
||||||
type: "run" | "query" | "rawQuery";
|
type: "run" | "query" | "rawQuery";
|
||||||
@@ -39,7 +40,10 @@ interface QueuedOperation {
|
|||||||
* - Platform-specific features
|
* - Platform-specific features
|
||||||
* - SQLite database operations
|
* - SQLite database operations
|
||||||
*/
|
*/
|
||||||
export class CapacitorPlatformService implements PlatformService {
|
export class CapacitorPlatformService
|
||||||
|
extends BaseDatabaseService
|
||||||
|
implements PlatformService
|
||||||
|
{
|
||||||
/** Current camera direction */
|
/** Current camera direction */
|
||||||
private currentDirection: CameraDirection = CameraDirection.Rear;
|
private currentDirection: CameraDirection = CameraDirection.Rear;
|
||||||
|
|
||||||
@@ -52,6 +56,7 @@ export class CapacitorPlatformService implements PlatformService {
|
|||||||
private isProcessingQueue: boolean = false;
|
private isProcessingQueue: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
this.sqlite = new SQLiteConnection(CapacitorSQLite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1328,110 +1333,8 @@ export class CapacitorPlatformService implements PlatformService {
|
|||||||
// --- PWA/Web-only methods (no-op for Capacitor) ---
|
// --- PWA/Web-only methods (no-op for Capacitor) ---
|
||||||
public registerServiceWorker(): void {}
|
public registerServiceWorker(): void {}
|
||||||
|
|
||||||
// Database utility methods
|
// Database utility methods - inherited from BaseDatabaseService
|
||||||
generateInsertStatement(
|
// generateInsertStatement, updateDefaultSettings, updateActiveDid,
|
||||||
model: Record<string, unknown>,
|
// getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
|
||||||
tableName: string,
|
// retrieveSettingsForActiveAccount are all inherited from BaseDatabaseService
|
||||||
): { sql: string; params: unknown[] } {
|
|
||||||
const keys = Object.keys(model);
|
|
||||||
const placeholders = keys.map(() => "?").join(", ");
|
|
||||||
const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
|
|
||||||
const params = keys.map((key) => model[key]);
|
|
||||||
return { sql, params };
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDefaultSettings(
|
|
||||||
settings: Record<string, unknown>,
|
|
||||||
): Promise<void> {
|
|
||||||
// Get current active DID and update that identity's settings
|
|
||||||
const activeIdentity = await this.getActiveIdentity();
|
|
||||||
const activeDid = activeIdentity.activeDid;
|
|
||||||
|
|
||||||
if (!activeDid) {
|
|
||||||
logger.warn(
|
|
||||||
"[CapacitorPlatformService] No active DID found, cannot update default settings",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(settings);
|
|
||||||
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
|
||||||
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
|
||||||
const params = [...keys.map((key) => settings[key]), activeDid];
|
|
||||||
await this.dbExec(sql, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateActiveDid(did: string): Promise<void> {
|
|
||||||
await this.dbExec(
|
|
||||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
|
||||||
[did],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getActiveIdentity(): Promise<{ activeDid: string }> {
|
|
||||||
const result = await this.dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1",
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
activeDid: (result?.values?.[0]?.[0] as string) || "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async insertNewDidIntoSettings(did: string): Promise<void> {
|
|
||||||
// Import constants dynamically to avoid circular dependencies
|
|
||||||
const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
|
|
||||||
await import("@/constants/app");
|
|
||||||
|
|
||||||
// Use INSERT OR REPLACE to handle case where settings already exist for this DID
|
|
||||||
// This prevents duplicate accountDid entries and ensures data integrity
|
|
||||||
await this.dbExec(
|
|
||||||
"INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
|
|
||||||
[did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDidSpecificSettings(
|
|
||||||
did: string,
|
|
||||||
settings: Record<string, unknown>,
|
|
||||||
): Promise<void> {
|
|
||||||
const keys = Object.keys(settings);
|
|
||||||
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
|
||||||
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
|
||||||
const params = [...keys.map((key) => settings[key]), did];
|
|
||||||
await this.dbExec(sql, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrieveSettingsForActiveAccount(): Promise<Record<
|
|
||||||
string,
|
|
||||||
unknown
|
|
||||||
> | null> {
|
|
||||||
// Get current active DID from active_identity table
|
|
||||||
const activeIdentity = await this.getActiveIdentity();
|
|
||||||
const activeDid = activeIdentity.activeDid;
|
|
||||||
|
|
||||||
if (!activeDid) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await this.dbQuery(
|
|
||||||
"SELECT * FROM settings WHERE accountDid = ?",
|
|
||||||
[activeDid],
|
|
||||||
);
|
|
||||||
if (result?.values?.[0]) {
|
|
||||||
// Convert the row to an object
|
|
||||||
const row = result.values[0];
|
|
||||||
const columns = result.columns || [];
|
|
||||||
const settings: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
columns.forEach((column, index) => {
|
|
||||||
if (column !== "id") {
|
|
||||||
// Exclude the id column
|
|
||||||
settings[column] = row[index];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
} from "../PlatformService";
|
} from "../PlatformService";
|
||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
import { QueryExecResult } from "@/interfaces/database";
|
import { QueryExecResult } from "@/interfaces/database";
|
||||||
|
import { BaseDatabaseService } from "./BaseDatabaseService";
|
||||||
// Dynamic import of initBackend to prevent worker context errors
|
// Dynamic import of initBackend to prevent worker context errors
|
||||||
import type {
|
import type {
|
||||||
WorkerRequest,
|
WorkerRequest,
|
||||||
@@ -29,7 +30,10 @@ import type {
|
|||||||
* Note: File system operations are not available in the web platform
|
* Note: File system operations are not available in the web platform
|
||||||
* due to browser security restrictions. These methods throw appropriate errors.
|
* due to browser security restrictions. These methods throw appropriate errors.
|
||||||
*/
|
*/
|
||||||
export class WebPlatformService implements PlatformService {
|
export class WebPlatformService
|
||||||
|
extends BaseDatabaseService
|
||||||
|
implements PlatformService
|
||||||
|
{
|
||||||
private static instanceCount = 0; // Debug counter
|
private static instanceCount = 0; // Debug counter
|
||||||
private worker: Worker | null = null;
|
private worker: Worker | null = null;
|
||||||
private workerReady = false;
|
private workerReady = false;
|
||||||
@@ -46,6 +50,7 @@ export class WebPlatformService implements PlatformService {
|
|||||||
private readonly messageTimeout = 30000; // 30 seconds
|
private readonly messageTimeout = 30000; // 30 seconds
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
WebPlatformService.instanceCount++;
|
WebPlatformService.instanceCount++;
|
||||||
|
|
||||||
// Use debug level logging for development mode to reduce console noise
|
// Use debug level logging for development mode to reduce console noise
|
||||||
@@ -670,116 +675,8 @@ export class WebPlatformService implements PlatformService {
|
|||||||
// SharedArrayBuffer initialization is handled by initBackend call in initializeWorker
|
// SharedArrayBuffer initialization is handled by initBackend call in initializeWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database utility methods
|
// Database utility methods - inherited from BaseDatabaseService
|
||||||
generateInsertStatement(
|
// generateInsertStatement, updateDefaultSettings, updateActiveDid,
|
||||||
model: Record<string, unknown>,
|
// getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
|
||||||
tableName: string,
|
// retrieveSettingsForActiveAccount are all inherited from BaseDatabaseService
|
||||||
): { sql: string; params: unknown[] } {
|
|
||||||
const keys = Object.keys(model);
|
|
||||||
const placeholders = keys.map(() => "?").join(", ");
|
|
||||||
const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
|
|
||||||
const params = keys.map((key) => model[key]);
|
|
||||||
return { sql, params };
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDefaultSettings(
|
|
||||||
settings: Record<string, unknown>,
|
|
||||||
): Promise<void> {
|
|
||||||
// Get current active DID and update that identity's settings
|
|
||||||
const activeIdentity = await this.getActiveIdentity();
|
|
||||||
const activeDid = activeIdentity.activeDid;
|
|
||||||
|
|
||||||
if (!activeDid) {
|
|
||||||
logger.warn(
|
|
||||||
"[WebPlatformService] No active DID found, cannot update default settings",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(settings);
|
|
||||||
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
|
||||||
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
|
||||||
const params = [...keys.map((key) => settings[key]), activeDid];
|
|
||||||
await this.dbExec(sql, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateActiveDid(did: string): Promise<void> {
|
|
||||||
await this.dbExec(
|
|
||||||
"INSERT OR REPLACE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, ?)",
|
|
||||||
[did, new Date().toISOString()],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getActiveIdentity(): Promise<{ activeDid: string }> {
|
|
||||||
const result = await this.dbQuery(
|
|
||||||
"SELECT activeDid FROM active_identity WHERE id = 1",
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
activeDid: (result?.values?.[0]?.[0] as string) || "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async insertNewDidIntoSettings(did: string): Promise<void> {
|
|
||||||
// Import constants dynamically to avoid circular dependencies
|
|
||||||
const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
|
|
||||||
await import("@/constants/app");
|
|
||||||
|
|
||||||
// Use INSERT OR REPLACE to handle case where settings already exist for this DID
|
|
||||||
// This prevents duplicate accountDid entries and ensures data integrity
|
|
||||||
await this.dbExec(
|
|
||||||
"INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
|
|
||||||
[did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDidSpecificSettings(
|
|
||||||
did: string,
|
|
||||||
settings: Record<string, unknown>,
|
|
||||||
): Promise<void> {
|
|
||||||
const keys = Object.keys(settings);
|
|
||||||
const setClause = keys.map((key) => `${key} = ?`).join(", ");
|
|
||||||
const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
|
|
||||||
const params = [...keys.map((key) => settings[key]), did];
|
|
||||||
// Log update operation for debugging
|
|
||||||
logger.debug(
|
|
||||||
"[WebPlatformService] updateDidSpecificSettings",
|
|
||||||
sql,
|
|
||||||
JSON.stringify(params, null, 2),
|
|
||||||
);
|
|
||||||
await this.dbExec(sql, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async retrieveSettingsForActiveAccount(): Promise<Record<
|
|
||||||
string,
|
|
||||||
unknown
|
|
||||||
> | null> {
|
|
||||||
// Get current active DID from active_identity table
|
|
||||||
const activeIdentity = await this.getActiveIdentity();
|
|
||||||
const activeDid = activeIdentity.activeDid;
|
|
||||||
|
|
||||||
if (!activeDid) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await this.dbQuery(
|
|
||||||
"SELECT * FROM settings WHERE accountDid = ?",
|
|
||||||
[activeDid],
|
|
||||||
);
|
|
||||||
if (result?.values?.[0]) {
|
|
||||||
// Convert the row to an object
|
|
||||||
const row = result.values[0];
|
|
||||||
const columns = result.columns || [];
|
|
||||||
const settings: Record<string, unknown> = {};
|
|
||||||
|
|
||||||
columns.forEach((column, index) => {
|
|
||||||
if (column !== "id") {
|
|
||||||
// Exclude the id column
|
|
||||||
settings[column] = row[index];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user