Files
mobile-android/app/src/main/java/com/rosetta/messenger/database/DatabaseService.kt

206 lines
6.2 KiB
Kotlin

package com.rosetta.messenger.database
import android.content.Context
import com.rosetta.messenger.crypto.CryptoManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import java.time.Instant
/**
* Service for secure database operations
* Matches the architecture from React Native app
*/
class DatabaseService(context: Context) {
private val database = RosettaDatabase.getDatabase(context)
private val accountDao = database.accountDao()
// 🚀 ОПТИМИЗАЦИЯ: LRU кэш для зашифрованных аккаунтов (избегаем повторных запросов к БД)
private val accountCache = mutableMapOf<String, EncryptedAccountEntity>()
private val cacheMaxSize = 10
companion object {
private const val TAG = "DatabaseService"
@Volatile
private var INSTANCE: DatabaseService? = null
fun getInstance(context: Context): DatabaseService {
return INSTANCE ?: synchronized(this) {
val instance = DatabaseService(context.applicationContext)
INSTANCE = instance
instance
}
}
}
/**
* Saves encrypted account to database
*/
suspend fun saveEncryptedAccount(
publicKey: String,
privateKeyEncrypted: String,
seedPhraseEncrypted: String
): Boolean = withContext(Dispatchers.IO) {
try {
val account = EncryptedAccountEntity(
publicKey = publicKey,
privateKeyEncrypted = privateKeyEncrypted,
seedPhraseEncrypted = seedPhraseEncrypted,
createdAt = Instant.now().toString(),
lastUsed = Instant.now().toString(),
isActive = true
)
accountDao.insertAccount(account)
// 🚀 ОПТИМИЗАЦИЯ: Обновляем кэш после сохранения
accountCache[publicKey] = account
true
} catch (e: Exception) {
false
}
}
/**
* Loads encrypted account from database
* 🚀 ОПТИМИЗАЦИЯ: Использует кэш для избежания повторных запросов
*/
suspend fun getEncryptedAccount(publicKey: String): EncryptedAccountEntity? =
withContext(Dispatchers.IO) {
try {
// Проверяем кэш сначала
accountCache[publicKey]?.let { return@withContext it }
// Загружаем из БД и кэшируем
val account = accountDao.getAccount(publicKey)
account?.let {
accountCache[publicKey] = it
// Ограничиваем размер кэша
if (accountCache.size > cacheMaxSize) {
accountCache.remove(accountCache.keys.first())
}
}
account
} catch (e: Exception) {
null
}
}
/**
* Gets all encrypted accounts
*/
suspend fun getAllEncryptedAccounts(): List<EncryptedAccountEntity> =
withContext(Dispatchers.IO) {
try {
accountDao.getAllAccounts()
} catch (e: Exception) {
emptyList()
}
}
/**
* Gets all accounts as Flow for reactive updates
*/
fun getAllAccountsFlow(): Flow<List<EncryptedAccountEntity>> {
return accountDao.getAllAccountsFlow()
}
/**
* Updates last used timestamp for account
*/
suspend fun updateLastUsed(publicKey: String) = withContext(Dispatchers.IO) {
try {
accountDao.updateLastUsed(publicKey, Instant.now().toString())
} catch (e: Exception) {
}
}
/**
* Deletes account from database
*/
suspend fun deleteAccount(publicKey: String): Boolean = withContext(Dispatchers.IO) {
try {
accountDao.deleteAccount(publicKey)
true
} catch (e: Exception) {
false
}
}
/**
* Checks if any accounts exist
*/
suspend fun hasAccounts(): Boolean = withContext(Dispatchers.IO) {
try {
accountDao.getAccountCount() > 0
} catch (e: Exception) {
false
}
}
/**
* Decrypts account with password
* Returns decrypted private key and seed phrase
*/
suspend fun decryptAccount(
publicKey: String,
password: String
): DecryptedAccountData? = withContext(Dispatchers.IO) {
try {
val encryptedAccount = getEncryptedAccount(publicKey) ?: run {
return@withContext null
}
// Decrypt private key
val privateKey = try {
CryptoManager.decryptWithPassword(
encryptedAccount.privateKeyEncrypted,
password
) ?: run {
return@withContext null
}
} catch (e: Exception) {
return@withContext null
}
// Decrypt seed phrase
val seedPhraseString = try {
CryptoManager.decryptWithPassword(
encryptedAccount.seedPhraseEncrypted,
password
) ?: run {
return@withContext null
}
} catch (e: Exception) {
return@withContext null
}
val seedPhrase = seedPhraseString.split(" ")
// Generate private key hash for protocol
val privateKeyHash = CryptoManager.generatePrivateKeyHash(privateKey)
DecryptedAccountData(
publicKey = publicKey,
privateKey = privateKey,
privateKeyHash = privateKeyHash,
seedPhrase = seedPhrase
)
} catch (e: Exception) {
null
}
}
}
/**
* Decrypted account data
*/
data class DecryptedAccountData(
val publicKey: String,
val privateKey: String,
val privateKeyHash: String,
val seedPhrase: List<String>
)