Skip to content

Feature/backup repair database #1247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Mixin.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@
7C5DFE34284F3EA3008733FC /* UserCenterTableHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C5DFE33284F3EA3008733FC /* UserCenterTableHeaderView.swift */; };
7C6132B627953B15002777EE /* DeleteAccountAbortWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C6132B527953B15002777EE /* DeleteAccountAbortWindow.swift */; };
7C6132B827953B4F002777EE /* DeleteAccountAbortWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7C6132B727953B4F002777EE /* DeleteAccountAbortWindow.xib */; };
7C62922229B9699000B3596C /* DatabaseBackupJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C62922129B9699000B3596C /* DatabaseBackupJob.swift */; };
7C66E7B82743988500FF24C1 /* ProfileDescriptionLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C66E7B72743988500FF24C1 /* ProfileDescriptionLabel.swift */; };
7C66F0272689D1FE006D8462 /* HomeAppsDragInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C66F0262689D1FE006D8462 /* HomeAppsDragInteraction.swift */; };
7C66F029268A0384006D8462 /* AppPageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C66F028268A0384006D8462 /* AppPageCell.swift */; };
Expand Down Expand Up @@ -636,6 +637,9 @@
7CCC801C292DC68E000B4200 /* ImageCropViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCC801A292DC68E000B4200 /* ImageCropViewController.swift */; };
7CCE65A828D69D1D00FE944A /* TransferActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCE65A628D69D1D00FE944A /* TransferActionView.swift */; };
7CCE65A928D69D1D00FE944A /* TransferActionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7CCE65A728D69D1D00FE944A /* TransferActionView.xib */; };
7CD9C17229B9BE94008F8D85 /* DatabaseBackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CD9C17129B9BE94008F8D85 /* DatabaseBackupManager.swift */; };
7CD9C17429B9DDE0008F8D85 /* DatabaseRepairViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CD9C17329B9DDE0008F8D85 /* DatabaseRepairViewController.swift */; };
7CD9C17829BB306F008F8D85 /* DatabaseFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CD9C17729BB306F008F8D85 /* DatabaseFile.swift */; };
7CDBA58A28F64E5000AC3777 /* WalletTransferSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDBA58928F64E5000AC3777 /* WalletTransferSearchResultsViewController.swift */; };
7CDBA58E28F7B6CB00AC3777 /* TransferSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDBA58D28F7B6CB00AC3777 /* TransferSearchViewController.swift */; };
7CDF316C29890FB200421808 /* ConversationFontSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDF316B29890FB200421808 /* ConversationFontSet.swift */; };
Expand Down Expand Up @@ -1622,6 +1626,7 @@
7C5DFE33284F3EA3008733FC /* UserCenterTableHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCenterTableHeaderView.swift; sourceTree = "<group>"; };
7C6132B527953B15002777EE /* DeleteAccountAbortWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteAccountAbortWindow.swift; sourceTree = "<group>"; };
7C6132B727953B4F002777EE /* DeleteAccountAbortWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DeleteAccountAbortWindow.xib; sourceTree = "<group>"; };
7C62922129B9699000B3596C /* DatabaseBackupJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseBackupJob.swift; sourceTree = "<group>"; };
7C66E7B72743988500FF24C1 /* ProfileDescriptionLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDescriptionLabel.swift; sourceTree = "<group>"; };
7C66F0262689D1FE006D8462 /* HomeAppsDragInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAppsDragInteraction.swift; sourceTree = "<group>"; };
7C66F028268A0384006D8462 /* AppPageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPageCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1663,6 +1668,9 @@
7CCC801A292DC68E000B4200 /* ImageCropViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCropViewController.swift; sourceTree = "<group>"; };
7CCE65A628D69D1D00FE944A /* TransferActionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferActionView.swift; sourceTree = "<group>"; };
7CCE65A728D69D1D00FE944A /* TransferActionView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TransferActionView.xib; sourceTree = "<group>"; };
7CD9C17129B9BE94008F8D85 /* DatabaseBackupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseBackupManager.swift; sourceTree = "<group>"; };
7CD9C17329B9DDE0008F8D85 /* DatabaseRepairViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseRepairViewController.swift; sourceTree = "<group>"; };
7CD9C17729BB306F008F8D85 /* DatabaseFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFile.swift; sourceTree = "<group>"; };
7CDBA58928F64E5000AC3777 /* WalletTransferSearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletTransferSearchResultsViewController.swift; sourceTree = "<group>"; };
7CDBA58D28F7B6CB00AC3777 /* TransferSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransferSearchViewController.swift; sourceTree = "<group>"; };
7CDF316B29890FB200421808 /* ConversationFontSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationFontSet.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2368,6 +2376,8 @@
7BA24D7225342575004906AD /* HomeOverlaysCoordinator.swift */,
7BA24D76253438B9004906AD /* ViewPanningController.swift */,
94D9DF6025F89D6E00FC2F28 /* BulletinContent.swift */,
7CD9C17729BB306F008F8D85 /* DatabaseFile.swift */,
7CD9C17129B9BE94008F8D85 /* DatabaseBackupManager.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -3288,6 +3298,7 @@
DF75FF4324FE61E1008A7CF3 /* UpdateViewController.swift */,
7BDB4724215A666B008B21F9 /* SignalLoadingViewController.swift */,
DFD1FBC62302CB7A00C570D4 /* DatabaseUpgradeViewController.swift */,
7CD9C17329B9DDE0008F8D85 /* DatabaseRepairViewController.swift */,
DFB2062721ABC088006E4341 /* RestoreViewController.swift */,
7B63A8622431C9EE00D0F7C7 /* CirclesViewController.swift */,
7BB0F90F2434821000BEDA97 /* CircleEditorViewController.swift */,
Expand Down Expand Up @@ -3723,6 +3734,7 @@
947F4AD525866D6C00B0A5F9 /* InitializeFTSJob.swift */,
842347ED2695BA6400009A39 /* InitializeBotJob.swift */,
94FCB83A264683D900CCC8FD /* TranscriptAttachmentUploadJob.swift */,
7C62922129B9699000B3596C /* DatabaseBackupJob.swift */,
);
path = Job;
sourceTree = "<group>";
Expand Down Expand Up @@ -4442,6 +4454,7 @@
files = (
7BF49DD320C3DBAC00A8510E /* CaptchaManager.swift in Sources */,
DF8CECE11FC3054700E40064 /* TransferTypeCell.swift in Sources */,
7CD9C17829BB306F008F8D85 /* DatabaseFile.swift in Sources */,
7BCB8C8422BB56B8002A13CC /* DataAndStorageSettingsViewController.swift in Sources */,
9BB351671FB19ECB00EDDD2C /* ConversationDateHeaderView.swift in Sources */,
DF2819752014669E001EE5FA /* RefreshAccountJob.swift in Sources */,
Expand Down Expand Up @@ -4674,6 +4687,7 @@
E041064B23C5C3BC00A6F08E /* CoreTextLabelDelegate.swift in Sources */,
7B915F74215FB0C100A562C6 /* GiphySearchViewController.swift in Sources */,
7B7DACA623505793006AA2AC /* AudioCell.swift in Sources */,
7CD9C17429B9DDE0008F8D85 /* DatabaseRepairViewController.swift in Sources */,
5E5CA86D2674B09100C1E113 /* ScreenLockSettingViewController.swift in Sources */,
DF5D9F251F9C79E10036D5FD /* LocalizedExtension.swift in Sources */,
7B4FCCE02440A66600360F65 /* SolidBackgroundColorImageView.swift in Sources */,
Expand Down Expand Up @@ -4792,6 +4806,7 @@
7BFDB73920DA41E3005673CC /* Quote.swift in Sources */,
7B51DDB5223A489F008ACDBB /* LoginContext.swift in Sources */,
DF5D9F281F9C79E10036D5FD /* UIApplicationExtension.swift in Sources */,
7C62922229B9699000B3596C /* DatabaseBackupJob.swift in Sources */,
DF7A4B4A1FCE6EE200F21BCB /* UIViewExtension.swift in Sources */,
7C5823D5268966A1003AA142 /* HomeAppsFolderViewController.swift in Sources */,
7BD00F1E2559711A004D8814 /* WalletSearchResultsViewController.swift in Sources */,
Expand Down Expand Up @@ -4991,6 +5006,7 @@
7BEE5351222D0E5C008D3911 /* ConversationExtensionCell.swift in Sources */,
DF8CECF21FC4256D00E40064 /* BlockUserCell.swift in Sources */,
7CC730502745F95D002780F5 /* StickerStore.swift in Sources */,
7CD9C17229B9BE94008F8D85 /* DatabaseBackupManager.swift in Sources */,
7B2E56B0244EA6BB0073102C /* SettingsFooterView.swift in Sources */,
7BD7534E2182CDCE00BAC172 /* IconPrefixedTextMessageCell.swift in Sources */,
7B369206233A3314007321A7 /* SharedMediaViewController.swift in Sources */,
Expand Down
7 changes: 6 additions & 1 deletion Mixin/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if #available(iOS 15.0, *), !ProcessInfo.processInfo.isiOSAppOnMac {
UITableView.appearance().sectionHeaderTopPadding = 0
}
addObservers()
checkLogin()
ScreenLockManager.shared.lockScreenIfNeeded()
checkJailbreak()
configAnalytics()
pendingShortcutItem = launchOptions?[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem
addObservers()
Logger.general.info(category: "AppDelegate", message: "App \(Bundle.main.shortVersion)(\(Bundle.main.bundleVersion)) did finish launching with state: \(UIApplication.shared.applicationStateString), device: \(Device.current.machineName) \(ProcessInfo.processInfo.operatingSystemVersionString), id: \(Device.current.id)")
if UIApplication.shared.applicationState == .background {
MixinService.isStopProcessMessages = false
Expand Down Expand Up @@ -197,6 +197,7 @@ extension AppDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(handleClockSkew), name: MixinService.clockSkewDetectedNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(webSocketDidConnect), name: WebSocketService.didConnectNotification, object: nil)
NotificationCenter.default.addObserver(JobService.shared, selector: #selector(JobService.restoreJobs), name: WebSocketService.didSendListPendingMessageNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(databaseCorrupted), name: AppGroupUserDefaults.User.databaseCorruptedNotification, object: nil)
}

@objc func webSocketDidConnect() {
Expand Down Expand Up @@ -257,6 +258,10 @@ extension AppDelegate {
}
}

@objc func databaseCorrupted() {
mainWindow.rootViewController = makeInitialViewController()
}

}

