med-det-samme/app/src/main/java/no/naiv/meddetsamme/backup/BackupManager.kt

77 lines
2.9 KiB
Kotlin
Raw Normal View History

package no.naiv.meddetsamme.backup
import android.content.Context
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import no.naiv.meddetsamme.data.MedDatabase
import no.naiv.meddetsamme.settings.SettingsStore
/**
* The one entry point for export, import and auto-backup all three share
* [BackupSerializer] and [BackupCrypto] by construction (brief decision #6).
*/
class BackupManager(private val context: Context) {
private val db get() = MedDatabase.get(context)
/** Plaintext JSON export (caller decides whether/how to encrypt). */
suspend fun exportJson(): String {
val dao = db.backupDao()
return BackupSerializer.export(
dao.allInventoryItems(),
dao.allMedications(),
dao.allDoseTimes(),
dao.allDoseLogs(),
)
}
/** Export encrypted with a *typed* passphrase (never stored — manual path). */
suspend fun exportEncrypted(passphrase: CharArray): ByteArray =
BackupCrypto.all().first().encrypt(exportJson().toByteArray(Charsets.UTF_8), passphrase)
/**
* Import from file content: encrypted (format auto-detected) or plain JSON.
* Replace-all; the caller MUST re-arm alarms afterwards.
*/
suspend fun import(data: ByteArray, passphrase: CharArray?): Int {
val crypto = BackupCrypto.detect(data)
val json = when {
crypto != null -> {
requireNotNull(passphrase) { "Denne filen er kryptert — passordfrase kreves" }
crypto.decrypt(data, passphrase).toString(Charsets.UTF_8)
}
else -> data.toString(Charsets.UTF_8)
}
val file = BackupSerializer.parse(json)
BackupSerializer.restore(db, file)
return file.medications.size
}
/**
* Unattended backup S3. Encrypts with the STORED passphrase if one is
* configured (protects against bucket compromise only see SettingsStore),
* otherwise uploads plaintext JSON by explicit user choice.
* Distinct timestamped objects; retention is the bucket's lifecycle rule's
* problem, not ours.
*/
suspend fun runAutoBackup(): String {
val settings = SettingsStore(context)
val s3 = checkNotNull(settings.s3Client()) { "Backup er ikke konfigurert" }
val json = exportJson().toByteArray(Charsets.UTF_8)
val passphrase = settings.autoBackupPassphrase
val (body, suffix) = if (passphrase.isNullOrEmpty()) {
json to "json"
} else {
val crypto = BackupCrypto.all().first()
crypto.encrypt(json, passphrase.toCharArray()) to "json.${crypto.formatId}"
}
val stamp = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'")
.withZone(ZoneOffset.UTC).format(Instant.now())
val key = "meddetsamme/backup-$stamp.$suffix"
s3.put(key, body)
return key
}
}