mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:08:40 +00:00
create backup zip file
This commit is contained in:
parent
4536e82e1b
commit
fcc2c9fb9e
13 changed files with 119 additions and 22 deletions
|
|
@ -1,4 +1,7 @@
|
|||
class SecureStorageKeys {
|
||||
static const String signalIdentity = "signal_identity";
|
||||
static const String signalSignedPreKey = "signed_pre_key_store";
|
||||
static const String apiAuthToken = "api_auth_token";
|
||||
static const String googleFcm = "google_fcm";
|
||||
static const String userData = "userData";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ class SignalDao extends DatabaseAccessor<TwonlyDatabase> with _$SignalDaoMixin {
|
|||
// this constructor is required so that the main database can create an instance
|
||||
// of this object.
|
||||
SignalDao(super.db);
|
||||
|
||||
Future deleteAllByContactId(int contactId) async {
|
||||
await (delete(signalContactPreKeys)
|
||||
..where((t) => t.contactId.equals(contactId)))
|
||||
|
|
|
|||
|
|
@ -129,4 +129,13 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
|||
notifyUpdates({TableUpdate.onTable(messages, kind: UpdateKind.update)});
|
||||
notifyUpdates({TableUpdate.onTable(contacts, kind: UpdateKind.update)});
|
||||
}
|
||||
|
||||
Future deleteDataForTwonlySafe() async {
|
||||
await delete(messages).go();
|
||||
await delete(messageRetransmissions).go();
|
||||
await delete(mediaDownloads).go();
|
||||
await delete(mediaUploads).go();
|
||||
await delete(signalContactPreKeys).go();
|
||||
await delete(signalContactSignedPreKeys).go();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:mutex/mutex.dart';
|
|||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/app.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/websocket/client_to_server.pbserver.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/websocket/error.pb.dart';
|
||||
|
|
@ -329,7 +330,8 @@ class ApiService {
|
|||
|
||||
Future<bool> tryAuthenticateWithToken(int userId) async {
|
||||
final storage = FlutterSecureStorage();
|
||||
String? apiAuthToken = await storage.read(key: "api_auth_token");
|
||||
String? apiAuthToken =
|
||||
await storage.read(key: SecureStorageKeys.apiAuthToken);
|
||||
|
||||
if (apiAuthToken != null) {
|
||||
final authenticate = Handshake_Authenticate()
|
||||
|
|
@ -414,7 +416,8 @@ class ApiService {
|
|||
String apiAuthTokenB64 = base64Encode(apiAuthToken);
|
||||
|
||||
final storage = FlutterSecureStorage();
|
||||
await storage.write(key: "api_auth_token", value: apiAuthTokenB64);
|
||||
await storage.write(
|
||||
key: SecureStorageKeys.apiAuthToken, value: apiAuthTokenB64);
|
||||
|
||||
await tryAuthenticateWithToken(userData.userId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:mutex/mutex.dart';
|
|||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/database/tables/media_uploads_table.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
|
@ -562,8 +563,8 @@ Future<bool> handleMediaUpload(MediaUpload media) async {
|
|||
|
||||
final uploadRequestBytes = uploadRequest.writeToBuffer();
|
||||
|
||||
final storage = FlutterSecureStorage();
|
||||
String? apiAuthToken = await storage.read(key: "api_auth_token");
|
||||
String? apiAuthToken =
|
||||
await FlutterSecureStorage().read(key: SecureStorageKeys.apiAuthToken);
|
||||
if (apiAuthToken == null) {
|
||||
Log.error("api auth token not defined.");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,79 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hashlib/hashlib.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
Future performTwonlySafeBackup() async {
|
||||
Log.info("Starting new backup creation.");
|
||||
final baseDir = (await getApplicationSupportDirectory()).path;
|
||||
|
||||
var originalDatabase = File(join(baseDir, "twonly_database.sqlite"));
|
||||
var backupDir = Directory(join(baseDir, "backup_twonly_safe/"));
|
||||
if (backupDir.existsSync()) {
|
||||
await backupDir.delete(recursive: true);
|
||||
}
|
||||
await backupDir.create(recursive: true);
|
||||
|
||||
var backupDatabaseFile =
|
||||
File(join(backupDir.path, "twonly_database.backup.sqlite"));
|
||||
|
||||
// copy database
|
||||
await originalDatabase.copy(backupDatabaseFile.path);
|
||||
|
||||
final backupDB = TwonlyDatabase(
|
||||
driftDatabase(
|
||||
name: "twonly_database.backup",
|
||||
native: DriftNativeOptions(
|
||||
databaseDirectory: () async {
|
||||
return backupDir;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await backupDB.deleteDataForTwonlySafe();
|
||||
|
||||
var secureStorageBackup = {};
|
||||
final storage = FlutterSecureStorage();
|
||||
secureStorageBackup[SecureStorageKeys.signalIdentity] =
|
||||
await storage.read(key: SecureStorageKeys.signalIdentity);
|
||||
secureStorageBackup[SecureStorageKeys.signalSignedPreKey] =
|
||||
await storage.read(key: SecureStorageKeys.signalSignedPreKey);
|
||||
secureStorageBackup[SecureStorageKeys.userData] =
|
||||
await storage.read(key: SecureStorageKeys.userData);
|
||||
|
||||
var backupSecureStorage = File(join(backupDir.path, "secure_storage.json"));
|
||||
|
||||
await backupSecureStorage.writeAsString(jsonEncode(secureStorageBackup));
|
||||
|
||||
Log.info("Backup files created.");
|
||||
|
||||
var twonlySafeBackupZip = File(join(backupDir.path, "twonly_safe.zip"));
|
||||
|
||||
await createZipArchive(
|
||||
twonlySafeBackupZip.path, [backupSecureStorage, backupDatabaseFile]);
|
||||
|
||||
// await backupDir.delete(recursive: true);
|
||||
}
|
||||
|
||||
Future<void> createZipArchive(String zipFilePath, List<File> filesToZip) async {
|
||||
final encoder = ZipFileEncoder();
|
||||
encoder.create(zipFilePath);
|
||||
for (var file in filesToZip) {
|
||||
await encoder.addFile(file);
|
||||
}
|
||||
await encoder.close();
|
||||
}
|
||||
|
||||
Future enableTwonlySafe(String password) async {
|
||||
final user = await getUser();
|
||||
if (user == null) return;
|
||||
|
|
@ -16,6 +87,7 @@ Future enableTwonlySafe(String password) async {
|
|||
return user;
|
||||
});
|
||||
startTwonlySafeBackup();
|
||||
performTwonlySafeBackup();
|
||||
}
|
||||
|
||||
Future disableTwonlySafe() async {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/app.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/services/notification.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -16,7 +17,7 @@ Future initFCMAfterAuthenticated() async {
|
|||
|
||||
final storage = FlutterSecureStorage();
|
||||
|
||||
String? storedToken = await storage.read(key: "google_fcm");
|
||||
String? storedToken = await storage.read(key: SecureStorageKeys.googleFcm);
|
||||
|
||||
try {
|
||||
final fcmToken = await FirebaseMessaging.instance.getToken();
|
||||
|
|
@ -27,12 +28,12 @@ Future initFCMAfterAuthenticated() async {
|
|||
|
||||
if (storedToken == null || fcmToken != storedToken) {
|
||||
await apiService.updateFCMToken(fcmToken);
|
||||
await storage.write(key: "google_fcm", value: fcmToken);
|
||||
await storage.write(key: SecureStorageKeys.googleFcm, value: fcmToken);
|
||||
}
|
||||
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) async {
|
||||
await apiService.updateFCMToken(fcmToken);
|
||||
await storage.write(key: "google_fcm", value: fcmToken);
|
||||
await storage.write(key: SecureStorageKeys.googleFcm, value: fcmToken);
|
||||
}).onError((err) {
|
||||
Log.error("could not listen on token refresh");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|||
import 'package:mutex/mutex.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/model/json/userdata.dart';
|
||||
import 'package:twonly/src/providers/connection.provider.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -17,8 +18,8 @@ Future<bool> isUserCreated() async {
|
|||
}
|
||||
|
||||
Future<UserData?> getUser() async {
|
||||
final storage = FlutterSecureStorage();
|
||||
String? userJson = await storage.read(key: "userData");
|
||||
String? userJson =
|
||||
await FlutterSecureStorage().read(key: SecureStorageKeys.userData);
|
||||
if (userJson == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -51,8 +52,8 @@ Future<UserData?> updateUserdata(Function(UserData userData) updateUser) async {
|
|||
final user = await getUser();
|
||||
if (user == null) return null;
|
||||
UserData updated = updateUser(user);
|
||||
final storage = FlutterSecureStorage();
|
||||
storage.write(key: "userData", value: jsonEncode(updated));
|
||||
FlutterSecureStorage()
|
||||
.write(key: SecureStorageKeys.userData, value: jsonEncode(updated));
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
|
@ -62,7 +63,6 @@ Future<bool> deleteLocalUserData() async {
|
|||
if (appDir.existsSync()) {
|
||||
appDir.deleteSync(recursive: true);
|
||||
}
|
||||
final storage = FlutterSecureStorage();
|
||||
await storage.deleteAll();
|
||||
await FlutterSecureStorage().deleteAll();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
|
|
@ -32,8 +33,6 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
_isTryingToRegister = true;
|
||||
});
|
||||
|
||||
final storage = FlutterSecureStorage();
|
||||
|
||||
await createIfNotExistsSignalIdentity();
|
||||
|
||||
int userId = 0;
|
||||
|
|
@ -66,7 +65,10 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
subscriptionPlan: "Preview",
|
||||
isDemoUser: isDemoAccount,
|
||||
);
|
||||
storage.write(key: "userData", value: jsonEncode(userData));
|
||||
|
||||
FlutterSecureStorage()
|
||||
.write(key: SecureStorageKeys.userData, value: jsonEncode(userData));
|
||||
|
||||
if (!isDemoAccount) {
|
||||
await apiService.authenticate();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:twonly/src/services/backup.identitiy.service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/views/settings/backup/twonly_safe_backup.view.dart';
|
||||
|
||||
class BackupView extends StatefulWidget {
|
||||
|
|
@ -49,7 +50,11 @@ class _BackupViewState extends State<BackupView> {
|
|||
autoBackupEnabled: _twonlyIdBackupEnabled,
|
||||
onTap: () async {
|
||||
if (_twonlyIdBackupEnabled) {
|
||||
await disableTwonlySafe();
|
||||
bool disable = await showAlertDialog(context, "Are you sure?",
|
||||
"Without an backup, you can not restore your user account.");
|
||||
if (disable) {
|
||||
await disableTwonlySafe();
|
||||
}
|
||||
} else {
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) {
|
||||
|
|
@ -110,7 +115,7 @@ class BackupOption extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
onTap: (autoBackupEnabled) ? null : onTap,
|
||||
child: Card(
|
||||
margin: EdgeInsets.all(8.0),
|
||||
child: Padding(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/services/fcm.service.dart';
|
||||
import 'package:twonly/src/services/notification.service.dart';
|
||||
|
|
@ -26,8 +27,8 @@ class NotificationView extends StatelessWidget {
|
|||
subtitle: Text(context.lang.settingsNotifyTroubleshootingDesc),
|
||||
onTap: () async {
|
||||
await initFCMAfterAuthenticated();
|
||||
final storage = FlutterSecureStorage();
|
||||
String? storedToken = await storage.read(key: "google_fcm");
|
||||
String? storedToken = await FlutterSecureStorage()
|
||||
.read(key: SecureStorageKeys.googleFcm);
|
||||
await setupNotificationWithUsers(force: true);
|
||||
if (!context.mounted) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ packages:
|
|||
source: hosted
|
||||
version: "7.4.5"
|
||||
archive:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: archive
|
||||
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ dependencies:
|
|||
tutorial_coach_mark: ^1.3.0
|
||||
background_downloader: ^9.2.2
|
||||
hashlib: ^2.0.0
|
||||
archive: ^4.0.7
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in a new issue