ios: restore db (#1063)

* wip

* wip

* wip

* refactor

* clean up

* simplify

* simplify

* refactor

* rename

* rename consts

* refactor

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
JRoberts
2022-09-17 16:41:20 +04:00
committed by GitHub
parent 3351503744
commit 2eca3e789c
5 changed files with 107 additions and 12 deletions

View File

@@ -663,6 +663,10 @@ func initializeChat(start: Bool, dbKey: String? = nil) throws {
let m = ChatModel.shared
(m.chatDbEncrypted, m.chatDbStatus) = migrateChatDatabase(dbKey)
if m.chatDbStatus != .ok { return }
// If we migrated successfully means previous re-encryption process on database level finished successfully too
if encryptionStartedDefault.get() {
encryptionStartedDefault.set(false)
}
let _ = getChatCtrl(dbKey)
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try apiSetIncognito(incognito: incognitoGroupDefault.get())

View File

@@ -133,7 +133,10 @@ struct DatabaseEncryptionView: View {
progressIndicator = true
Task {
do {
encryptionStartedDefault.set(true)
encryptionStartedAtDefault.set(Date.now)
try await apiStorageEncryption(currentKey: currentKey, newKey: newKey)
encryptionStartedDefault.set(false)
initialRandomDBPassphraseGroupDefault.set(false)
if useKeychain {
if setDatabaseKey(newKey) {

View File

@@ -15,6 +15,7 @@ struct DatabaseErrorView: View {
@State private var dbKey = ""
@State private var storedDBKey = getDatabaseKey()
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
@State private var showRestoreDbButton = false
var body: some View {
VStack(alignment: .leading, spacing: 16) {
@@ -25,7 +26,6 @@ struct DatabaseErrorView: View {
Text("Database passphrase is different from saved in the keychain.")
databaseKeyField(onSubmit: saveAndRunChat)
saveAndOpenButton()
Spacer()
Text("File: \(dbFile)")
} else {
Text("Encrypted database").font(.title)
@@ -37,30 +37,32 @@ struct DatabaseErrorView: View {
databaseKeyField(onSubmit: runChat)
openChatButton()
}
Spacer()
}
case let .error(dbFile, migrationError):
Text("Database error")
.font(.title)
Text("File: \(dbFile)")
Text("Error: \(migrationError)")
Spacer()
case .errorKeychain:
Text("Keychain error")
.font(.title)
Text("Cannot access keychain to save database password")
Spacer()
case let .unknown(json):
Text("Database error")
.font(.title)
Text("Unknown database error: \(json)")
Spacer()
case .ok:
EmptyView()
}
if showRestoreDbButton {
Spacer().frame(height: 10)
Text("The attempt to change database passphrase was not completed.")
restoreDbButton()
}
}
.padding()
.frame(maxHeight: .infinity)
.frame(maxHeight: .infinity, alignment: .topLeading)
.onAppear() { showRestoreDbButton = shouldShowRestoreDbButton() }
}
private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View {
@@ -118,6 +120,42 @@ struct DatabaseErrorView: View {
logger.error("initializeChat \(responseError(error))")
}
}
private func shouldShowRestoreDbButton() -> Bool {
if !encryptionStartedDefault.get() { return false }
let startedAt = encryptionStartedAtDefault.get()
// In case there is a small difference between saved encryptionStartedAt time and last modified timestamp on a file
let safeDiffInTime = TimeInterval(10)
return hasBackup(newerThan: startedAt - safeDiffInTime)
}
private func restoreDbButton() -> some View {
Button() {
AlertManager.shared.showAlert(Alert(
title: Text("Restore database backup?"),
message: Text("Please enter the previous password after restoring database backup. This action can not be undone."),
primaryButton: .destructive(Text("Restore")) {
restoreDb()
},
secondaryButton: .cancel()
))
} label: {
Text("Restore database backup").foregroundColor(.red)
}
}
private func restoreDb() {
do {
try restoreBackup()
showRestoreDbButton = false
encryptionStartedDefault.set(false)
} catch {
AlertManager.shared.showAlert(Alert(
title: Text("Restore database error"),
message: Text(error.localizedDescription)
))
}
}
}
struct DatabaseErrorView_Previews: PreviewProvider {

View File

@@ -26,6 +26,8 @@ let DEFAULT_CHAT_ARCHIVE_NAME = "chatArchiveName"
let DEFAULT_CHAT_ARCHIVE_TIME = "chatArchiveTime"
let DEFAULT_CHAT_V3_DB_MIGRATION = "chatV3DBMigration"
let DEFAULT_DEVELOPER_TOOLS = "developerTools"
let DEFAULT_ENCRYPTION_STARTED = "encryptionStarted"
let DEFAULT_ENCRYPTION_STARTED_AT = "encryptionStartedAt"
let DEFAULT_ACCENT_COLOR_RED = "accentColorRed"
let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen"
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue"
@@ -41,6 +43,7 @@ let appDefaults: [String: Any] = [
DEFAULT_EXPERIMENTAL_CALLS: false,
DEFAULT_CHAT_V3_DB_MIGRATION: "offer",
DEFAULT_DEVELOPER_TOOLS: false,
DEFAULT_ENCRYPTION_STARTED: false,
DEFAULT_ACCENT_COLOR_RED: 0.000,
DEFAULT_ACCENT_COLOR_GREEN: 0.533,
DEFAULT_ACCENT_COLOR_BLUE: 1.000,
@@ -51,6 +54,10 @@ private var indent: CGFloat = 36
let chatArchiveTimeDefault = DateDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CHAT_ARCHIVE_TIME)
let encryptionStartedDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_ENCRYPTION_STARTED)
let encryptionStartedAtDefault = DateDefault(defaults: UserDefaults.standard, forKey: DEFAULT_ENCRYPTION_STARTED_AT)
func setGroupDefaults() {
privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES))
}