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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user