extension AppDelegate {
Expand Down
22 changes: 22 additions & 0 deletions Mixin/Assets.xcassets/ic_repair_database.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions Mixin/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "Remove emergency contact for sure?";
"remove_from_group" = "Remove from Group";
"remove_stickers" = "Remove Stickers";
"repair" = "Repair";
"repair_chat_history" = "Repair Chat History";
"repair_chat_history_hint" = "The database file is found to be damaged, some messages may be lost, please try to repair it.";
"repair_chat_history_success" = "The chat history has been repaired successfully! Chat records before %@ have been restored.";
"repairing" = "Repairing";
"reply" = "Reply";
"report" = "Report";
"report_and_block" = "Report and block?";
Expand Down
5 changes: 5 additions & 0 deletions Mixin/Resources/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "¿Eliminar contacto de emergencia seguro?";
"remove_from_group" = "Eliminar del grupo";
"remove_stickers" = "Eliminar pegatinas";
"repair" = "Reparando";
"repair_chat_history" = "Restaurar el historial de chat";
"repair_chat_history_hint" = "El archivo de la base de datos ha sido encontrado corrupto, es posible que se pierdan algunos datos. Por favor, intente repararlo.";
"repair_chat_history_success" = "¡La reparación del historial de chat se ha completado con éxito! Los registros de chat anteriores a %@ han sido restaurados.";
"repairing" = "Reparación en proceso";
"reply" = "Responder";
"report" = "Informe";
"report_and_block" = "¿Denunciar y bloquear?";
Expand Down
5 changes: 5 additions & 0 deletions Mixin/Resources/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "本当に緊急連絡先を削除しますか?";
"remove_from_group" = "グループから退会させる";
"remove_stickers" = "スタンプの削除";
"repair" = "Repair";
"repair_chat_history" = "Repair Chat History";
"repair_chat_history_hint" = "The database file is found to be damaged, some messages may be lost, please try to repair it.";
"repair_chat_history_success" = "The chat history has been repaired successfully! Chat records before %@ have been restored.";
"repairing" = "Repairing";
"reply" = "返信";
"report" = "報告";
"report_and_block" = "報告してブロックしますか?";
Expand Down
5 changes: 5 additions & 0 deletions Mixin/Resources/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "Точно удалить экстренный контакт?";
"remove_from_group" = "Удалить из группы";
"remove_stickers" = "Удалить наклейки";
"repair" = "Repair";
"repair_chat_history" = "Repair Chat History";
"repair_chat_history_hint" = "The database file is found to be damaged, some messages may be lost, please try to repair it.";
"repair_chat_history_success" = "The chat history has been repaired successfully! Chat records before %@ have been restored.";
"repairing" = "Repairing";
"reply" = "Ответить";
"report" = "Отчет";
"report_and_block" = "Пожаловаться и заблокировать?";
Expand Down
5 changes: 5 additions & 0 deletions Mixin/Resources/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "确定删除紧急联系人?";
"remove_from_group" = "从群组中移除";
"remove_stickers" = "移除所有表情";
"repair" = "修复";
"repair_chat_history" = "修复聊天记录";
"repair_chat_history_hint" = "数据库文件被发现损坏,可能会丢失一些信息,请尝试修复它。";
"repair_chat_history_success" = "聊天记录修复成功!%@ 之前的聊天记录已经恢复。";
"repairing" = "修复中";
"reply" = "回复";
"report" = "举报";
"report_and_block" = "举报并屏蔽?";
Expand Down
5 changes: 5 additions & 0 deletions Mixin/Resources/zh-Hant.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,11 @@
"remove_emergency_contact_for_sure" = "確定刪除緊急聯絡人?";
"remove_from_group" = "從群組中移除";
"remove_stickers" = "移除所有表情";
"repair" = "修復";
"repair_chat_history" = "修復聊天記錄";
"repair_chat_history_hint" = "資料庫檔案被發現損壞,可能會丟失一些資訊,請嘗試修復它。";
"repair_chat_history_success" = "聊天記錄修復成功!%@ 之前的聊天記錄已經恢復。";
"repairing" = "修复中";
"reply" = "回覆";
"report" = "舉報";
"report_and_block" = "舉報並封鎖?";
Expand Down
32 changes: 32 additions & 0 deletions Mixin/Service/Job/DatabaseBackupJob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation
import MixinServices

class DatabaseBackupJob: AsynchronousJob {

override func getJobId() -> String {
"database-backup"
}

override func execute() -> Bool {
do {
try UserDatabase.current.writeWithoutTransaction { _ in
try DatabaseFile.removeIfExists(.temp)
try DatabaseFile.copy(at: .original, to: .temp)
}

try DatabaseFile.checkIntegrity(.temp)

try DatabaseFile.removeIfExists(.backup)
try DatabaseFile.copy(at: .temp, to: .backup)
try DatabaseFile.removeIfExists(.temp)

AppGroupUserDefaults.User.lastDatabaseBackupDate = Date()
finishJob()
} catch {
Logger.general.error(category: "BackupDatabaseJob", message: "Backup database failed: \(error)")
reporter.report(error: error)
}
return true
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import UIKit
import MixinServices

func makeInitialViewController(isUsernameJustInitialized: Bool = false) -> UIViewController {
if AppGroupUserDefaults.Account.isClockSkewed {
if AppGroupUserDefaults.User.isDatabaseCorrupted {
return DatabaseRepairViewController.instance()
} else if AppGroupUserDefaults.Account.isClockSkewed {
if let viewController = AppDelegate.current.mainWindow.rootViewController as? ClockSkewViewController {
viewController.checkFailed()
return viewController
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import UIKit
import MixinServices

class DatabaseRepairViewController: UIViewController {

@IBOutlet weak var repairButton: RoundedButton!
@IBOutlet weak var stackView: UIStackView!
@IBOutlet weak var activityIndicator: ActivityIndicatorView!

class func instance() -> DatabaseRepairViewController {
R.storyboard.home.repair_database()!
}

@IBAction func repairAction(_ sender: Any) {
repairButton.isHidden = true
stackView.isHidden = false
activityIndicator.startAnimating()
if DatabaseFile.exists(.backup) {
do {
try DatabaseFile.removeIfExists(.original)
try DatabaseFile.copy(at: .backup, to: .original)
let lastBackupDate = AppGroupUserDefaults.User.lastDatabaseBackupDate ?? Date()
let formattedDate = DateFormatter.dateFull.string(from: lastBackupDate)
stackView.isHidden = true
alert(nil, message: R.string.localizable.repair_chat_history_success(formattedDate)) { _ in
AppGroupUserDefaults.User.isDatabaseCorrupted = false
}
} catch {
LoginManager.shared.logout(reason: "Failed to repair database")
}
} else {
LoginManager.shared.logout(reason: "Failed to repair database")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ class HomeViewController: UIViewController {
if SpotlightManager.isAvailable {
SpotlightManager.shared.indexIfNeeded()
}
DatabaseBackupManager.shared.backupIfNeeded()
}
UIApplication.homeContainerViewController?.clipSwitcher.loadClipsFromPreviousSession()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation
import MixinServices

class DatabaseBackupManager: NSObject {

static let shared = DatabaseBackupManager()

override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(backupIfNeeded), name: UIApplication.didBecomeActiveNotification, object: nil)
}

@objc func backupIfNeeded() {
guard LoginManager.shared.isLoggedIn else {
return
}
let needsBackup: Bool
if let date = AppGroupUserDefaults.User.lastDatabaseBackupDate {
needsBackup = -date.timeIntervalSinceNow > TimeInterval.hour * 2
} else {
needsBackup = true
}
if needsBackup {
ConcurrentJobQueue.shared.addJob(job: DatabaseBackupJob())
}
}

}
Loading