feat: Update database version to 10 and implement migration to clear large blob attachments; adjust blob handling for IMAGE/FILE types in MessageRepository and ChatViewModel

This commit is contained in:
k1ngsterr1
2026-01-26 17:19:30 +05:00
parent 7f3edd2ad8
commit 44c0151294
3 changed files with 42 additions and 16 deletions

View File

@@ -828,7 +828,8 @@ class MessageRepository private constructor(private val context: Context) {
for (attachment in attachments) {
val jsonObj = JSONObject()
// Для MESSAGES типа расшифровываем и re-encrypt
// ⚠️ НЕ сохраняем blob для IMAGE/FILE - слишком большие (SQLite CursorWindow 2MB limit)
// Только MESSAGES (reply) сохраняем - они небольшие
if (attachment.type == AttachmentType.MESSAGES && attachment.blob.isNotEmpty()) {
try {
// 1. Расшифровываем с ChaCha ключом сообщения
@@ -842,7 +843,7 @@ class MessageRepository private constructor(private val context: Context) {
// 2. Re-encrypt с приватным ключом для хранения (как в Desktop Архиве)
val reEncryptedBlob = CryptoManager.encryptWithPassword(decryptedBlob, privateKey)
// 3. Сохраняем ЗАШИФРОВАННЫЙ blob в БД
// 3. Сохраняем ЗАШИФРОВАННЫЙ blob в БД (только для MESSAGES - они небольшие)
jsonObj.put("id", attachment.id)
jsonObj.put("blob", reEncryptedBlob) // 🔒 Зашифрован приватным ключом!
jsonObj.put("type", attachment.type.value)
@@ -850,27 +851,27 @@ class MessageRepository private constructor(private val context: Context) {
jsonObj.put("width", attachment.width)
jsonObj.put("height", attachment.height)
} else {
// Fallback - сохраняем как есть
// Fallback - пустой blob для IMAGE/FILE
jsonObj.put("id", attachment.id)
jsonObj.put("blob", attachment.blob)
jsonObj.put("blob", "")
jsonObj.put("type", attachment.type.value)
jsonObj.put("preview", attachment.preview)
jsonObj.put("width", attachment.width)
jsonObj.put("height", attachment.height)
}
} catch (e: Exception) {
// Fallback - сохраняем как есть
// Fallback - пустой blob
jsonObj.put("id", attachment.id)
jsonObj.put("blob", attachment.blob)
jsonObj.put("blob", "")
jsonObj.put("type", attachment.type.value)
jsonObj.put("preview", attachment.preview)
jsonObj.put("width", attachment.width)
jsonObj.put("height", attachment.height)
}
} else {
// Для других типов сохраняем как есть
// Для IMAGE/FILE - НЕ сохраняем blob (пустой)
jsonObj.put("id", attachment.id)
jsonObj.put("blob", attachment.blob)
jsonObj.put("blob", "") // Пустой blob для IMAGE/FILE
jsonObj.put("type", attachment.type.value)
jsonObj.put("preview", attachment.preview)
jsonObj.put("width", attachment.width)

View File

@@ -15,7 +15,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
BlacklistEntity::class,
AvatarCacheEntity::class
],
version = 9,
version = 10,
exportSchema = false
)
abstract class RosettaDatabase : RoomDatabase() {
@@ -83,6 +83,21 @@ abstract class RosettaDatabase : RoomDatabase() {
""")
}
}
/**
* 🔥 МИГРАЦИЯ 9->10: Повторная очистка blob из attachments
* Для пользователей которые уже были на версии 9
*/
private val MIGRATION_9_10 = object : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
// Очищаем все attachments с большими blob'ами
database.execSQL("""
UPDATE messages
SET attachments = '[]'
WHERE length(attachments) > 10000
""")
}
}
fun getDatabase(context: Context): RosettaDatabase {
return INSTANCE ?: synchronized(this) {
@@ -92,7 +107,7 @@ abstract class RosettaDatabase : RoomDatabase() {
"rosetta_secure.db"
)
.setJournalMode(JournalMode.WRITE_AHEAD_LOGGING) // WAL mode for performance
.addMigrations(MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9)
.addMigrations(MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10)
.fallbackToDestructiveMigration() // Для разработки - только если миграция не найдена
.build()
INSTANCE = instance

View File

@@ -1268,7 +1268,9 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
updateMessageStatus(messageId, MessageStatus.SENT)
}
// 4. 💾 Сохранение в БД с attachments (зашифрованный blob для MESSAGES)
// 4. 💾 Сохранение в БД с attachments
// ⚠️ НЕ сохраняем blob для IMAGE/FILE - слишком большие (SQLite CursorWindow 2MB limit)
// Только MESSAGES (reply) сохраняем - они небольшие
val attachmentsJson = if (messageAttachments.isNotEmpty()) {
JSONArray().apply {
messageAttachments.forEach { att ->
@@ -1276,8 +1278,13 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
put("id", att.id)
put("type", att.type.value)
put("preview", att.preview)
// 🔥 Для MESSAGES сохраняем зашифрованный приватным ключом, для остальных - как есть
put("blob", if (att.type == AttachmentType.MESSAGES) replyBlobForDatabase else att.blob)
// Только для MESSAGES сохраняем blob (reply data небольшие)
// Для IMAGE/FILE - пустой blob
val blobToSave = when (att.type) {
AttachmentType.MESSAGES -> replyBlobForDatabase ?: ""
else -> "" // IMAGE, FILE - не сохраняем blob
}
put("blob", blobToSave)
})
}
}.toString()
@@ -1397,13 +1404,14 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
updateMessageStatus(messageId, MessageStatus.SENT)
}
// Сохраняем в БД с зашифрованным blob'ом
// ⚠️ НЕ сохраняем blob в БД - он слишком большой (SQLite CursorWindow 2MB limit)
// Изображение должно храниться в файловой системе или загружаться с сервера при необходимости
val attachmentsJson = JSONArray().apply {
put(JSONObject().apply {
put("id", imageAttachment.id)
put("type", AttachmentType.IMAGE.value)
put("preview", blurhash)
put("blob", imageBlobForDatabase)
put("blob", "") // Пустой blob - не сохраняем в БД!
})
}.toString()
@@ -1521,12 +1529,14 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
updateMessageStatus(messageId, MessageStatus.SENT)
}
// ⚠️ НЕ сохраняем blob в БД - он слишком большой (SQLite CursorWindow 2MB limit)
// Файл должен храниться в файловой системе или загружаться с сервера при необходимости
val attachmentsJson = JSONArray().apply {
put(JSONObject().apply {
put("id", fileAttachment.id)
put("type", AttachmentType.FILE.value)
put("preview", preview)
put("blob", fileBlobForDatabase)
put("blob", "") // Пустой blob - не сохраняем в БД!
})
}.toString()