import { getDb, DatabaseSaveTrigger } from "../database/db/index.js"; import { DataCrypto } from "./data-crypto.js"; import { databaseLogger } from "./logger.js"; import type { SQLiteTable } from "drizzle-orm/sqlite-core"; type TableName = "users" | "ssh_data" | "ssh_credentials"; /** * SimpleDBOps - Simplified encrypted database operations * * Linus-style simplification: * - Remove all complex abstraction layers * - Direct CRUD operations * - Automatic encryption/decryption * - No special case handling */ class SimpleDBOps { /** * Insert encrypted record */ static async insert>( table: SQLiteTable, tableName: TableName, data: T, userId: string, ): Promise { // Get user data key once and reuse throughout operation const userDataKey = DataCrypto.validateUserAccess(userId); // Generate consistent temporary ID for encryption context if record has no ID const tempId = data.id || `temp-${userId}-${Date.now()}`; const dataWithTempId = { ...data, id: tempId }; // Encrypt data using the locked key - recordId will be stored in encrypted fields const encryptedData = DataCrypto.encryptRecord(tableName, dataWithTempId, userId, userDataKey); // Remove temp ID if it was generated, let database assign real ID if (!data.id) { delete encryptedData.id; } // Insert into database const result = await getDb().insert(table).values(encryptedData).returning(); // Trigger database save after insert DatabaseSaveTrigger.triggerSave(`insert_${tableName}`); // Decrypt return result using the same key - FieldCrypto will use stored recordId const decryptedResult = DataCrypto.decryptRecord( tableName, result[0], userId, userDataKey ); return decryptedResult as T; } /** * Query multiple records */ static async select>( query: any, tableName: TableName, userId: string, ): Promise { // Get user data key once and reuse throughout operation const userDataKey = DataCrypto.validateUserAccess(userId); // Execute query const results = await query; // Decrypt results using locked key const decryptedResults = DataCrypto.decryptRecords( tableName, results, userId, userDataKey ); return decryptedResults; } /** * Query single record */ static async selectOne>( query: any, tableName: TableName, userId: string, ): Promise { // Get user data key once and reuse throughout operation const userDataKey = DataCrypto.validateUserAccess(userId); // Execute query const result = await query; if (!result) return undefined; // Decrypt results using locked key const decryptedResult = DataCrypto.decryptRecord( tableName, result, userId, userDataKey ); return decryptedResult; } /** * Update record */ static async update>( table: SQLiteTable, tableName: TableName, where: any, data: Partial, userId: string, ): Promise { // Get user data key once and reuse throughout operation const userDataKey = DataCrypto.validateUserAccess(userId); // Encrypt update data using the locked key const encryptedData = DataCrypto.encryptRecord(tableName, data, userId, userDataKey); // Execute update const result = await getDb() .update(table) .set(encryptedData) .where(where) .returning(); // Trigger database save after update DatabaseSaveTrigger.triggerSave(`update_${tableName}`); // Decrypt return data using the same key const decryptedResults = DataCrypto.decryptRecords( tableName, result, userId, userDataKey ); return decryptedResults as T[]; } /** * Delete record */ static async delete( table: SQLiteTable, tableName: TableName, where: any, userId: string, ): Promise { const result = await getDb().delete(table).where(where).returning(); // Trigger database save after delete DatabaseSaveTrigger.triggerSave(`delete_${tableName}`); return result; } /** * Health check */ static async healthCheck(userId: string): Promise { return DataCrypto.canUserAccessData(userId); } /** * Special method: return encrypted data (for auto-start scenarios) * No decryption, return data in encrypted state directly */ static async selectEncrypted(query: any, tableName: TableName): Promise { // Execute query directly, no decryption const results = await query; return results; } } export { SimpleDBOps, type TableName };