import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { MASTER_SETTINGS_KEY, Settings } from "./tables/settings"; import { logger } from "@/utils/logger"; import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app"; export async function updateDefaultSettings( settingsChanges: Settings, ): Promise { delete settingsChanges.accountDid; // just in case // ensure there is no "id" that would override the key delete settingsChanges.id; try { const platformService = PlatformServiceFactory.getInstance(); const { sql, params } = generateUpdateStatement(settingsChanges, "settings", "id = ?", [MASTER_SETTINGS_KEY]); const result = await platformService.dbExec(sql, params); return result.changes === 1; } catch (error) { logger.error("Error updating default settings:", error); if (error instanceof Error) { throw error; // Re-throw if it's already an Error with a message } else { throw new Error( `Failed to update settings. We recommend you try again or restart the app.`, ); } } } export async function updateAccountSettings( accountDid: string, settingsChanges: Settings, ): Promise { settingsChanges.accountDid = accountDid; delete settingsChanges.id; // key off account, not ID const platform = PlatformServiceFactory.getInstance(); // First try to update existing record const { sql: updateSql, params: updateParams } = generateUpdateStatement( settingsChanges, "settings", "accountDid = ?", [accountDid] ); const updateResult = await platform.dbExec(updateSql, updateParams); // If no record was updated, insert a new one if (updateResult.changes === 1) { return true; } else { const columns = Object.keys(settingsChanges); const values = Object.values(settingsChanges); const placeholders = values.map(() => '?').join(', '); const insertSql = `INSERT INTO settings (${columns.join(', ')}) VALUES (${placeholders})`; const result = await platform.dbExec(insertSql, values); return result.changes === 1; } } const DEFAULT_SETTINGS: Settings = { id: MASTER_SETTINGS_KEY, activeDid: undefined, apiServer: DEFAULT_ENDORSER_API_SERVER, }; // retrieves default settings export async function retrieveSettingsForDefaultAccount(): Promise { const platform = PlatformServiceFactory.getInstance(); const result = await platform.dbQuery("SELECT * FROM settings WHERE id = ?", [MASTER_SETTINGS_KEY]) if (!result) { return DEFAULT_SETTINGS; } else { return mapColumnsToValues(result.columns, result.values)[0] as Settings; } } export async function retrieveSettingsForActiveAccount(): Promise { const defaultSettings = await retrieveSettingsForDefaultAccount(); if (!defaultSettings.activeDid) { return defaultSettings; } else { const platform = PlatformServiceFactory.getInstance(); const result = await platform.dbQuery( "SELECT * FROM settings WHERE accountDid = ?", [defaultSettings.activeDid] ); const overrideSettings = result ? mapColumnsToValues(result.columns, result.values)[0] as Settings : {}; const overrideSettingsFiltered = Object.fromEntries(Object.entries(overrideSettings).filter(([_, v]) => v !== null)); return { ...defaultSettings, ...overrideSettingsFiltered }; } } export async function logToDb(message: string): Promise { const platform = PlatformServiceFactory.getInstance(); const todayKey = new Date().toDateString(); // Check if we have any logs for today const result = await platform.dbQuery( "SELECT message FROM logs WHERE date = ?", [todayKey] ); if (!result || result.values.length === 0) { // If no logs for today, clear all previous logs await platform.dbExec("DELETE FROM logs"); // Insert new log const fullMessage = `${new Date().toISOString()} ${message}`; await platform.dbExec( "INSERT INTO logs (date, message) VALUES (?, ?)", [todayKey, fullMessage] ); } else { // Append to existing log const prevMessages = result.values[0][0] as string; const fullMessage = `${prevMessages}\n${new Date().toISOString()} ${message}`; await platform.dbExec( "UPDATE logs SET message = ? WHERE date = ?", [fullMessage, todayKey] ); } } // similar method is in the sw_scripts/additional-scripts.js file export async function logConsoleAndDb( message: string, isError = false, ): Promise { if (isError) { logger.error(`${new Date().toISOString()} ${message}`); } else { logger.log(`${new Date().toISOString()} ${message}`); } await logToDb(message); } /** * Generates an SQL UPDATE statement and parameters from a model object. * @param model The model object containing fields to update * @param tableName The name of the table to update * @param whereClause The WHERE clause for the update (e.g. "id = ?") * @param whereParams Parameters for the WHERE clause * @returns Object containing the SQL statement and parameters array */ function generateUpdateStatement( model: Record, tableName: string, whereClause: string, whereParams: any[] = [] ): { sql: string; params: any[] } { // Filter out undefined/null values and create SET clause const setClauses: string[] = []; const params: any[] = []; Object.entries(model).forEach(([key, value]) => { if (value !== undefined) { setClauses.push(`${key} = ?`); params.push(value); } }); if (setClauses.length === 0) { throw new Error('No valid fields to update'); } const sql = `UPDATE ${tableName} SET ${setClauses.join(', ')} WHERE ${whereClause}`; return { sql, params: [...params, ...whereParams] }; } /** * Maps an array of column names to an array of value arrays, creating objects where each column name * is mapped to its corresponding value. * @param columns Array of column names to use as object keys * @param values Array of value arrays, where each inner array corresponds to one row of data * @returns Array of objects where each object maps column names to their corresponding values */ export function mapColumnsToValues( columns: string[], values: any[][] ): Record[] { return values.map(row => { const obj: Record = {}; columns.forEach((column, index) => { obj[column] = row[index]; }); return obj; }); }