fix: 修复数据库解密Silent Failure导致数据丢失

- 移除静默忽略解密错误的逻辑,始终快速失败
- 添加详细的SystemCrypto初始化和解密过程日志
- 修复CommonJS require语法错误
- 确保数据库解密失败时不会创建空数据库

问题根源:异步初始化竞争条件 + Silent Failure掩盖真实错误
修复后:解密失败会明确报错,防止数据丢失

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ZacharyZcR
2025-09-23 23:37:57 +08:00
parent 009f258996
commit 084bfd9500

View File

@@ -31,9 +31,23 @@ let sqlite: Database.Database; // Module-level sqlite instance
// Async initialization function to handle SystemCrypto and DatabaseFileEncryption
async function initializeDatabaseAsync(): Promise<void> {
// Initialize SystemCrypto database key first
databaseLogger.info("Initializing SystemCrypto database key...", {
operation: "db_init_systemcrypto",
envKeyAvailable: !!process.env.DATABASE_KEY,
envKeyLength: process.env.DATABASE_KEY?.length || 0,
});
const systemCrypto = SystemCrypto.getInstance();
await systemCrypto.initializeDatabaseKey();
// Verify key is available after initialization
const dbKey = await systemCrypto.getDatabaseKey();
databaseLogger.info("SystemCrypto database key initialized", {
operation: "db_init_systemcrypto_complete",
keyLength: dbKey.length,
keyAvailable: !!dbKey,
});
if (enableFileEncryption) {
try {
// Check if encrypted database exists
@@ -43,15 +57,31 @@ async function initializeDatabaseAsync(): Promise<void> {
{
operation: "db_memory_load",
encryptedPath: encryptedDbPath,
fileSize: fs.statSync(encryptedDbPath).size,
},
);
// Decrypt database content to memory buffer (now async)
databaseLogger.info("Starting database decryption...", {
operation: "db_decrypt_start",
encryptedPath: encryptedDbPath,
});
const decryptedBuffer =
await DatabaseFileEncryption.decryptDatabaseToBuffer(encryptedDbPath);
databaseLogger.info("Database decryption successful", {
operation: "db_decrypt_success",
decryptedSize: decryptedBuffer.length,
isSqlite: decryptedBuffer.slice(0, 16).toString().startsWith('SQLite format 3'),
});
// Create in-memory database from decrypted buffer
memoryDatabase = new Database(decryptedBuffer);
databaseLogger.info("In-memory database created from decrypted buffer", {
operation: "db_memory_create_success",
});
} else {
memoryDatabase = new Database(":memory:");
isNewDatabase = true;
@@ -101,15 +131,22 @@ async function initializeDatabaseAsync(): Promise<void> {
} catch (error) {
databaseLogger.error("Failed to initialize memory database", error, {
operation: "db_memory_init_failed",
errorMessage: error instanceof Error ? error.message : "Unknown error",
errorStack: error instanceof Error ? error.stack : undefined,
encryptedDbExists: DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath),
databaseKeyAvailable: !!process.env.DATABASE_KEY,
databaseKeyLength: process.env.DATABASE_KEY?.length || 0,
});
// If file encryption is critical, fail fast
if (process.env.DB_FILE_ENCRYPTION_REQUIRED === "true") {
throw error;
}
// 🔥 CRITICAL: Never silently ignore database decryption failures!
// This causes complete data loss for users
console.error("🚨 DATABASE DECRYPTION FAILED - THIS IS CRITICAL!");
console.error("Error details:", error instanceof Error ? error.message : error);
console.error("Encrypted file exists:", DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath));
console.error("DATABASE_KEY available:", !!process.env.DATABASE_KEY);
memoryDatabase = new Database(":memory:");
isNewDatabase = true;
// Always fail fast on decryption errors - data integrity is critical
throw new Error(`Database decryption failed: ${error instanceof Error ? error.message : "Unknown error"}. This prevents data loss.`);
}
} else {
memoryDatabase = new Database(":memory:");