forked from jsnbuchanan/crowd-funder-for-time-pwa
add encryption for the two SQL columns, replace basic DB utils, add USE_DEXIE_DB flag, and start adding SQL everywhere
This commit is contained in:
196
src/db/databaseUtil.ts
Normal file
196
src/db/databaseUtil.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
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<boolean> {
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: Settings = {
|
||||
id: MASTER_SETTINGS_KEY,
|
||||
activeDid: undefined,
|
||||
apiServer: DEFAULT_ENDORSER_API_SERVER,
|
||||
};
|
||||
|
||||
// retrieves default settings
|
||||
export async function retrieveSettingsForDefaultAccount(): Promise<Settings> {
|
||||
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<Settings> {
|
||||
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 : {};
|
||||
return { ...defaultSettings, ...overrideSettings };
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateAccountSettings(
|
||||
accountDid: string,
|
||||
settingsChanges: Settings,
|
||||
): Promise<boolean> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export async function logToDb(message: string): Promise<void> {
|
||||
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<void> {
|
||||
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<string, any>,
|
||||
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<string, any>[] {
|
||||
return values.map(row => {
|
||||
const obj: Record<string, any> = {};
|
||||
columns.forEach((column, index) => {
|
||||
obj[column] = row[index];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user