mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-23 01:06:44 +00:00
chore: Merge pull request 'backups-rewrite' (#228) from backups-rewrite into master
Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/228 Reviewed-by: NaiJi ✨ <naiji@udongein.xyz>
This commit is contained in:
commit
8bc1121206
|
@ -162,6 +162,7 @@
|
|||
},
|
||||
"backup": {
|
||||
"card_title": "Backup",
|
||||
"card_subtitle": "Manage your backups",
|
||||
"description": "Will save your day in case of incident: hackers attack, server deletion, etc.",
|
||||
"reupload_key": "Force reupload key",
|
||||
"reuploaded_key": "Key reuploaded",
|
||||
|
@ -176,7 +177,27 @@
|
|||
"restore_alert": "You are about to restore from backup created on {}. All current data will be lost. Are you sure?",
|
||||
"refresh": "Refresh status",
|
||||
"refetch_backups": "Refetch backup list",
|
||||
"refetching_list": "In a few minutes list will be updated"
|
||||
"refetch_backups_subtitle": "Invalidate cache and refetch data from your storage provider. May cause additional charges.",
|
||||
"reupload_key_subtitle": "Will instruct the server to initialize backup storage again. Use if something is broken.",
|
||||
"refetching_list": "In a few minutes list will be updated",
|
||||
"select_all": "Backup everything",
|
||||
"create_new_select_heading": "Select what to backup",
|
||||
"start": "Start backup",
|
||||
"service_busy": "Another backup operation is in progress",
|
||||
"latest_snapshots": "Latest snapshots",
|
||||
"latest_snapshots_subtitle": "Showing last 15 snapshots",
|
||||
"show_more": "Show more",
|
||||
"autobackup_period_title": "Automatic backups period",
|
||||
"autobackup_period_subtitle": "Backups created every {period}",
|
||||
"autobackup_period_never": "Automatic backups are disabled",
|
||||
"autobackup_period_every": "Every {period}",
|
||||
"autobackup_period_disable": "Disable automatic backups",
|
||||
"autobackup_custom": "Custom",
|
||||
"autobackup_custom_hint": "Enter custom period in minutes",
|
||||
"autobackup_set_period": "Set period",
|
||||
"autobackup_period_set": "Period set",
|
||||
"pending_jobs": "Currently running backup jobs",
|
||||
"snapshots_title": "Snapshot list"
|
||||
},
|
||||
"storage": {
|
||||
"card_title": "Server Storage",
|
||||
|
@ -210,6 +231,7 @@
|
|||
"enable": "Enable service",
|
||||
"move": "Move to another volume",
|
||||
"uses": "Uses {usage} on {volume}",
|
||||
"snapshots": "Backup snapshots",
|
||||
"status": {
|
||||
"active": "Up and running",
|
||||
"inactive": "Stopped",
|
||||
|
@ -514,4 +536,4 @@
|
|||
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
|
||||
"cubit_statuses": "Cubit loading statuses"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:typed_data';
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||
|
@ -15,12 +15,13 @@ class HiveConfig {
|
|||
Hive.registerAdapter(UserAdapter());
|
||||
Hive.registerAdapter(ServerHostingDetailsAdapter());
|
||||
Hive.registerAdapter(ServerDomainAdapter());
|
||||
Hive.registerAdapter(BackblazeCredentialAdapter());
|
||||
Hive.registerAdapter(BackupsCredentialAdapter());
|
||||
Hive.registerAdapter(BackblazeBucketAdapter());
|
||||
Hive.registerAdapter(ServerVolumeAdapter());
|
||||
Hive.registerAdapter(UserTypeAdapter());
|
||||
Hive.registerAdapter(DnsProviderTypeAdapter());
|
||||
Hive.registerAdapter(ServerProviderTypeAdapter());
|
||||
Hive.registerAdapter(BackupsProviderTypeAdapter());
|
||||
|
||||
await Hive.openBox(BNames.appSettingsBox);
|
||||
|
||||
|
@ -110,7 +111,7 @@ class BNames {
|
|||
/// A [ServerHostingDetails] field of [serverInstallationBox] box.
|
||||
static String serverDetails = 'hetznerServer';
|
||||
|
||||
/// A [BackblazeCredential] field of [serverInstallationBox] box.
|
||||
/// A [BackupsCredential] field of [serverInstallationBox] box.
|
||||
static String backblazeCredential = 'backblazeKey';
|
||||
|
||||
/// A [BackblazeBucket] field of [serverInstallationBox] box.
|
||||
|
|
93
lib/logic/api_maps/graphql_maps/schema/backups.graphql
Normal file
93
lib/logic/api_maps/graphql_maps/schema/backups.graphql
Normal file
|
@ -0,0 +1,93 @@
|
|||
query BackupConfiguration {
|
||||
backup {
|
||||
configuration {
|
||||
autobackupPeriod
|
||||
encryptionKey
|
||||
isInitialized
|
||||
locationId
|
||||
locationName
|
||||
provider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query AllBackupSnapshots {
|
||||
backup {
|
||||
allSnapshots {
|
||||
id
|
||||
createdAt
|
||||
service {
|
||||
displayName
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment genericBackupConfigReturn on GenericBackupConfigReturn {
|
||||
code
|
||||
message
|
||||
success
|
||||
configuration {
|
||||
provider
|
||||
encryptionKey
|
||||
isInitialized
|
||||
autobackupPeriod
|
||||
locationName
|
||||
locationId
|
||||
}
|
||||
}
|
||||
|
||||
mutation ForceSnapshotsReload {
|
||||
backup {
|
||||
forceSnapshotsReload {
|
||||
...basicMutationReturnFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation StartBackup($serviceId: String!) {
|
||||
backup {
|
||||
startBackup(serviceId: $serviceId) {
|
||||
...basicMutationReturnFields
|
||||
job {
|
||||
...basicApiJobsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation SetAutobackupPeriod($period: Int = null) {
|
||||
backup {
|
||||
setAutobackupPeriod(period: $period) {
|
||||
...genericBackupConfigReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation RemoveRepository {
|
||||
backup {
|
||||
removeRepository {
|
||||
...genericBackupConfigReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation InitializeRepository($repository: InitializeRepositoryInput!) {
|
||||
backup {
|
||||
initializeRepository(repository: $repository) {
|
||||
...genericBackupConfigReturn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation RestoreBackup($snapshotId: String!) {
|
||||
backup {
|
||||
restoreBackup(snapshotId: $snapshotId) {
|
||||
...basicMutationReturnFields
|
||||
job {
|
||||
...basicApiJobsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5729
lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart
Normal file
5729
lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,3 @@
|
|||
fragment basicMutationReturnFields on MutationReturnInterface{
|
||||
code
|
||||
message
|
||||
success
|
||||
}
|
||||
|
||||
query GetServerDiskVolumes {
|
||||
storage {
|
||||
volumes {
|
||||
|
@ -53,17 +47,7 @@ mutation MigrateToBinds($input: MigrateToBindsInput!) {
|
|||
migrateToBinds(input: $input) {
|
||||
...basicMutationReturnFields
|
||||
job {
|
||||
createdAt
|
||||
description
|
||||
error
|
||||
finishedAt
|
||||
name
|
||||
progress
|
||||
result
|
||||
status
|
||||
statusText
|
||||
uid
|
||||
updatedAt
|
||||
...basicApiJobsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,54 +1,65 @@
|
|||
type Alert {
|
||||
message: String!
|
||||
severity: Severity!
|
||||
timestamp: DateTime
|
||||
title: String!
|
||||
message: String!
|
||||
timestamp: DateTime
|
||||
}
|
||||
|
||||
type Api {
|
||||
devices: [ApiDevice!]!
|
||||
recoveryKey: ApiRecoveryKeyStatus!
|
||||
version: String!
|
||||
recoveryKey: ApiRecoveryKeyStatus!
|
||||
devices: [ApiDevice!]!
|
||||
}
|
||||
|
||||
type ApiDevice {
|
||||
name: String!
|
||||
creationDate: DateTime!
|
||||
isCaller: Boolean!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type ApiJob {
|
||||
createdAt: DateTime!
|
||||
description: String!
|
||||
error: String
|
||||
finishedAt: DateTime
|
||||
uid: String!
|
||||
typeId: String!
|
||||
name: String!
|
||||
progress: Int
|
||||
result: String
|
||||
description: String!
|
||||
status: String!
|
||||
statusText: String
|
||||
uid: String!
|
||||
progress: Int
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
finishedAt: DateTime
|
||||
error: String
|
||||
result: String
|
||||
}
|
||||
|
||||
type ApiKeyMutationReturn implements MutationReturnInterface {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
key: String
|
||||
message: String!
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type ApiMutations {
|
||||
getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn!
|
||||
useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn!
|
||||
refreshDeviceApiToken: DeviceApiTokenMutationReturn!
|
||||
deleteDeviceApiToken(device: String!): GenericMutationReturn!
|
||||
getNewDeviceApiKey: ApiKeyMutationReturn!
|
||||
invalidateNewDeviceApiKey: GenericMutationReturn!
|
||||
authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn!
|
||||
}
|
||||
|
||||
type ApiRecoveryKeyStatus {
|
||||
creationDate: DateTime
|
||||
exists: Boolean!
|
||||
valid: Boolean!
|
||||
creationDate: DateTime
|
||||
expirationDate: DateTime
|
||||
usesLeft: Int
|
||||
valid: Boolean!
|
||||
}
|
||||
|
||||
type AutoUpgradeOptions {
|
||||
allowReboot: Boolean!
|
||||
enable: Boolean!
|
||||
allowReboot: Boolean!
|
||||
}
|
||||
|
||||
input AutoUpgradeSettingsInput {
|
||||
|
@ -57,53 +68,102 @@ input AutoUpgradeSettingsInput {
|
|||
}
|
||||
|
||||
type AutoUpgradeSettingsMutationReturn implements MutationReturnInterface {
|
||||
allowReboot: Boolean!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
enableAutoUpgrade: Boolean!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
allowReboot: Boolean!
|
||||
}
|
||||
|
||||
type Backup {
|
||||
configuration: BackupConfiguration!
|
||||
allSnapshots: [SnapshotInfo!]!
|
||||
}
|
||||
|
||||
type BackupConfiguration {
|
||||
provider: BackupProvider!
|
||||
encryptionKey: String!
|
||||
isInitialized: Boolean!
|
||||
autobackupPeriod: Int
|
||||
locationName: String
|
||||
locationId: String
|
||||
}
|
||||
|
||||
type BackupMutations {
|
||||
initializeRepository(repository: InitializeRepositoryInput!): GenericBackupConfigReturn!
|
||||
removeRepository: GenericBackupConfigReturn!
|
||||
setAutobackupPeriod(period: Int = null): GenericBackupConfigReturn!
|
||||
startBackup(serviceId: String!): GenericJobMutationReturn!
|
||||
restoreBackup(snapshotId: String!): GenericJobMutationReturn!
|
||||
forceSnapshotsReload: GenericMutationReturn!
|
||||
}
|
||||
|
||||
enum BackupProvider {
|
||||
BACKBLAZE
|
||||
NONE
|
||||
MEMORY
|
||||
FILE
|
||||
}
|
||||
|
||||
"""Date with time (isoformat)"""
|
||||
scalar DateTime
|
||||
|
||||
type DeviceApiTokenMutationReturn implements MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
token: String
|
||||
}
|
||||
|
||||
enum DnsProvider {
|
||||
CLOUDFLARE,
|
||||
DESEC,
|
||||
CLOUDFLARE
|
||||
DIGITALOCEAN
|
||||
DESEC
|
||||
}
|
||||
|
||||
type DnsRecord {
|
||||
content: String!
|
||||
name: String!
|
||||
priority: Int
|
||||
recordType: String!
|
||||
name: String!
|
||||
content: String!
|
||||
ttl: Int!
|
||||
priority: Int
|
||||
}
|
||||
|
||||
type GenericJobButationReturn implements MutationReturnInterface {
|
||||
type GenericBackupConfigReturn implements MutationReturnInterface {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
configuration: BackupConfiguration
|
||||
}
|
||||
|
||||
type GenericJobMutationReturn implements MutationReturnInterface {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
job: ApiJob
|
||||
message: String!
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type GenericMutationReturn implements MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
}
|
||||
|
||||
input InitializeRepositoryInput {
|
||||
provider: BackupProvider!
|
||||
locationId: String!
|
||||
locationName: String!
|
||||
login: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type Job {
|
||||
getJob(jobId: String!): ApiJob
|
||||
getJobs: [ApiJob!]!
|
||||
getJob(jobId: String!): ApiJob
|
||||
}
|
||||
|
||||
type JobMutations {
|
||||
removeJob(jobId: String!): GenericMutationReturn!
|
||||
}
|
||||
|
||||
input MigrateToBindsInput {
|
||||
|
@ -120,52 +180,60 @@ input MoveServiceInput {
|
|||
}
|
||||
|
||||
type Mutation {
|
||||
addSshKey(sshInput: SshMutationInput!): UserMutationReturn!
|
||||
authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn!
|
||||
changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn!
|
||||
changeTimezone(timezone: String!): TimezoneMutationReturn!
|
||||
createUser(user: UserMutationInput!): UserMutationReturn!
|
||||
deleteDeviceApiToken(device: String!): GenericMutationReturn!
|
||||
deleteUser(username: String!): GenericMutationReturn!
|
||||
disableService(serviceId: String!): ServiceMutationReturn!
|
||||
enableService(serviceId: String!): ServiceMutationReturn!
|
||||
getNewDeviceApiKey: ApiKeyMutationReturn!
|
||||
getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn!
|
||||
invalidateNewDeviceApiKey: GenericMutationReturn!
|
||||
migrateToBinds(input: MigrateToBindsInput!): GenericJobButationReturn!
|
||||
mountVolume(name: String!): GenericMutationReturn!
|
||||
moveService(input: MoveServiceInput!): ServiceJobMutationReturn!
|
||||
pullRepositoryChanges: GenericMutationReturn!
|
||||
rebootSystem: GenericMutationReturn!
|
||||
refreshDeviceApiToken: DeviceApiTokenMutationReturn!
|
||||
removeJob(jobId: String!): GenericMutationReturn!
|
||||
removeSshKey(sshInput: SshMutationInput!): UserMutationReturn!
|
||||
resizeVolume(name: String!): GenericMutationReturn!
|
||||
restartService(serviceId: String!): ServiceMutationReturn!
|
||||
runSystemRebuild: GenericMutationReturn!
|
||||
runSystemRollback: GenericMutationReturn!
|
||||
runSystemUpgrade: GenericMutationReturn!
|
||||
startService(serviceId: String!): ServiceMutationReturn!
|
||||
stopService(serviceId: String!): ServiceMutationReturn!
|
||||
getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn! @deprecated(reason: "Use `api.get_new_recovery_api_key` instead")
|
||||
useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.use_recovery_api_key` instead")
|
||||
refreshDeviceApiToken: DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.refresh_device_api_token` instead")
|
||||
deleteDeviceApiToken(device: String!): GenericMutationReturn! @deprecated(reason: "Use `api.delete_device_api_token` instead")
|
||||
getNewDeviceApiKey: ApiKeyMutationReturn! @deprecated(reason: "Use `api.get_new_device_api_key` instead")
|
||||
invalidateNewDeviceApiKey: GenericMutationReturn! @deprecated(reason: "Use `api.invalidate_new_device_api_key` instead")
|
||||
authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.authorize_with_new_device_api_key` instead")
|
||||
changeTimezone(timezone: String!): TimezoneMutationReturn! @deprecated(reason: "Use `system.change_timezone` instead")
|
||||
changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn! @deprecated(reason: "Use `system.change_auto_upgrade_settings` instead")
|
||||
runSystemRebuild: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_rebuild` instead")
|
||||
runSystemRollback: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_rollback` instead")
|
||||
runSystemUpgrade: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_upgrade` instead")
|
||||
rebootSystem: GenericMutationReturn! @deprecated(reason: "Use `system.reboot_system` instead")
|
||||
pullRepositoryChanges: GenericMutationReturn! @deprecated(reason: "Use `system.pull_repository_changes` instead")
|
||||
createUser(user: UserMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.create_user` instead")
|
||||
deleteUser(username: String!): GenericMutationReturn! @deprecated(reason: "Use `users.delete_user` instead")
|
||||
updateUser(user: UserMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.update_user` instead")
|
||||
addSshKey(sshInput: SshMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.add_ssh_key` instead")
|
||||
removeSshKey(sshInput: SshMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.remove_ssh_key` instead")
|
||||
resizeVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.resize_volume` instead")
|
||||
mountVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.mount_volume` instead")
|
||||
unmountVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.unmount_volume` instead")
|
||||
migrateToBinds(input: MigrateToBindsInput!): GenericJobMutationReturn! @deprecated(reason: "Use `storage.migrate_to_binds` instead")
|
||||
enableService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.enable_service` instead")
|
||||
disableService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.disable_service` instead")
|
||||
stopService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.stop_service` instead")
|
||||
startService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.start_service` instead")
|
||||
restartService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.restart_service` instead")
|
||||
moveService(input: MoveServiceInput!): ServiceJobMutationReturn! @deprecated(reason: "Use `services.move_service` instead")
|
||||
removeJob(jobId: String!): GenericMutationReturn! @deprecated(reason: "Use `jobs.remove_job` instead")
|
||||
api: ApiMutations!
|
||||
system: SystemMutations!
|
||||
users: UsersMutations!
|
||||
storage: StorageMutations!
|
||||
services: ServicesMutations!
|
||||
jobs: JobMutations!
|
||||
backup: BackupMutations!
|
||||
testMutation: GenericMutationReturn!
|
||||
unmountVolume(name: String!): GenericMutationReturn!
|
||||
updateUser(user: UserMutationInput!): UserMutationReturn!
|
||||
useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn!
|
||||
}
|
||||
|
||||
interface MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
}
|
||||
|
||||
type Query {
|
||||
api: Api!
|
||||
jobs: Job!
|
||||
services: Services!
|
||||
storage: Storage!
|
||||
system: System!
|
||||
users: Users!
|
||||
storage: Storage!
|
||||
jobs: Job!
|
||||
services: Services!
|
||||
backup: Backup!
|
||||
}
|
||||
|
||||
input RecoveryKeyLimitsInput {
|
||||
|
@ -179,61 +247,79 @@ enum ServerProvider {
|
|||
}
|
||||
|
||||
type Service {
|
||||
description: String!
|
||||
displayName: String!
|
||||
dnsRecords: [DnsRecord!]
|
||||
id: String!
|
||||
isEnabled: Boolean!
|
||||
displayName: String!
|
||||
description: String!
|
||||
svgIcon: String!
|
||||
isMovable: Boolean!
|
||||
isRequired: Boolean!
|
||||
isEnabled: Boolean!
|
||||
canBeBackedUp: Boolean!
|
||||
backupDescription: String!
|
||||
status: ServiceStatusEnum!
|
||||
storageUsage: ServiceStorageUsage!
|
||||
svgIcon: String!
|
||||
url: String
|
||||
dnsRecords: [DnsRecord!]
|
||||
storageUsage: ServiceStorageUsage!
|
||||
backupSnapshots: [SnapshotInfo!]
|
||||
}
|
||||
|
||||
type ServiceJobMutationReturn implements MutationReturnInterface {
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
job: ApiJob
|
||||
message: String!
|
||||
service: Service
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type ServiceMutationReturn implements MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
service: Service
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
service: Service
|
||||
}
|
||||
|
||||
enum ServiceStatusEnum {
|
||||
ACTIVATING
|
||||
ACTIVE
|
||||
DEACTIVATING
|
||||
FAILED
|
||||
INACTIVE
|
||||
OFF
|
||||
RELOADING
|
||||
INACTIVE
|
||||
FAILED
|
||||
ACTIVATING
|
||||
DEACTIVATING
|
||||
OFF
|
||||
}
|
||||
|
||||
type ServiceStorageUsage implements StorageUsageInterface {
|
||||
service: Service
|
||||
title: String!
|
||||
usedSpace: String!
|
||||
volume: StorageVolume
|
||||
title: String!
|
||||
service: Service
|
||||
}
|
||||
|
||||
type Services {
|
||||
allServices: [Service!]!
|
||||
}
|
||||
|
||||
type ServicesMutations {
|
||||
enableService(serviceId: String!): ServiceMutationReturn!
|
||||
disableService(serviceId: String!): ServiceMutationReturn!
|
||||
stopService(serviceId: String!): ServiceMutationReturn!
|
||||
startService(serviceId: String!): ServiceMutationReturn!
|
||||
restartService(serviceId: String!): ServiceMutationReturn!
|
||||
moveService(input: MoveServiceInput!): ServiceJobMutationReturn!
|
||||
}
|
||||
|
||||
enum Severity {
|
||||
CRITICAL
|
||||
ERROR
|
||||
INFO
|
||||
SUCCESS
|
||||
WARNING
|
||||
ERROR
|
||||
CRITICAL
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
type SnapshotInfo {
|
||||
id: String!
|
||||
service: Service!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
|
||||
input SshMutationInput {
|
||||
|
@ -251,22 +337,29 @@ type Storage {
|
|||
volumes: [StorageVolume!]!
|
||||
}
|
||||
|
||||
type StorageMutations {
|
||||
resizeVolume(name: String!): GenericMutationReturn!
|
||||
mountVolume(name: String!): GenericMutationReturn!
|
||||
unmountVolume(name: String!): GenericMutationReturn!
|
||||
migrateToBinds(input: MigrateToBindsInput!): GenericJobMutationReturn!
|
||||
}
|
||||
|
||||
interface StorageUsageInterface {
|
||||
title: String!
|
||||
usedSpace: String!
|
||||
volume: StorageVolume
|
||||
title: String!
|
||||
}
|
||||
|
||||
type StorageVolume {
|
||||
freeSpace: String!
|
||||
model: String
|
||||
name: String!
|
||||
root: Boolean!
|
||||
serial: String
|
||||
totalSpace: String!
|
||||
freeSpace: String!
|
||||
usedSpace: String!
|
||||
root: Boolean!
|
||||
name: String!
|
||||
model: String
|
||||
serial: String
|
||||
type: String!
|
||||
usages: [StorageUsageInterface!]!
|
||||
usedSpace: String!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
|
@ -274,12 +367,12 @@ type Subscription {
|
|||
}
|
||||
|
||||
type System {
|
||||
busy: Boolean!
|
||||
status: Alert!
|
||||
domainInfo: SystemDomainInfo!
|
||||
settings: SystemSettings!
|
||||
info: SystemInfo!
|
||||
provider: SystemProviderInfo!
|
||||
settings: SystemSettings!
|
||||
status: Alert!
|
||||
busy: Boolean!
|
||||
workingDirectory: String!
|
||||
}
|
||||
|
||||
|
@ -291,14 +384,24 @@ type SystemDomainInfo {
|
|||
}
|
||||
|
||||
type SystemInfo {
|
||||
pythonVersion: String!
|
||||
systemVersion: String!
|
||||
pythonVersion: String!
|
||||
usingBinds: Boolean!
|
||||
}
|
||||
|
||||
type SystemMutations {
|
||||
changeTimezone(timezone: String!): TimezoneMutationReturn!
|
||||
changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn!
|
||||
runSystemRebuild: GenericMutationReturn!
|
||||
runSystemRollback: GenericMutationReturn!
|
||||
runSystemUpgrade: GenericMutationReturn!
|
||||
rebootSystem: GenericMutationReturn!
|
||||
pullRepositoryChanges: GenericMutationReturn!
|
||||
}
|
||||
|
||||
type SystemProviderInfo {
|
||||
id: String!
|
||||
provider: ServerProvider!
|
||||
id: String!
|
||||
}
|
||||
|
||||
type SystemSettings {
|
||||
|
@ -308,9 +411,9 @@ type SystemSettings {
|
|||
}
|
||||
|
||||
type TimezoneMutationReturn implements MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
timezone: String
|
||||
}
|
||||
|
||||
|
@ -325,9 +428,9 @@ input UseRecoveryKeyInput {
|
|||
}
|
||||
|
||||
type User {
|
||||
sshKeys: [String!]!
|
||||
userType: UserType!
|
||||
username: String!
|
||||
sshKeys: [String!]!
|
||||
}
|
||||
|
||||
input UserMutationInput {
|
||||
|
@ -336,9 +439,9 @@ input UserMutationInput {
|
|||
}
|
||||
|
||||
type UserMutationReturn implements MutationReturnInterface {
|
||||
code: Int!
|
||||
message: String!
|
||||
success: Boolean!
|
||||
message: String!
|
||||
code: Int!
|
||||
user: User
|
||||
}
|
||||
|
||||
|
@ -353,10 +456,10 @@ type Users {
|
|||
getUser(username: String!): User
|
||||
}
|
||||
|
||||
fragment dnsRecordFields on DnsRecord {
|
||||
content
|
||||
name
|
||||
priority
|
||||
recordType
|
||||
ttl
|
||||
}
|
||||
type UsersMutations {
|
||||
createUser(user: UserMutationInput!): UserMutationReturn!
|
||||
deleteUser(username: String!): GenericMutationReturn!
|
||||
updateUser(user: UserMutationInput!): UserMutationReturn!
|
||||
addSshKey(sshInput: SshMutationInput!): UserMutationReturn!
|
||||
removeSshKey(sshInput: SshMutationInput!): UserMutationReturn!
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'package:gql/ast.dart';
|
||||
import 'package:graphql/client.dart' as graphql;
|
||||
import 'package:selfprivacy/utils/scalars.dart';
|
||||
|
||||
class Input$AutoUpgradeSettingsInput {
|
||||
|
@ -143,6 +141,190 @@ class _CopyWithStubImpl$Input$AutoUpgradeSettingsInput<TRes>
|
|||
_res;
|
||||
}
|
||||
|
||||
class Input$InitializeRepositoryInput {
|
||||
factory Input$InitializeRepositoryInput({
|
||||
required Enum$BackupProvider provider,
|
||||
required String locationId,
|
||||
required String locationName,
|
||||
required String login,
|
||||
required String password,
|
||||
}) =>
|
||||
Input$InitializeRepositoryInput._({
|
||||
r'provider': provider,
|
||||
r'locationId': locationId,
|
||||
r'locationName': locationName,
|
||||
r'login': login,
|
||||
r'password': password,
|
||||
});
|
||||
|
||||
Input$InitializeRepositoryInput._(this._$data);
|
||||
|
||||
factory Input$InitializeRepositoryInput.fromJson(Map<String, dynamic> data) {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$provider = data['provider'];
|
||||
result$data['provider'] =
|
||||
fromJson$Enum$BackupProvider((l$provider as String));
|
||||
final l$locationId = data['locationId'];
|
||||
result$data['locationId'] = (l$locationId as String);
|
||||
final l$locationName = data['locationName'];
|
||||
result$data['locationName'] = (l$locationName as String);
|
||||
final l$login = data['login'];
|
||||
result$data['login'] = (l$login as String);
|
||||
final l$password = data['password'];
|
||||
result$data['password'] = (l$password as String);
|
||||
return Input$InitializeRepositoryInput._(result$data);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$data;
|
||||
|
||||
Enum$BackupProvider get provider =>
|
||||
(_$data['provider'] as Enum$BackupProvider);
|
||||
String get locationId => (_$data['locationId'] as String);
|
||||
String get locationName => (_$data['locationName'] as String);
|
||||
String get login => (_$data['login'] as String);
|
||||
String get password => (_$data['password'] as String);
|
||||
Map<String, dynamic> toJson() {
|
||||
final result$data = <String, dynamic>{};
|
||||
final l$provider = provider;
|
||||
result$data['provider'] = toJson$Enum$BackupProvider(l$provider);
|
||||
final l$locationId = locationId;
|
||||
result$data['locationId'] = l$locationId;
|
||||
final l$locationName = locationName;
|
||||
result$data['locationName'] = l$locationName;
|
||||
final l$login = login;
|
||||
result$data['login'] = l$login;
|
||||
final l$password = password;
|
||||
result$data['password'] = l$password;
|
||||
return result$data;
|
||||
}
|
||||
|
||||
CopyWith$Input$InitializeRepositoryInput<Input$InitializeRepositoryInput>
|
||||
get copyWith => CopyWith$Input$InitializeRepositoryInput(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Input$InitializeRepositoryInput) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$provider = provider;
|
||||
final lOther$provider = other.provider;
|
||||
if (l$provider != lOther$provider) {
|
||||
return false;
|
||||
}
|
||||
final l$locationId = locationId;
|
||||
final lOther$locationId = other.locationId;
|
||||
if (l$locationId != lOther$locationId) {
|
||||
return false;
|
||||
}
|
||||
final l$locationName = locationName;
|
||||
final lOther$locationName = other.locationName;
|
||||
if (l$locationName != lOther$locationName) {
|
||||
return false;
|
||||
}
|
||||
final l$login = login;
|
||||
final lOther$login = other.login;
|
||||
if (l$login != lOther$login) {
|
||||
return false;
|
||||
}
|
||||
final l$password = password;
|
||||
final lOther$password = other.password;
|
||||
if (l$password != lOther$password) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$provider = provider;
|
||||
final l$locationId = locationId;
|
||||
final l$locationName = locationName;
|
||||
final l$login = login;
|
||||
final l$password = password;
|
||||
return Object.hashAll([
|
||||
l$provider,
|
||||
l$locationId,
|
||||
l$locationName,
|
||||
l$login,
|
||||
l$password,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CopyWith$Input$InitializeRepositoryInput<TRes> {
|
||||
factory CopyWith$Input$InitializeRepositoryInput(
|
||||
Input$InitializeRepositoryInput instance,
|
||||
TRes Function(Input$InitializeRepositoryInput) then,
|
||||
) = _CopyWithImpl$Input$InitializeRepositoryInput;
|
||||
|
||||
factory CopyWith$Input$InitializeRepositoryInput.stub(TRes res) =
|
||||
_CopyWithStubImpl$Input$InitializeRepositoryInput;
|
||||
|
||||
TRes call({
|
||||
Enum$BackupProvider? provider,
|
||||
String? locationId,
|
||||
String? locationName,
|
||||
String? login,
|
||||
String? password,
|
||||
});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Input$InitializeRepositoryInput<TRes>
|
||||
implements CopyWith$Input$InitializeRepositoryInput<TRes> {
|
||||
_CopyWithImpl$Input$InitializeRepositoryInput(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Input$InitializeRepositoryInput _instance;
|
||||
|
||||
final TRes Function(Input$InitializeRepositoryInput) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? provider = _undefined,
|
||||
Object? locationId = _undefined,
|
||||
Object? locationName = _undefined,
|
||||
Object? login = _undefined,
|
||||
Object? password = _undefined,
|
||||
}) =>
|
||||
_then(Input$InitializeRepositoryInput._({
|
||||
..._instance._$data,
|
||||
if (provider != _undefined && provider != null)
|
||||
'provider': (provider as Enum$BackupProvider),
|
||||
if (locationId != _undefined && locationId != null)
|
||||
'locationId': (locationId as String),
|
||||
if (locationName != _undefined && locationName != null)
|
||||
'locationName': (locationName as String),
|
||||
if (login != _undefined && login != null) 'login': (login as String),
|
||||
if (password != _undefined && password != null)
|
||||
'password': (password as String),
|
||||
}));
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Input$InitializeRepositoryInput<TRes>
|
||||
implements CopyWith$Input$InitializeRepositoryInput<TRes> {
|
||||
_CopyWithStubImpl$Input$InitializeRepositoryInput(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
Enum$BackupProvider? provider,
|
||||
String? locationId,
|
||||
String? locationName,
|
||||
String? login,
|
||||
String? password,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
||||
class Input$MigrateToBindsInput {
|
||||
factory Input$MigrateToBindsInput({
|
||||
required String emailBlockDevice,
|
||||
|
@ -1096,16 +1278,48 @@ class _CopyWithStubImpl$Input$UserMutationInput<TRes>
|
|||
_res;
|
||||
}
|
||||
|
||||
enum Enum$DnsProvider { CLOUDFLARE, DESEC, DIGITALOCEAN, $unknown }
|
||||
enum Enum$BackupProvider { BACKBLAZE, NONE, MEMORY, FILE, $unknown }
|
||||
|
||||
String toJson$Enum$BackupProvider(Enum$BackupProvider e) {
|
||||
switch (e) {
|
||||
case Enum$BackupProvider.BACKBLAZE:
|
||||
return r'BACKBLAZE';
|
||||
case Enum$BackupProvider.NONE:
|
||||
return r'NONE';
|
||||
case Enum$BackupProvider.MEMORY:
|
||||
return r'MEMORY';
|
||||
case Enum$BackupProvider.FILE:
|
||||
return r'FILE';
|
||||
case Enum$BackupProvider.$unknown:
|
||||
return r'$unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Enum$BackupProvider fromJson$Enum$BackupProvider(String value) {
|
||||
switch (value) {
|
||||
case r'BACKBLAZE':
|
||||
return Enum$BackupProvider.BACKBLAZE;
|
||||
case r'NONE':
|
||||
return Enum$BackupProvider.NONE;
|
||||
case r'MEMORY':
|
||||
return Enum$BackupProvider.MEMORY;
|
||||
case r'FILE':
|
||||
return Enum$BackupProvider.FILE;
|
||||
default:
|
||||
return Enum$BackupProvider.$unknown;
|
||||
}
|
||||
}
|
||||
|
||||
enum Enum$DnsProvider { CLOUDFLARE, DIGITALOCEAN, DESEC, $unknown }
|
||||
|
||||
String toJson$Enum$DnsProvider(Enum$DnsProvider e) {
|
||||
switch (e) {
|
||||
case Enum$DnsProvider.CLOUDFLARE:
|
||||
return r'CLOUDFLARE';
|
||||
case Enum$DnsProvider.DESEC:
|
||||
return r'DESEC';
|
||||
case Enum$DnsProvider.DIGITALOCEAN:
|
||||
return r'DIGITALOCEAN';
|
||||
case Enum$DnsProvider.DESEC:
|
||||
return r'DESEC';
|
||||
case Enum$DnsProvider.$unknown:
|
||||
return r'$unknown';
|
||||
}
|
||||
|
@ -1115,10 +1329,10 @@ Enum$DnsProvider fromJson$Enum$DnsProvider(String value) {
|
|||
switch (value) {
|
||||
case r'CLOUDFLARE':
|
||||
return Enum$DnsProvider.CLOUDFLARE;
|
||||
case r'DESEC':
|
||||
return Enum$DnsProvider.DESEC;
|
||||
case r'DIGITALOCEAN':
|
||||
return Enum$DnsProvider.DIGITALOCEAN;
|
||||
case r'DESEC':
|
||||
return Enum$DnsProvider.DESEC;
|
||||
default:
|
||||
return Enum$DnsProvider.$unknown;
|
||||
}
|
||||
|
@ -1149,32 +1363,32 @@ Enum$ServerProvider fromJson$Enum$ServerProvider(String value) {
|
|||
}
|
||||
|
||||
enum Enum$ServiceStatusEnum {
|
||||
ACTIVATING,
|
||||
ACTIVE,
|
||||
DEACTIVATING,
|
||||
FAILED,
|
||||
INACTIVE,
|
||||
OFF,
|
||||
RELOADING,
|
||||
INACTIVE,
|
||||
FAILED,
|
||||
ACTIVATING,
|
||||
DEACTIVATING,
|
||||
OFF,
|
||||
$unknown
|
||||
}
|
||||
|
||||
String toJson$Enum$ServiceStatusEnum(Enum$ServiceStatusEnum e) {
|
||||
switch (e) {
|
||||
case Enum$ServiceStatusEnum.ACTIVATING:
|
||||
return r'ACTIVATING';
|
||||
case Enum$ServiceStatusEnum.ACTIVE:
|
||||
return r'ACTIVE';
|
||||
case Enum$ServiceStatusEnum.DEACTIVATING:
|
||||
return r'DEACTIVATING';
|
||||
case Enum$ServiceStatusEnum.FAILED:
|
||||
return r'FAILED';
|
||||
case Enum$ServiceStatusEnum.INACTIVE:
|
||||
return r'INACTIVE';
|
||||
case Enum$ServiceStatusEnum.OFF:
|
||||
return r'OFF';
|
||||
case Enum$ServiceStatusEnum.RELOADING:
|
||||
return r'RELOADING';
|
||||
case Enum$ServiceStatusEnum.INACTIVE:
|
||||
return r'INACTIVE';
|
||||
case Enum$ServiceStatusEnum.FAILED:
|
||||
return r'FAILED';
|
||||
case Enum$ServiceStatusEnum.ACTIVATING:
|
||||
return r'ACTIVATING';
|
||||
case Enum$ServiceStatusEnum.DEACTIVATING:
|
||||
return r'DEACTIVATING';
|
||||
case Enum$ServiceStatusEnum.OFF:
|
||||
return r'OFF';
|
||||
case Enum$ServiceStatusEnum.$unknown:
|
||||
return r'$unknown';
|
||||
}
|
||||
|
@ -1182,39 +1396,39 @@ String toJson$Enum$ServiceStatusEnum(Enum$ServiceStatusEnum e) {
|
|||
|
||||
Enum$ServiceStatusEnum fromJson$Enum$ServiceStatusEnum(String value) {
|
||||
switch (value) {
|
||||
case r'ACTIVATING':
|
||||
return Enum$ServiceStatusEnum.ACTIVATING;
|
||||
case r'ACTIVE':
|
||||
return Enum$ServiceStatusEnum.ACTIVE;
|
||||
case r'DEACTIVATING':
|
||||
return Enum$ServiceStatusEnum.DEACTIVATING;
|
||||
case r'FAILED':
|
||||
return Enum$ServiceStatusEnum.FAILED;
|
||||
case r'INACTIVE':
|
||||
return Enum$ServiceStatusEnum.INACTIVE;
|
||||
case r'OFF':
|
||||
return Enum$ServiceStatusEnum.OFF;
|
||||
case r'RELOADING':
|
||||
return Enum$ServiceStatusEnum.RELOADING;
|
||||
case r'INACTIVE':
|
||||
return Enum$ServiceStatusEnum.INACTIVE;
|
||||
case r'FAILED':
|
||||
return Enum$ServiceStatusEnum.FAILED;
|
||||
case r'ACTIVATING':
|
||||
return Enum$ServiceStatusEnum.ACTIVATING;
|
||||
case r'DEACTIVATING':
|
||||
return Enum$ServiceStatusEnum.DEACTIVATING;
|
||||
case r'OFF':
|
||||
return Enum$ServiceStatusEnum.OFF;
|
||||
default:
|
||||
return Enum$ServiceStatusEnum.$unknown;
|
||||
}
|
||||
}
|
||||
|
||||
enum Enum$Severity { CRITICAL, ERROR, INFO, SUCCESS, WARNING, $unknown }
|
||||
enum Enum$Severity { INFO, WARNING, ERROR, CRITICAL, SUCCESS, $unknown }
|
||||
|
||||
String toJson$Enum$Severity(Enum$Severity e) {
|
||||
switch (e) {
|
||||
case Enum$Severity.CRITICAL:
|
||||
return r'CRITICAL';
|
||||
case Enum$Severity.ERROR:
|
||||
return r'ERROR';
|
||||
case Enum$Severity.INFO:
|
||||
return r'INFO';
|
||||
case Enum$Severity.SUCCESS:
|
||||
return r'SUCCESS';
|
||||
case Enum$Severity.WARNING:
|
||||
return r'WARNING';
|
||||
case Enum$Severity.ERROR:
|
||||
return r'ERROR';
|
||||
case Enum$Severity.CRITICAL:
|
||||
return r'CRITICAL';
|
||||
case Enum$Severity.SUCCESS:
|
||||
return r'SUCCESS';
|
||||
case Enum$Severity.$unknown:
|
||||
return r'$unknown';
|
||||
}
|
||||
|
@ -1222,16 +1436,16 @@ String toJson$Enum$Severity(Enum$Severity e) {
|
|||
|
||||
Enum$Severity fromJson$Enum$Severity(String value) {
|
||||
switch (value) {
|
||||
case r'CRITICAL':
|
||||
return Enum$Severity.CRITICAL;
|
||||
case r'ERROR':
|
||||
return Enum$Severity.ERROR;
|
||||
case r'INFO':
|
||||
return Enum$Severity.INFO;
|
||||
case r'SUCCESS':
|
||||
return Enum$Severity.SUCCESS;
|
||||
case r'WARNING':
|
||||
return Enum$Severity.WARNING;
|
||||
case r'ERROR':
|
||||
return Enum$Severity.ERROR;
|
||||
case r'CRITICAL':
|
||||
return Enum$Severity.CRITICAL;
|
||||
case r'SUCCESS':
|
||||
return Enum$Severity.SUCCESS;
|
||||
default:
|
||||
return Enum$Severity.$unknown;
|
||||
}
|
||||
|
@ -1265,306 +1479,13 @@ Enum$UserType fromJson$Enum$UserType(String value) {
|
|||
}
|
||||
}
|
||||
|
||||
class Fragment$dnsRecordFields {
|
||||
Fragment$dnsRecordFields({
|
||||
required this.content,
|
||||
required this.name,
|
||||
this.priority,
|
||||
required this.recordType,
|
||||
required this.ttl,
|
||||
this.$__typename = 'DnsRecord',
|
||||
});
|
||||
|
||||
factory Fragment$dnsRecordFields.fromJson(Map<String, dynamic> json) {
|
||||
final l$content = json['content'];
|
||||
final l$name = json['name'];
|
||||
final l$priority = json['priority'];
|
||||
final l$recordType = json['recordType'];
|
||||
final l$ttl = json['ttl'];
|
||||
final l$$__typename = json['__typename'];
|
||||
return Fragment$dnsRecordFields(
|
||||
content: (l$content as String),
|
||||
name: (l$name as String),
|
||||
priority: (l$priority as int?),
|
||||
recordType: (l$recordType as String),
|
||||
ttl: (l$ttl as int),
|
||||
$__typename: (l$$__typename as String),
|
||||
);
|
||||
}
|
||||
|
||||
final String content;
|
||||
|
||||
final String name;
|
||||
|
||||
final int? priority;
|
||||
|
||||
final String recordType;
|
||||
|
||||
final int ttl;
|
||||
|
||||
final String $__typename;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final _resultData = <String, dynamic>{};
|
||||
final l$content = content;
|
||||
_resultData['content'] = l$content;
|
||||
final l$name = name;
|
||||
_resultData['name'] = l$name;
|
||||
final l$priority = priority;
|
||||
_resultData['priority'] = l$priority;
|
||||
final l$recordType = recordType;
|
||||
_resultData['recordType'] = l$recordType;
|
||||
final l$ttl = ttl;
|
||||
_resultData['ttl'] = l$ttl;
|
||||
final l$$__typename = $__typename;
|
||||
_resultData['__typename'] = l$$__typename;
|
||||
return _resultData;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
final l$content = content;
|
||||
final l$name = name;
|
||||
final l$priority = priority;
|
||||
final l$recordType = recordType;
|
||||
final l$ttl = ttl;
|
||||
final l$$__typename = $__typename;
|
||||
return Object.hashAll([
|
||||
l$content,
|
||||
l$name,
|
||||
l$priority,
|
||||
l$recordType,
|
||||
l$ttl,
|
||||
l$$__typename,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (!(other is Fragment$dnsRecordFields) ||
|
||||
runtimeType != other.runtimeType) {
|
||||
return false;
|
||||
}
|
||||
final l$content = content;
|
||||
final lOther$content = other.content;
|
||||
if (l$content != lOther$content) {
|
||||
return false;
|
||||
}
|
||||
final l$name = name;
|
||||
final lOther$name = other.name;
|
||||
if (l$name != lOther$name) {
|
||||
return false;
|
||||
}
|
||||
final l$priority = priority;
|
||||
final lOther$priority = other.priority;
|
||||
if (l$priority != lOther$priority) {
|
||||
return false;
|
||||
}
|
||||
final l$recordType = recordType;
|
||||
final lOther$recordType = other.recordType;
|
||||
if (l$recordType != lOther$recordType) {
|
||||
return false;
|
||||
}
|
||||
final l$ttl = ttl;
|
||||
final lOther$ttl = other.ttl;
|
||||
if (l$ttl != lOther$ttl) {
|
||||
return false;
|
||||
}
|
||||
final l$$__typename = $__typename;
|
||||
final lOther$$__typename = other.$__typename;
|
||||
if (l$$__typename != lOther$$__typename) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UtilityExtension$Fragment$dnsRecordFields
|
||||
on Fragment$dnsRecordFields {
|
||||
CopyWith$Fragment$dnsRecordFields<Fragment$dnsRecordFields> get copyWith =>
|
||||
CopyWith$Fragment$dnsRecordFields(
|
||||
this,
|
||||
(i) => i,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class CopyWith$Fragment$dnsRecordFields<TRes> {
|
||||
factory CopyWith$Fragment$dnsRecordFields(
|
||||
Fragment$dnsRecordFields instance,
|
||||
TRes Function(Fragment$dnsRecordFields) then,
|
||||
) = _CopyWithImpl$Fragment$dnsRecordFields;
|
||||
|
||||
factory CopyWith$Fragment$dnsRecordFields.stub(TRes res) =
|
||||
_CopyWithStubImpl$Fragment$dnsRecordFields;
|
||||
|
||||
TRes call({
|
||||
String? content,
|
||||
String? name,
|
||||
int? priority,
|
||||
String? recordType,
|
||||
int? ttl,
|
||||
String? $__typename,
|
||||
});
|
||||
}
|
||||
|
||||
class _CopyWithImpl$Fragment$dnsRecordFields<TRes>
|
||||
implements CopyWith$Fragment$dnsRecordFields<TRes> {
|
||||
_CopyWithImpl$Fragment$dnsRecordFields(
|
||||
this._instance,
|
||||
this._then,
|
||||
);
|
||||
|
||||
final Fragment$dnsRecordFields _instance;
|
||||
|
||||
final TRes Function(Fragment$dnsRecordFields) _then;
|
||||
|
||||
static const _undefined = <dynamic, dynamic>{};
|
||||
|
||||
TRes call({
|
||||
Object? content = _undefined,
|
||||
Object? name = _undefined,
|
||||
Object? priority = _undefined,
|
||||
Object? recordType = _undefined,
|
||||
Object? ttl = _undefined,
|
||||
Object? $__typename = _undefined,
|
||||
}) =>
|
||||
_then(Fragment$dnsRecordFields(
|
||||
content: content == _undefined || content == null
|
||||
? _instance.content
|
||||
: (content as String),
|
||||
name: name == _undefined || name == null
|
||||
? _instance.name
|
||||
: (name as String),
|
||||
priority:
|
||||
priority == _undefined ? _instance.priority : (priority as int?),
|
||||
recordType: recordType == _undefined || recordType == null
|
||||
? _instance.recordType
|
||||
: (recordType as String),
|
||||
ttl: ttl == _undefined || ttl == null ? _instance.ttl : (ttl as int),
|
||||
$__typename: $__typename == _undefined || $__typename == null
|
||||
? _instance.$__typename
|
||||
: ($__typename as String),
|
||||
));
|
||||
}
|
||||
|
||||
class _CopyWithStubImpl$Fragment$dnsRecordFields<TRes>
|
||||
implements CopyWith$Fragment$dnsRecordFields<TRes> {
|
||||
_CopyWithStubImpl$Fragment$dnsRecordFields(this._res);
|
||||
|
||||
TRes _res;
|
||||
|
||||
call({
|
||||
String? content,
|
||||
String? name,
|
||||
int? priority,
|
||||
String? recordType,
|
||||
int? ttl,
|
||||
String? $__typename,
|
||||
}) =>
|
||||
_res;
|
||||
}
|
||||
|
||||
const fragmentDefinitiondnsRecordFields = FragmentDefinitionNode(
|
||||
name: NameNode(value: 'dnsRecordFields'),
|
||||
typeCondition: TypeConditionNode(
|
||||
on: NamedTypeNode(
|
||||
name: NameNode(value: 'DnsRecord'),
|
||||
isNonNull: false,
|
||||
)),
|
||||
directives: [],
|
||||
selectionSet: SelectionSetNode(selections: [
|
||||
FieldNode(
|
||||
name: NameNode(value: 'content'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: 'name'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: 'priority'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: 'recordType'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: 'ttl'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
FieldNode(
|
||||
name: NameNode(value: '__typename'),
|
||||
alias: null,
|
||||
arguments: [],
|
||||
directives: [],
|
||||
selectionSet: null,
|
||||
),
|
||||
]),
|
||||
);
|
||||
const documentNodeFragmentdnsRecordFields = DocumentNode(definitions: [
|
||||
fragmentDefinitiondnsRecordFields,
|
||||
]);
|
||||
|
||||
extension ClientExtension$Fragment$dnsRecordFields on graphql.GraphQLClient {
|
||||
void writeFragment$dnsRecordFields({
|
||||
required Fragment$dnsRecordFields data,
|
||||
required Map<String, dynamic> idFields,
|
||||
bool broadcast = true,
|
||||
}) =>
|
||||
this.writeFragment(
|
||||
graphql.FragmentRequest(
|
||||
idFields: idFields,
|
||||
fragment: const graphql.Fragment(
|
||||
fragmentName: 'dnsRecordFields',
|
||||
document: documentNodeFragmentdnsRecordFields,
|
||||
),
|
||||
),
|
||||
data: data.toJson(),
|
||||
broadcast: broadcast,
|
||||
);
|
||||
Fragment$dnsRecordFields? readFragment$dnsRecordFields({
|
||||
required Map<String, dynamic> idFields,
|
||||
bool optimistic = true,
|
||||
}) {
|
||||
final result = this.readFragment(
|
||||
graphql.FragmentRequest(
|
||||
idFields: idFields,
|
||||
fragment: const graphql.Fragment(
|
||||
fragmentName: 'dnsRecordFields',
|
||||
document: documentNodeFragmentdnsRecordFields,
|
||||
),
|
||||
),
|
||||
optimistic: optimistic,
|
||||
);
|
||||
return result == null ? null : Fragment$dnsRecordFields.fromJson(result);
|
||||
}
|
||||
}
|
||||
|
||||
const possibleTypesMap = <String, Set<String>>{
|
||||
'MutationReturnInterface': {
|
||||
'ApiKeyMutationReturn',
|
||||
'AutoUpgradeSettingsMutationReturn',
|
||||
'DeviceApiTokenMutationReturn',
|
||||
'GenericJobButationReturn',
|
||||
'GenericBackupConfigReturn',
|
||||
'GenericJobMutationReturn',
|
||||
'GenericMutationReturn',
|
||||
'ServiceJobMutationReturn',
|
||||
'ServiceMutationReturn',
|
||||
|
|
|
@ -4,6 +4,21 @@ fragment basicMutationReturnFields on MutationReturnInterface{
|
|||
success
|
||||
}
|
||||
|
||||
fragment basicApiJobsFields on ApiJob{
|
||||
createdAt
|
||||
description
|
||||
error
|
||||
finishedAt
|
||||
name
|
||||
progress
|
||||
result
|
||||
status
|
||||
statusText
|
||||
uid
|
||||
typeId
|
||||
updatedAt
|
||||
}
|
||||
|
||||
query GetApiVersion {
|
||||
api {
|
||||
version
|
||||
|
@ -13,17 +28,7 @@ query GetApiVersion {
|
|||
query GetApiJobs {
|
||||
jobs {
|
||||
getJobs {
|
||||
createdAt
|
||||
description
|
||||
error
|
||||
finishedAt
|
||||
name
|
||||
progress
|
||||
result
|
||||
status
|
||||
statusText
|
||||
uid
|
||||
updatedAt
|
||||
...basicApiJobsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,3 @@
|
|||
fragment basicMutationReturnFields on MutationReturnInterface{
|
||||
code
|
||||
message
|
||||
success
|
||||
}
|
||||
|
||||
query SystemSettings {
|
||||
system {
|
||||
settings {
|
||||
|
@ -28,6 +22,14 @@ query SystemIsUsingBinds {
|
|||
}
|
||||
}
|
||||
|
||||
fragment fragmentDnsRecords on DnsRecord {
|
||||
recordType
|
||||
name
|
||||
content
|
||||
ttl
|
||||
priority
|
||||
}
|
||||
|
||||
query DomainInfo {
|
||||
system {
|
||||
domainInfo {
|
||||
|
@ -35,7 +37,7 @@ query DomainInfo {
|
|||
hostname
|
||||
provider
|
||||
requiredDnsRecords {
|
||||
...dnsRecordFields
|
||||
...fragmentDnsRecords
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,21 +1,17 @@
|
|||
fragment basicMutationReturnFields on MutationReturnInterface{
|
||||
code
|
||||
message
|
||||
success
|
||||
}
|
||||
|
||||
query AllServices {
|
||||
services {
|
||||
allServices {
|
||||
description
|
||||
displayName
|
||||
dnsRecords {
|
||||
...dnsRecordFields
|
||||
...fragmentDnsRecords
|
||||
}
|
||||
id
|
||||
isEnabled
|
||||
isMovable
|
||||
isRequired
|
||||
canBeBackedUp
|
||||
backupDescription
|
||||
status
|
||||
storageUsage {
|
||||
title
|
||||
|
@ -64,17 +60,7 @@ mutation MoveService($input: MoveServiceInput!) {
|
|||
moveService(input: $input) {
|
||||
...basicMutationReturnFields
|
||||
job {
|
||||
createdAt
|
||||
description
|
||||
error
|
||||
finishedAt
|
||||
name
|
||||
progress
|
||||
result
|
||||
status
|
||||
statusText
|
||||
uid
|
||||
updatedAt
|
||||
...basicApiJobsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,3 @@
|
|||
fragment basicMutationReturnFields on MutationReturnInterface{
|
||||
code
|
||||
message
|
||||
success
|
||||
}
|
||||
|
||||
fragment userFields on User{
|
||||
username
|
||||
userType
|
||||
|
|
File diff suppressed because it is too large
Load diff
248
lib/logic/api_maps/graphql_maps/server_api/backups_api.dart
Normal file
248
lib/logic/api_maps/graphql_maps/server_api/backups_api.dart
Normal file
|
@ -0,0 +1,248 @@
|
|||
part of 'server_api.dart';
|
||||
|
||||
mixin BackupsApi on GraphQLApiMap {
|
||||
Future<List<Backup>> getBackups() async {
|
||||
List<Backup> backups;
|
||||
QueryResult<Query$AllBackupSnapshots> response;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
response = await client.query$AllBackupSnapshots();
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
backups = [];
|
||||
}
|
||||
final List<Backup> parsed = response.parsedData!.backup.allSnapshots
|
||||
.map(
|
||||
(
|
||||
final Query$AllBackupSnapshots$backup$allSnapshots snapshot,
|
||||
) =>
|
||||
Backup.fromGraphQL(snapshot),
|
||||
)
|
||||
.toList();
|
||||
backups = parsed;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
backups = [];
|
||||
}
|
||||
|
||||
return backups;
|
||||
}
|
||||
|
||||
Future<BackupConfiguration?> getBackupsConfiguration() async {
|
||||
BackupConfiguration? backupConfiguration;
|
||||
QueryResult<Query$BackupConfiguration> response;
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
response = await client.query$BackupConfiguration();
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
backupConfiguration = null;
|
||||
}
|
||||
final BackupConfiguration parsed = BackupConfiguration.fromGraphQL(
|
||||
response.parsedData!.backup.configuration,
|
||||
);
|
||||
backupConfiguration = parsed;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
backupConfiguration = null;
|
||||
}
|
||||
|
||||
return backupConfiguration;
|
||||
}
|
||||
|
||||
Future<GenericResult> forceBackupListReload() async {
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
await client.mutate$ForceSnapshotsReload();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<GenericResult<ServerJob?>> startBackup(final String serviceId) async {
|
||||
QueryResult<Mutation$StartBackup> response;
|
||||
GenericResult<ServerJob?>? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables = Variables$Mutation$StartBackup(serviceId: serviceId);
|
||||
final options = Options$Mutation$StartBackup(variables: variables);
|
||||
response = await client.mutate$StartBackup(options);
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
result = GenericResult(
|
||||
success: true,
|
||||
data: ServerJob.fromGraphQL(
|
||||
response.parsedData!.backup.startBackup.job!,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<GenericResult> setAutobackupPeriod({final int? period}) async {
|
||||
QueryResult<Mutation$SetAutobackupPeriod> response;
|
||||
GenericResult? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables = Variables$Mutation$SetAutobackupPeriod(period: period);
|
||||
final options =
|
||||
Options$Mutation$SetAutobackupPeriod(variables: variables);
|
||||
response = await client.mutate$SetAutobackupPeriod(options);
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
result = GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<GenericResult> removeRepository() async {
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
await client.mutate$RemoveRepository();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
return GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<GenericResult> initializeRepository(
|
||||
final InitializeRepositoryInput input,
|
||||
) async {
|
||||
QueryResult<Mutation$InitializeRepository> response;
|
||||
GenericResult? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables = Variables$Mutation$InitializeRepository(
|
||||
repository: Input$InitializeRepositoryInput(
|
||||
locationId: input.locationId,
|
||||
locationName: input.locationName,
|
||||
login: input.login,
|
||||
password: input.password,
|
||||
provider: input.provider.toGraphQL(),
|
||||
),
|
||||
);
|
||||
final options =
|
||||
Options$Mutation$InitializeRepository(variables: variables);
|
||||
response = await client.mutate$InitializeRepository(options);
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
result = GenericResult(
|
||||
success: true,
|
||||
data: null,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<GenericResult<ServerJob?>> restoreBackup(
|
||||
final String snapshotId,
|
||||
) async {
|
||||
QueryResult<Mutation$RestoreBackup> response;
|
||||
GenericResult<ServerJob?>? result;
|
||||
|
||||
try {
|
||||
final GraphQLClient client = await getClient();
|
||||
final variables =
|
||||
Variables$Mutation$RestoreBackup(snapshotId: snapshotId);
|
||||
final options = Options$Mutation$RestoreBackup(variables: variables);
|
||||
response = await client.mutate$RestoreBackup(options);
|
||||
if (response.hasException) {
|
||||
final message = response.exception.toString();
|
||||
print(message);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: message,
|
||||
);
|
||||
}
|
||||
result = GenericResult(
|
||||
success: true,
|
||||
data: ServerJob.fromGraphQL(
|
||||
response.parsedData!.backup.restoreBackup.job!,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
result = GenericResult(
|
||||
success: false,
|
||||
data: null,
|
||||
message: e.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'package:graphql/client.dart';
|
|||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/graphql_api_map.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart';
|
||||
|
@ -9,12 +10,12 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.g
|
|||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
|
||||
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||
import 'package:selfprivacy/logic/models/initialize_repository_input.dart';
|
||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/json/device_token.dart';
|
||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
||||
|
@ -31,9 +32,16 @@ part 'server_actions_api.dart';
|
|||
part 'services_api.dart';
|
||||
part 'users_api.dart';
|
||||
part 'volume_api.dart';
|
||||
part 'backups_api.dart';
|
||||
|
||||
class ServerApi extends GraphQLApiMap
|
||||
with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi {
|
||||
with
|
||||
VolumeApi,
|
||||
JobsApi,
|
||||
ServerActionsApi,
|
||||
ServicesApi,
|
||||
UsersApi,
|
||||
BackupsApi {
|
||||
ServerApi({
|
||||
this.hasLogger = false,
|
||||
this.isWithToken = true,
|
||||
|
@ -288,8 +296,10 @@ class ServerApi extends GraphQLApiMap
|
|||
}
|
||||
records = response.parsedData!.system.domainInfo.requiredDnsRecords
|
||||
.map<DnsRecord>(
|
||||
(final Fragment$dnsRecordFields fragment) =>
|
||||
DnsRecord.fromGraphQL(fragment),
|
||||
(
|
||||
final Fragment$fragmentDnsRecords record,
|
||||
) =>
|
||||
DnsRecord.fromGraphQL(record),
|
||||
)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
|
@ -509,22 +519,4 @@ class ServerApi extends GraphQLApiMap
|
|||
|
||||
return token;
|
||||
}
|
||||
|
||||
/// TODO: backups're not implemented on server side
|
||||
|
||||
Future<BackupStatus> getBackupStatus() async => BackupStatus(
|
||||
progress: 0.0,
|
||||
status: BackupStatusEnum.error,
|
||||
errorMessage: null,
|
||||
);
|
||||
|
||||
Future<List<Backup>> getBackups() async => [];
|
||||
|
||||
Future<void> uploadBackblazeConfig(final BackblazeBucket bucket) async {}
|
||||
|
||||
Future<void> forceBackupListReload() async {}
|
||||
|
||||
Future<void> startBackup() async {}
|
||||
|
||||
Future<void> restoreBackup(final String backupId) async {}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'dart:io';
|
|||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
|
||||
export 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||
|
||||
|
@ -36,7 +36,7 @@ class BackblazeApi extends RestApiMap {
|
|||
responseType: ResponseType.json,
|
||||
);
|
||||
if (isWithToken) {
|
||||
final BackblazeCredential? backblazeCredential =
|
||||
final BackupsCredential? backblazeCredential =
|
||||
getIt<ApiConfigModel>().backblazeCredential;
|
||||
final String token = backblazeCredential!.applicationKey;
|
||||
options.headers = {'Authorization': 'Basic $token'};
|
||||
|
@ -56,7 +56,7 @@ class BackblazeApi extends RestApiMap {
|
|||
|
||||
Future<BackblazeApiAuth> getAuthorizationToken() async {
|
||||
final Dio client = await getClient();
|
||||
final BackblazeCredential? backblazeCredential =
|
||||
final BackupsCredential? backblazeCredential =
|
||||
getIt<ApiConfigModel>().backblazeCredential;
|
||||
if (backblazeCredential == null) {
|
||||
throw Exception('Backblaze credential is null');
|
||||
|
@ -121,7 +121,7 @@ class BackblazeApi extends RestApiMap {
|
|||
// Create bucket
|
||||
Future<String> createBucket(final String bucketName) async {
|
||||
final BackblazeApiAuth auth = await getAuthorizationToken();
|
||||
final BackblazeCredential? backblazeCredential =
|
||||
final BackupsCredential? backblazeCredential =
|
||||
getIt<ApiConfigModel>().backblazeCredential;
|
||||
final Dio client = await getClient();
|
||||
client.options.baseUrl = auth.apiUrl;
|
||||
|
|
|
@ -6,7 +6,10 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
|
|||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/initialize_repository_input.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
|
||||
part 'backups_state.dart';
|
||||
|
||||
|
@ -24,107 +27,85 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
Future<void> load() async {
|
||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
||||
if (bucket == null) {
|
||||
emit(
|
||||
const BackupsState(
|
||||
isInitialized: false,
|
||||
preventActions: false,
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final BackupStatus status = await api.getBackupStatus();
|
||||
switch (status.status) {
|
||||
case BackupStatusEnum.noKey:
|
||||
case BackupStatusEnum.notInitialized:
|
||||
emit(
|
||||
BackupsState(
|
||||
backups: const [],
|
||||
isInitialized: true,
|
||||
preventActions: false,
|
||||
progress: 0,
|
||||
status: status.status,
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case BackupStatusEnum.initializing:
|
||||
emit(
|
||||
BackupsState(
|
||||
backups: const [],
|
||||
isInitialized: true,
|
||||
preventActions: false,
|
||||
progress: 0,
|
||||
status: status.status,
|
||||
refreshTimer: const Duration(seconds: 10),
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case BackupStatusEnum.initialized:
|
||||
case BackupStatusEnum.error:
|
||||
final List<Backup> backups = await api.getBackups();
|
||||
emit(
|
||||
BackupsState(
|
||||
backups: backups,
|
||||
isInitialized: true,
|
||||
preventActions: false,
|
||||
progress: status.progress,
|
||||
status: status.status,
|
||||
error: status.errorMessage ?? '',
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case BackupStatusEnum.backingUp:
|
||||
case BackupStatusEnum.restoring:
|
||||
final List<Backup> backups = await api.getBackups();
|
||||
emit(
|
||||
BackupsState(
|
||||
backups: backups,
|
||||
isInitialized: true,
|
||||
preventActions: true,
|
||||
progress: status.progress,
|
||||
status: status.status,
|
||||
error: status.errorMessage ?? '',
|
||||
refreshTimer: const Duration(seconds: 5),
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
emit(const BackupsState());
|
||||
}
|
||||
Timer(state.refreshTimer, () => updateBackups(useTimer: true));
|
||||
}
|
||||
final BackupConfiguration? backupConfig =
|
||||
await api.getBackupsConfiguration();
|
||||
final List<Backup> backups = await api.getBackups();
|
||||
backups.sort((final a, final b) => b.time.compareTo(a.time));
|
||||
emit(
|
||||
state.copyWith(
|
||||
backblazeBucket: bucket,
|
||||
isInitialized: backupConfig?.isInitialized,
|
||||
autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero,
|
||||
backups: backups,
|
||||
preventActions: false,
|
||||
refreshing: false,
|
||||
),
|
||||
);
|
||||
print(state);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createBucket() async {
|
||||
Future<void> initializeBackups() async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
final String domain = serverInstallationCubit.state.serverDomain!.domainName
|
||||
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
||||
final int serverId = serverInstallationCubit.state.serverDetails!.id;
|
||||
String bucketName = 'selfprivacy-$domain-$serverId';
|
||||
// If bucket name is too long, shorten it
|
||||
if (bucketName.length > 49) {
|
||||
bucketName = bucketName.substring(0, 49);
|
||||
final String? encryptionKey =
|
||||
(await api.getBackupsConfiguration())?.encryptionKey;
|
||||
if (encryptionKey == null) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar("Couldn't get encryption key from your server.");
|
||||
emit(state.copyWith(preventActions: false));
|
||||
return;
|
||||
}
|
||||
final String bucketId = await backblaze.createBucket(bucketName);
|
||||
|
||||
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
|
||||
final BackblazeBucket bucket = BackblazeBucket(
|
||||
bucketId: bucketId,
|
||||
bucketName: bucketName,
|
||||
applicationKey: key.applicationKey,
|
||||
applicationKeyId: key.applicationKeyId,
|
||||
final BackblazeBucket bucket;
|
||||
|
||||
if (state.backblazeBucket == null) {
|
||||
final String domain = serverInstallationCubit
|
||||
.state.serverDomain!.domainName
|
||||
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
||||
final int serverId = serverInstallationCubit.state.serverDetails!.id;
|
||||
String bucketName = 'selfprivacy-$domain-$serverId';
|
||||
// If bucket name is too long, shorten it
|
||||
if (bucketName.length > 49) {
|
||||
bucketName = bucketName.substring(0, 49);
|
||||
}
|
||||
final String bucketId = await backblaze.createBucket(bucketName);
|
||||
|
||||
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
|
||||
bucket = BackblazeBucket(
|
||||
bucketId: bucketId,
|
||||
bucketName: bucketName,
|
||||
applicationKey: key.applicationKey,
|
||||
applicationKeyId: key.applicationKeyId,
|
||||
encryptionKey: encryptionKey,
|
||||
);
|
||||
|
||||
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
||||
emit(state.copyWith(backblazeBucket: bucket));
|
||||
} else {
|
||||
bucket = state.backblazeBucket!;
|
||||
}
|
||||
|
||||
final GenericResult result = await api.initializeRepository(
|
||||
InitializeRepositoryInput(
|
||||
provider: BackupsProviderType.backblaze,
|
||||
locationId: bucket.bucketId,
|
||||
locationName: bucket.bucketName,
|
||||
login: bucket.applicationKeyId,
|
||||
password: bucket.applicationKey,
|
||||
),
|
||||
);
|
||||
if (result.success == false) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar(result.message ?? 'Unknown error');
|
||||
emit(state.copyWith(preventActions: false));
|
||||
return;
|
||||
}
|
||||
await updateBackups();
|
||||
getIt<NavigationService>().showSnackBar(
|
||||
'Backups repository is now initializing. It may take a while.',
|
||||
);
|
||||
|
||||
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
||||
await api.uploadBackblazeConfig(bucket);
|
||||
await updateBackups();
|
||||
|
||||
emit(state.copyWith(isInitialized: true, preventActions: false));
|
||||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
Future<void> reuploadKey() async {
|
||||
|
@ -132,37 +113,48 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
||||
if (bucket == null) {
|
||||
emit(state.copyWith(isInitialized: false));
|
||||
print('bucket is null');
|
||||
} else {
|
||||
await api.uploadBackblazeConfig(bucket);
|
||||
emit(state.copyWith(isInitialized: true, preventActions: false));
|
||||
getIt<NavigationService>().showSnackBar('backup.reuploaded_key');
|
||||
print('bucket is not null');
|
||||
final GenericResult result = await api.initializeRepository(
|
||||
InitializeRepositoryInput(
|
||||
provider: BackupsProviderType.backblaze,
|
||||
locationId: bucket.bucketId,
|
||||
locationName: bucket.bucketName,
|
||||
login: bucket.applicationKeyId,
|
||||
password: bucket.applicationKey,
|
||||
),
|
||||
);
|
||||
print('result is $result');
|
||||
if (result.success == false) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar(result.message ?? 'Unknown error');
|
||||
emit(state.copyWith(preventActions: false));
|
||||
return;
|
||||
} else {
|
||||
emit(state.copyWith(preventActions: false));
|
||||
getIt<NavigationService>().showSnackBar('backup.reuploaded_key');
|
||||
await updateBackups();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Duration refreshTimeFromState(final BackupStatusEnum status) {
|
||||
switch (status) {
|
||||
case BackupStatusEnum.backingUp:
|
||||
case BackupStatusEnum.restoring:
|
||||
return const Duration(seconds: 5);
|
||||
case BackupStatusEnum.initializing:
|
||||
return const Duration(seconds: 10);
|
||||
default:
|
||||
return const Duration(seconds: 60);
|
||||
}
|
||||
}
|
||||
@Deprecated("we don't have states")
|
||||
Duration refreshTimeFromState() => const Duration(seconds: 60);
|
||||
|
||||
Future<void> updateBackups({final bool useTimer = false}) async {
|
||||
emit(state.copyWith(refreshing: true));
|
||||
final List<Backup> backups = await api.getBackups();
|
||||
final BackupStatus status = await api.getBackupStatus();
|
||||
final backups = await api.getBackups();
|
||||
backups.sort((final a, final b) => b.time.compareTo(a.time));
|
||||
final backupConfig = await api.getBackupsConfiguration();
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
backups: backups,
|
||||
progress: status.progress,
|
||||
status: status.status,
|
||||
error: status.errorMessage,
|
||||
refreshTimer: refreshTimeFromState(status.status),
|
||||
refreshTimer: refreshTimeFromState(),
|
||||
refreshing: false,
|
||||
isInitialized: backupConfig?.isInitialized ?? false,
|
||||
autobackupPeriod: backupConfig?.autobackupPeriod,
|
||||
),
|
||||
);
|
||||
if (useTimer) {
|
||||
|
@ -172,14 +164,23 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
|
||||
Future<void> forceUpdateBackups() async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
await api.forceBackupListReload();
|
||||
getIt<NavigationService>().showSnackBar('backup.refetching_list'.tr());
|
||||
await api.forceBackupListReload();
|
||||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
Future<void> createBackup() async {
|
||||
Future<void> createMultipleBackups(final List<Service> services) async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
await api.startBackup();
|
||||
for (final service in services) {
|
||||
await api.startBackup(service.id);
|
||||
}
|
||||
await updateBackups();
|
||||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
Future<void> createBackup(final String serviceId) async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
await api.startBackup(serviceId);
|
||||
await updateBackups();
|
||||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
@ -190,6 +191,26 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|||
emit(state.copyWith(preventActions: false));
|
||||
}
|
||||
|
||||
Future<void> setAutobackupPeriod(final Duration? period) async {
|
||||
emit(state.copyWith(preventActions: true));
|
||||
final result = await api.setAutobackupPeriod(period: period?.inMinutes);
|
||||
if (result.success == false) {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar(result.message ?? 'Unknown error');
|
||||
emit(state.copyWith(preventActions: false));
|
||||
} else {
|
||||
getIt<NavigationService>()
|
||||
.showSnackBar('backup.autobackup_period_set'.tr());
|
||||
emit(
|
||||
state.copyWith(
|
||||
preventActions: false,
|
||||
autobackupPeriod: period ?? Duration.zero,
|
||||
),
|
||||
);
|
||||
}
|
||||
await updateBackups();
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() async {
|
||||
emit(const BackupsState());
|
||||
|
|
|
@ -4,53 +4,54 @@ class BackupsState extends ServerInstallationDependendState {
|
|||
const BackupsState({
|
||||
this.isInitialized = false,
|
||||
this.backups = const [],
|
||||
this.progress = 0.0,
|
||||
this.status = BackupStatusEnum.noKey,
|
||||
this.preventActions = true,
|
||||
this.error = '',
|
||||
this.refreshTimer = const Duration(seconds: 60),
|
||||
this.refreshing = true,
|
||||
this.autobackupPeriod,
|
||||
this.backblazeBucket,
|
||||
});
|
||||
|
||||
final bool isInitialized;
|
||||
final List<Backup> backups;
|
||||
final double progress;
|
||||
final BackupStatusEnum status;
|
||||
final bool preventActions;
|
||||
final String error;
|
||||
final Duration refreshTimer;
|
||||
final bool refreshing;
|
||||
final Duration? autobackupPeriod;
|
||||
final BackblazeBucket? backblazeBucket;
|
||||
|
||||
List<Backup> serviceBackups(final String serviceId) => backups
|
||||
.where((final backup) => backup.serviceId == serviceId)
|
||||
.toList(growable: false);
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
isInitialized,
|
||||
backups,
|
||||
progress,
|
||||
preventActions,
|
||||
status,
|
||||
error,
|
||||
refreshTimer,
|
||||
refreshing
|
||||
refreshing,
|
||||
];
|
||||
|
||||
BackupsState copyWith({
|
||||
final bool? isInitialized,
|
||||
final List<Backup>? backups,
|
||||
final double? progress,
|
||||
final BackupStatusEnum? status,
|
||||
final bool? preventActions,
|
||||
final String? error,
|
||||
final Duration? refreshTimer,
|
||||
final bool? refreshing,
|
||||
final Duration? autobackupPeriod,
|
||||
final BackblazeBucket? backblazeBucket,
|
||||
}) =>
|
||||
BackupsState(
|
||||
isInitialized: isInitialized ?? this.isInitialized,
|
||||
backups: backups ?? this.backups,
|
||||
progress: progress ?? this.progress,
|
||||
status: status ?? this.status,
|
||||
preventActions: preventActions ?? this.preventActions,
|
||||
error: error ?? this.error,
|
||||
refreshTimer: refreshTimer ?? this.refreshTimer,
|
||||
refreshing: refreshing ?? this.refreshing,
|
||||
// The autobackupPeriod might be null, so if the duration is set to 0, we
|
||||
// set it to null.
|
||||
autobackupPeriod: autobackupPeriod?.inSeconds == 0
|
||||
? null
|
||||
: autobackupPeriod ?? this.autobackupPeriod,
|
||||
backblazeBucket: backblazeBucket ?? this.backblazeBucket,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class BackblazeFormCubit extends FormCubit {
|
||||
|
|
|
@ -5,20 +5,19 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
||||
import 'package:selfprivacy/logic/models/launch_installation_data.dart';
|
||||
import 'package:selfprivacy/logic/providers/provider_settings.dart';
|
||||
import 'package:selfprivacy/logic/providers/providers_controller.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||
|
||||
import 'package:selfprivacy/logic/models/launch_installation_data.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
|
||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||
import 'package:selfprivacy/logic/providers/provider_settings.dart';
|
||||
import 'package:selfprivacy/logic/providers/providers_controller.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
|
||||
export 'package:provider/provider.dart';
|
||||
|
@ -195,9 +194,10 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
}
|
||||
|
||||
void setBackblazeKey(final String keyId, final String applicationKey) async {
|
||||
final BackblazeCredential backblazeCredential = BackblazeCredential(
|
||||
final BackupsCredential backblazeCredential = BackupsCredential(
|
||||
keyId: keyId,
|
||||
applicationKey: applicationKey,
|
||||
provider: BackupsProviderType.backblaze,
|
||||
);
|
||||
await repository.saveBackblazeKey(backblazeCredential);
|
||||
if (state is ServerInstallationRecovery) {
|
||||
|
@ -699,7 +699,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
provider: dnsProviderType,
|
||||
),
|
||||
);
|
||||
await repository.setDnsApiToken(token);
|
||||
// await repository.setDnsApiToken(token);
|
||||
emit(
|
||||
dataState.copyWith(
|
||||
serverDomain: ServerDomain(
|
||||
|
@ -714,7 +714,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
}
|
||||
|
||||
void finishRecoveryProcess(
|
||||
final BackblazeCredential backblazeCredential,
|
||||
final BackupsCredential backblazeCredential,
|
||||
) async {
|
||||
await repository.saveIsServerStarted(true);
|
||||
await repository.saveIsServerResetedFirstTime(true);
|
||||
|
|
|
@ -13,7 +13,7 @@ import 'package:selfprivacy/logic/providers/provider_settings.dart';
|
|||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||
|
@ -46,7 +46,7 @@ class ServerInstallationRepository {
|
|||
final DnsProviderType? dnsProvider = getIt<ApiConfigModel>().dnsProvider;
|
||||
final ServerProviderType? serverProvider =
|
||||
getIt<ApiConfigModel>().serverProvider;
|
||||
final BackblazeCredential? backblazeCredential =
|
||||
final BackupsCredential? backblazeCredential =
|
||||
getIt<ApiConfigModel>().backblazeCredential;
|
||||
final ServerHostingDetails? serverDetails =
|
||||
getIt<ApiConfigModel>().serverDetails;
|
||||
|
@ -170,12 +170,14 @@ class ServerInstallationRepository {
|
|||
Future<String?> getDomainId(final String token, final String domain) async {
|
||||
final result =
|
||||
await ProvidersController.currentDnsProvider!.tryInitApiByToken(token);
|
||||
return result.success
|
||||
? (await ProvidersController.currentDnsProvider!.getZoneId(
|
||||
domain,
|
||||
))
|
||||
.data
|
||||
: null;
|
||||
if (!result.success) {
|
||||
return null;
|
||||
}
|
||||
await setDnsApiToken(token);
|
||||
return (await ProvidersController.currentDnsProvider!.getZoneId(
|
||||
domain,
|
||||
))
|
||||
.data;
|
||||
}
|
||||
|
||||
Future<Map<String, bool>> isDnsAddressesMatch(
|
||||
|
@ -519,7 +521,7 @@ class ServerInstallationRepository {
|
|||
}
|
||||
|
||||
Future<void> saveBackblazeKey(
|
||||
final BackblazeCredential backblazeCredential,
|
||||
final BackupsCredential backblazeCredential,
|
||||
) async {
|
||||
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ abstract class ServerInstallationState extends Equatable {
|
|||
final String? providerApiToken;
|
||||
final String? dnsApiToken;
|
||||
final String? serverTypeIdentificator;
|
||||
final BackblazeCredential? backblazeCredential;
|
||||
final BackupsCredential? backblazeCredential;
|
||||
final ServerDomain? serverDomain;
|
||||
final User? rootUser;
|
||||
final ServerHostingDetails? serverDetails;
|
||||
|
@ -167,7 +167,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
|||
final String? providerApiToken,
|
||||
final String? serverTypeIdentificator,
|
||||
final String? dnsApiToken,
|
||||
final BackblazeCredential? backblazeCredential,
|
||||
final BackupsCredential? backblazeCredential,
|
||||
final ServerDomain? serverDomain,
|
||||
final User? rootUser,
|
||||
final ServerHostingDetails? serverDetails,
|
||||
|
@ -237,7 +237,7 @@ class ServerInstallationFinished extends ServerInstallationState {
|
|||
required String super.providerApiToken,
|
||||
required String super.serverTypeIdentificator,
|
||||
required String super.dnsApiToken,
|
||||
required BackblazeCredential super.backblazeCredential,
|
||||
required BackupsCredential super.backblazeCredential,
|
||||
required ServerDomain super.serverDomain,
|
||||
required User super.rootUser,
|
||||
required ServerHostingDetails super.serverDetails,
|
||||
|
@ -324,7 +324,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
|||
final String? providerApiToken,
|
||||
final String? serverTypeIdentificator,
|
||||
final String? dnsApiToken,
|
||||
final BackblazeCredential? backblazeCredential,
|
||||
final BackupsCredential? backblazeCredential,
|
||||
final ServerDomain? serverDomain,
|
||||
final User? rootUser,
|
||||
final ServerHostingDetails? serverDetails,
|
||||
|
|
|
@ -21,6 +21,14 @@ class ServerJobsState extends ServerInstallationDependendState {
|
|||
}
|
||||
}
|
||||
|
||||
List<ServerJob> get backupJobList => serverJobList
|
||||
.where(
|
||||
// The backup jobs has the format of 'service.<service_id>.backup'
|
||||
(final job) =>
|
||||
job.typeId.contains('backup') || job.typeId.contains('restore'),
|
||||
)
|
||||
.toList();
|
||||
|
||||
bool get hasRemovableJobs => serverJobList.any(
|
||||
(final job) =>
|
||||
job.status == JobStatusEnum.finished ||
|
||||
|
|
|
@ -12,40 +12,15 @@ class ServicesState extends ServerInstallationDependendState {
|
|||
final List<Service> services;
|
||||
final List<String> lockedServices;
|
||||
|
||||
List<Service> get servicesThatCanBeBackedUp => services
|
||||
.where(
|
||||
(final service) => service.canBeBackedUp,
|
||||
)
|
||||
.toList();
|
||||
|
||||
bool isServiceLocked(final String serviceId) =>
|
||||
lockedServices.contains(serviceId);
|
||||
|
||||
bool get isPasswordManagerEnable => services
|
||||
.firstWhere(
|
||||
(final service) => service.id == 'bitwarden',
|
||||
orElse: () => Service.empty,
|
||||
)
|
||||
.isEnabled;
|
||||
bool get isCloudEnable => services
|
||||
.firstWhere(
|
||||
(final service) => service.id == 'nextcloud',
|
||||
orElse: () => Service.empty,
|
||||
)
|
||||
.isEnabled;
|
||||
bool get isGitEnable => services
|
||||
.firstWhere(
|
||||
(final service) => service.id == 'gitea',
|
||||
orElse: () => Service.empty,
|
||||
)
|
||||
.isEnabled;
|
||||
bool get isSocialNetworkEnable => services
|
||||
.firstWhere(
|
||||
(final service) => service.id == 'pleroma',
|
||||
orElse: () => Service.empty,
|
||||
)
|
||||
.isEnabled;
|
||||
bool get isVpnEnable => services
|
||||
.firstWhere(
|
||||
(final service) => service.id == 'ocserv',
|
||||
orElse: () => Service.empty,
|
||||
)
|
||||
.isEnabled;
|
||||
|
||||
Service? getServiceById(final String id) {
|
||||
final service = services.firstWhere(
|
||||
(final service) => service.id == id,
|
||||
|
@ -63,23 +38,6 @@ class ServicesState extends ServerInstallationDependendState {
|
|||
lockedServices,
|
||||
];
|
||||
|
||||
bool isEnableByType(final Service service) {
|
||||
switch (service.id) {
|
||||
case 'bitwarden':
|
||||
return isPasswordManagerEnable;
|
||||
case 'nextcloud':
|
||||
return isCloudEnable;
|
||||
case 'pleroma':
|
||||
return isSocialNetworkEnable;
|
||||
case 'gitea':
|
||||
return isGitEnable;
|
||||
case 'ocserv':
|
||||
return isVpnEnable;
|
||||
default:
|
||||
throw Exception('wrong state');
|
||||
}
|
||||
}
|
||||
|
||||
ServicesState copyWith({
|
||||
final List<Service>? services,
|
||||
final List<String>? lockedServices,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||
|
||||
|
@ -15,7 +15,8 @@ class ApiConfigModel {
|
|||
String? get dnsProviderKey => _dnsProviderKey;
|
||||
ServerProviderType? get serverProvider => _serverProvider;
|
||||
DnsProviderType? get dnsProvider => _dnsProvider;
|
||||
BackblazeCredential? get backblazeCredential => _backblazeCredential;
|
||||
|
||||
BackupsCredential? get backblazeCredential => _backblazeCredential;
|
||||
ServerDomain? get serverDomain => _serverDomain;
|
||||
BackblazeBucket? get backblazeBucket => _backblazeBucket;
|
||||
|
||||
|
@ -26,7 +27,7 @@ class ApiConfigModel {
|
|||
ServerProviderType? _serverProvider;
|
||||
DnsProviderType? _dnsProvider;
|
||||
ServerHostingDetails? _serverDetails;
|
||||
BackblazeCredential? _backblazeCredential;
|
||||
BackupsCredential? _backblazeCredential;
|
||||
ServerDomain? _serverDomain;
|
||||
BackblazeBucket? _backblazeBucket;
|
||||
|
||||
|
@ -60,7 +61,7 @@ class ApiConfigModel {
|
|||
_serverLocation = serverLocation;
|
||||
}
|
||||
|
||||
Future<void> storeBackblazeCredential(final BackblazeCredential value) async {
|
||||
Future<void> storeBackblazeCredential(final BackupsCredential value) async {
|
||||
await _box.put(BNames.backblazeCredential, value);
|
||||
_backblazeCredential = value;
|
||||
}
|
||||
|
|
60
lib/logic/models/backup.dart
Normal file
60
lib/logic/models/backup.dart
Normal file
|
@ -0,0 +1,60 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
|
||||
class Backup {
|
||||
Backup.fromGraphQL(
|
||||
final Query$AllBackupSnapshots$backup$allSnapshots snapshot,
|
||||
) : this(
|
||||
id: snapshot.id,
|
||||
time: snapshot.createdAt,
|
||||
serviceId: snapshot.service.id,
|
||||
fallbackServiceName: snapshot.service.displayName,
|
||||
);
|
||||
|
||||
Backup({
|
||||
required this.time,
|
||||
required this.id,
|
||||
required this.serviceId,
|
||||
required this.fallbackServiceName,
|
||||
});
|
||||
|
||||
// Time of the backup
|
||||
final DateTime time;
|
||||
@JsonKey(name: 'short_id')
|
||||
final String id;
|
||||
final String serviceId;
|
||||
final String fallbackServiceName;
|
||||
}
|
||||
|
||||
class BackupConfiguration {
|
||||
BackupConfiguration.fromGraphQL(
|
||||
final Query$BackupConfiguration$backup$configuration configuration,
|
||||
) : this(
|
||||
// Provided by API as int of minutes
|
||||
autobackupPeriod: configuration.autobackupPeriod != null
|
||||
? Duration(minutes: configuration.autobackupPeriod!)
|
||||
: null,
|
||||
encryptionKey: configuration.encryptionKey,
|
||||
isInitialized: configuration.isInitialized,
|
||||
locationId: configuration.locationId,
|
||||
locationName: configuration.locationName,
|
||||
provider: BackupsProviderType.fromGraphQL(configuration.provider),
|
||||
);
|
||||
|
||||
BackupConfiguration({
|
||||
required this.autobackupPeriod,
|
||||
required this.encryptionKey,
|
||||
required this.isInitialized,
|
||||
required this.locationId,
|
||||
required this.locationName,
|
||||
required this.provider,
|
||||
});
|
||||
|
||||
final Duration? autobackupPeriod;
|
||||
final String encryptionKey;
|
||||
final bool isInitialized;
|
||||
final String? locationId;
|
||||
final String? locationName;
|
||||
final BackupsProviderType provider;
|
||||
}
|
|
@ -12,3 +12,4 @@
|
|||
100. DnsProvider
|
||||
101. ServerProvider
|
||||
102. UserType
|
||||
103. BackupsProvider
|
||||
|
|
|
@ -9,6 +9,7 @@ class BackblazeBucket {
|
|||
required this.bucketName,
|
||||
required this.applicationKeyId,
|
||||
required this.applicationKey,
|
||||
required this.encryptionKey,
|
||||
});
|
||||
|
||||
@HiveField(0)
|
||||
|
@ -23,6 +24,9 @@ class BackblazeBucket {
|
|||
@HiveField(3)
|
||||
final String bucketName;
|
||||
|
||||
@HiveField(4)
|
||||
final String encryptionKey;
|
||||
|
||||
@override
|
||||
String toString() => bucketName;
|
||||
}
|
||||
|
|
|
@ -21,13 +21,14 @@ class BackblazeBucketAdapter extends TypeAdapter<BackblazeBucket> {
|
|||
bucketName: fields[3] as String,
|
||||
applicationKeyId: fields[1] as String,
|
||||
applicationKey: fields[2] as String,
|
||||
encryptionKey: fields[4] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, BackblazeBucket obj) {
|
||||
writer
|
||||
..writeByte(4)
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.bucketId)
|
||||
..writeByte(1)
|
||||
|
@ -35,7 +36,9 @@ class BackblazeBucketAdapter extends TypeAdapter<BackblazeBucket> {
|
|||
..writeByte(2)
|
||||
..write(obj.applicationKey)
|
||||
..writeByte(3)
|
||||
..write(obj.bucketName);
|
||||
..write(obj.bucketName)
|
||||
..writeByte(4)
|
||||
..write(obj.encryptionKey);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'backblaze_credential.g.dart';
|
||||
|
||||
@HiveType(typeId: 4)
|
||||
class BackblazeCredential {
|
||||
BackblazeCredential({required this.keyId, required this.applicationKey});
|
||||
|
||||
@HiveField(0)
|
||||
final String keyId;
|
||||
|
||||
@HiveField(1)
|
||||
final String applicationKey;
|
||||
|
||||
String get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
||||
|
||||
@override
|
||||
String toString() => '$keyId: $encodedApiKey';
|
||||
}
|
||||
|
||||
String encodedBackblazeKey(final String? keyId, final String? applicationKey) {
|
||||
final String apiKey = '$keyId:$applicationKey';
|
||||
final String encodedApiKey = base64.encode(utf8.encode(apiKey));
|
||||
return encodedApiKey;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'backblaze_credential.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class BackblazeCredentialAdapter extends TypeAdapter<BackblazeCredential> {
|
||||
@override
|
||||
final int typeId = 4;
|
||||
|
||||
@override
|
||||
BackblazeCredential read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return BackblazeCredential(
|
||||
keyId: fields[0] as String,
|
||||
applicationKey: fields[1] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, BackblazeCredential obj) {
|
||||
writer
|
||||
..writeByte(2)
|
||||
..writeByte(0)
|
||||
..write(obj.keyId)
|
||||
..writeByte(1)
|
||||
..write(obj.applicationKey);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BackblazeCredentialAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
63
lib/logic/models/hive/backups_credential.dart
Normal file
63
lib/logic/models/hive/backups_credential.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||
|
||||
part 'backups_credential.g.dart';
|
||||
|
||||
@HiveType(typeId: 4)
|
||||
class BackupsCredential {
|
||||
BackupsCredential({
|
||||
required this.keyId,
|
||||
required this.applicationKey,
|
||||
required this.provider,
|
||||
});
|
||||
|
||||
@HiveField(0)
|
||||
final String keyId;
|
||||
|
||||
@HiveField(1)
|
||||
final String applicationKey;
|
||||
|
||||
@HiveField(2, defaultValue: BackupsProviderType.backblaze)
|
||||
final BackupsProviderType provider;
|
||||
|
||||
String get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
||||
|
||||
@override
|
||||
String toString() => '$keyId: $encodedApiKey';
|
||||
}
|
||||
|
||||
String encodedBackblazeKey(final String? keyId, final String? applicationKey) {
|
||||
final String apiKey = '$keyId:$applicationKey';
|
||||
final String encodedApiKey = base64.encode(utf8.encode(apiKey));
|
||||
return encodedApiKey;
|
||||
}
|
||||
|
||||
@HiveType(typeId: 103)
|
||||
enum BackupsProviderType {
|
||||
@HiveField(0)
|
||||
none,
|
||||
@HiveField(1)
|
||||
memory,
|
||||
@HiveField(2)
|
||||
file,
|
||||
@HiveField(3)
|
||||
backblaze;
|
||||
|
||||
factory BackupsProviderType.fromGraphQL(final Enum$BackupProvider provider) =>
|
||||
switch (provider) {
|
||||
Enum$BackupProvider.NONE => none,
|
||||
Enum$BackupProvider.MEMORY => memory,
|
||||
Enum$BackupProvider.FILE => file,
|
||||
Enum$BackupProvider.BACKBLAZE => backblaze,
|
||||
Enum$BackupProvider.$unknown => none
|
||||
};
|
||||
|
||||
Enum$BackupProvider toGraphQL() => switch (this) {
|
||||
none => Enum$BackupProvider.NONE,
|
||||
memory => Enum$BackupProvider.MEMORY,
|
||||
file => Enum$BackupProvider.FILE,
|
||||
backblaze => Enum$BackupProvider.BACKBLAZE,
|
||||
};
|
||||
}
|
98
lib/logic/models/hive/backups_credential.g.dart
Normal file
98
lib/logic/models/hive/backups_credential.g.dart
Normal file
|
@ -0,0 +1,98 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'backups_credential.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class BackupsCredentialAdapter extends TypeAdapter<BackupsCredential> {
|
||||
@override
|
||||
final int typeId = 4;
|
||||
|
||||
@override
|
||||
BackupsCredential read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return BackupsCredential(
|
||||
keyId: fields[0] as String,
|
||||
applicationKey: fields[1] as String,
|
||||
provider: fields[2] == null
|
||||
? BackupsProviderType.backblaze
|
||||
: fields[2] as BackupsProviderType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, BackupsCredential obj) {
|
||||
writer
|
||||
..writeByte(3)
|
||||
..writeByte(0)
|
||||
..write(obj.keyId)
|
||||
..writeByte(1)
|
||||
..write(obj.applicationKey)
|
||||
..writeByte(2)
|
||||
..write(obj.provider);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BackupsCredentialAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
|
||||
class BackupsProviderTypeAdapter extends TypeAdapter<BackupsProviderType> {
|
||||
@override
|
||||
final int typeId = 103;
|
||||
|
||||
@override
|
||||
BackupsProviderType read(BinaryReader reader) {
|
||||
switch (reader.readByte()) {
|
||||
case 0:
|
||||
return BackupsProviderType.none;
|
||||
case 1:
|
||||
return BackupsProviderType.memory;
|
||||
case 2:
|
||||
return BackupsProviderType.file;
|
||||
case 3:
|
||||
return BackupsProviderType.backblaze;
|
||||
default:
|
||||
return BackupsProviderType.none;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, BackupsProviderType obj) {
|
||||
switch (obj) {
|
||||
case BackupsProviderType.none:
|
||||
writer.writeByte(0);
|
||||
break;
|
||||
case BackupsProviderType.memory:
|
||||
writer.writeByte(1);
|
||||
break;
|
||||
case BackupsProviderType.file:
|
||||
writer.writeByte(2);
|
||||
break;
|
||||
case BackupsProviderType.backblaze:
|
||||
writer.writeByte(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is BackupsProviderTypeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
16
lib/logic/models/initialize_repository_input.dart
Normal file
16
lib/logic/models/initialize_repository_input.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
||||
|
||||
class InitializeRepositoryInput {
|
||||
InitializeRepositoryInput({
|
||||
required this.provider,
|
||||
required this.locationId,
|
||||
required this.locationName,
|
||||
required this.login,
|
||||
required this.password,
|
||||
});
|
||||
final BackupsProviderType provider;
|
||||
final String locationId;
|
||||
final String locationName;
|
||||
final String login;
|
||||
final String password;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'backup.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Backup {
|
||||
factory Backup.fromJson(final Map<String, dynamic> json) =>
|
||||
_$BackupFromJson(json);
|
||||
Backup({required this.time, required this.id});
|
||||
|
||||
// Time of the backup
|
||||
final DateTime time;
|
||||
@JsonKey(name: 'short_id')
|
||||
final String id;
|
||||
}
|
||||
|
||||
enum BackupStatusEnum {
|
||||
@JsonValue('NO_KEY')
|
||||
noKey,
|
||||
@JsonValue('NOT_INITIALIZED')
|
||||
notInitialized,
|
||||
@JsonValue('INITIALIZED')
|
||||
initialized,
|
||||
@JsonValue('BACKING_UP')
|
||||
backingUp,
|
||||
@JsonValue('RESTORING')
|
||||
restoring,
|
||||
@JsonValue('ERROR')
|
||||
error,
|
||||
@JsonValue('INITIALIZING')
|
||||
initializing,
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class BackupStatus {
|
||||
factory BackupStatus.fromJson(final Map<String, dynamic> json) =>
|
||||
_$BackupStatusFromJson(json);
|
||||
BackupStatus({
|
||||
required this.status,
|
||||
required this.progress,
|
||||
required this.errorMessage,
|
||||
});
|
||||
|
||||
final BackupStatusEnum status;
|
||||
final double progress;
|
||||
@JsonKey(name: 'error_message')
|
||||
final String? errorMessage;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'backup.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Backup _$BackupFromJson(Map<String, dynamic> json) => Backup(
|
||||
time: DateTime.parse(json['time'] as String),
|
||||
id: json['short_id'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BackupToJson(Backup instance) => <String, dynamic>{
|
||||
'time': instance.time.toIso8601String(),
|
||||
'short_id': instance.id,
|
||||
};
|
||||
|
||||
BackupStatus _$BackupStatusFromJson(Map<String, dynamic> json) => BackupStatus(
|
||||
status: $enumDecode(_$BackupStatusEnumEnumMap, json['status']),
|
||||
progress: (json['progress'] as num).toDouble(),
|
||||
errorMessage: json['error_message'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BackupStatusToJson(BackupStatus instance) =>
|
||||
<String, dynamic>{
|
||||
'status': _$BackupStatusEnumEnumMap[instance.status]!,
|
||||
'progress': instance.progress,
|
||||
'error_message': instance.errorMessage,
|
||||
};
|
||||
|
||||
const _$BackupStatusEnumEnumMap = {
|
||||
BackupStatusEnum.noKey: 'NO_KEY',
|
||||
BackupStatusEnum.notInitialized: 'NOT_INITIALIZED',
|
||||
BackupStatusEnum.initialized: 'INITIALIZED',
|
||||
BackupStatusEnum.backingUp: 'BACKING_UP',
|
||||
BackupStatusEnum.restoring: 'RESTORING',
|
||||
BackupStatusEnum.error: 'ERROR',
|
||||
BackupStatusEnum.initializing: 'INITIALIZING',
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
|
||||
|
||||
part 'dns_records.g.dart';
|
||||
|
||||
|
@ -16,7 +16,7 @@ class DnsRecord {
|
|||
});
|
||||
|
||||
DnsRecord.fromGraphQL(
|
||||
final Fragment$dnsRecordFields record,
|
||||
final Fragment$fragmentDnsRecords record,
|
||||
) : this(
|
||||
type: record.recordType,
|
||||
name: record.name,
|
||||
|
|
|
@ -12,6 +12,7 @@ class ServerJob {
|
|||
required this.description,
|
||||
required this.status,
|
||||
required this.uid,
|
||||
required this.typeId,
|
||||
required this.updatedAt,
|
||||
required this.createdAt,
|
||||
this.error,
|
||||
|
@ -21,7 +22,7 @@ class ServerJob {
|
|||
this.finishedAt,
|
||||
});
|
||||
|
||||
ServerJob.fromGraphQL(final Query$GetApiJobs$jobs$getJobs serverJob)
|
||||
ServerJob.fromGraphQL(final Fragment$basicApiJobsFields serverJob)
|
||||
: this(
|
||||
createdAt: serverJob.createdAt,
|
||||
description: serverJob.description,
|
||||
|
@ -33,12 +34,14 @@ class ServerJob {
|
|||
status: JobStatusEnum.fromString(serverJob.status),
|
||||
statusText: serverJob.statusText,
|
||||
uid: serverJob.uid,
|
||||
typeId: serverJob.typeId,
|
||||
updatedAt: serverJob.updatedAt,
|
||||
);
|
||||
final String name;
|
||||
final String description;
|
||||
final JobStatusEnum status;
|
||||
final String uid;
|
||||
final String typeId;
|
||||
final DateTime updatedAt;
|
||||
final DateTime createdAt;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ ServerJob _$ServerJobFromJson(Map<String, dynamic> json) => ServerJob(
|
|||
description: json['description'] as String,
|
||||
status: $enumDecode(_$JobStatusEnumEnumMap, json['status']),
|
||||
uid: json['uid'] as String,
|
||||
typeId: json['typeId'] as String,
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
error: json['error'] as String?,
|
||||
|
@ -27,6 +28,7 @@ Map<String, dynamic> _$ServerJobToJson(ServerJob instance) => <String, dynamic>{
|
|||
'description': instance.description,
|
||||
'status': _$JobStatusEnumEnumMap[instance.status]!,
|
||||
'uid': instance.uid,
|
||||
'typeId': instance.typeId,
|
||||
'updatedAt': instance.updatedAt.toIso8601String(),
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'error': instance.error,
|
||||
|
|
|
@ -6,6 +6,8 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.
|
|||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
|
||||
|
||||
class Service {
|
||||
Service.fromGraphQL(final Query$AllServices$services$allServices service)
|
||||
: this(
|
||||
|
@ -15,6 +17,8 @@ class Service {
|
|||
isEnabled: service.isEnabled,
|
||||
isRequired: service.isRequired,
|
||||
isMovable: service.isMovable,
|
||||
canBeBackedUp: service.canBeBackedUp,
|
||||
backupDescription: service.backupDescription,
|
||||
status: ServiceStatus.fromGraphQL(service.status),
|
||||
storageUsage: ServiceStorageUsage(
|
||||
used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)),
|
||||
|
@ -24,7 +28,9 @@ class Service {
|
|||
svgIcon: utf8.decode(base64.decode(service.svgIcon)),
|
||||
dnsRecords: service.dnsRecords
|
||||
?.map(
|
||||
(final Fragment$dnsRecordFields record) =>
|
||||
(
|
||||
final Fragment$fragmentDnsRecords record,
|
||||
) =>
|
||||
DnsRecord.fromGraphQL(record),
|
||||
)
|
||||
.toList() ??
|
||||
|
@ -38,6 +44,8 @@ class Service {
|
|||
required this.isEnabled,
|
||||
required this.isRequired,
|
||||
required this.isMovable,
|
||||
required this.canBeBackedUp,
|
||||
required this.backupDescription,
|
||||
required this.status,
|
||||
required this.storageUsage,
|
||||
required this.svgIcon,
|
||||
|
@ -71,6 +79,8 @@ class Service {
|
|||
isEnabled: false,
|
||||
isRequired: false,
|
||||
isMovable: false,
|
||||
canBeBackedUp: false,
|
||||
backupDescription: '',
|
||||
status: ServiceStatus.off,
|
||||
storageUsage: ServiceStorageUsage(
|
||||
used: const DiskSize(byte: 0),
|
||||
|
@ -87,6 +97,8 @@ class Service {
|
|||
final bool isEnabled;
|
||||
final bool isRequired;
|
||||
final bool isMovable;
|
||||
final bool canBeBackedUp;
|
||||
final String backupDescription;
|
||||
final ServiceStatus status;
|
||||
final ServiceStorageUsage storageUsage;
|
||||
final String svgIcon;
|
||||
|
|
|
@ -5,7 +5,16 @@ import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
|||
export 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||
|
||||
abstract class DnsProvider {
|
||||
/// Returns an assigned enum value, respectively to which
|
||||
/// provider implements [DnsProvider] interface.
|
||||
DnsProviderType get type;
|
||||
|
||||
/// Tries to access an account linked to the provided token.
|
||||
///
|
||||
/// To generate a token for your account follow instructions of your
|
||||
/// DNS provider respectfully.
|
||||
///
|
||||
/// If success, saves it for future usage.
|
||||
Future<GenericResult<bool>> tryInitApiByToken(final String token);
|
||||
Future<GenericResult<String?>> getZoneId(final String domain);
|
||||
Future<GenericResult<void>> removeDomainRecords({
|
||||
|
|
|
@ -12,7 +12,12 @@ import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
|
|||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
|
||||
class JobsContent extends StatelessWidget {
|
||||
const JobsContent({super.key});
|
||||
const JobsContent({
|
||||
required this.controller,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final ScrollController controller;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
|
@ -119,9 +124,10 @@ class JobsContent extends StatelessWidget {
|
|||
];
|
||||
}
|
||||
return ListView(
|
||||
controller: controller,
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
const SizedBox(height: 15),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: Text(
|
||||
'jobs.title'.tr(),
|
||||
|
|
|
@ -62,7 +62,16 @@ class _BrandFabState extends State<BrandFab>
|
|||
// TODO: Make a hero animation to the screen
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (final BuildContext context) => const JobsContent(),
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) => DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.4,
|
||||
initialChildSize: 0.6,
|
||||
builder: (final context, final scrollController) =>
|
||||
JobsContent(controller: scrollController),
|
||||
),
|
||||
);
|
||||
},
|
||||
isExtended: widget.extended,
|
||||
|
|
|
@ -145,7 +145,17 @@ class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
|
|||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (final BuildContext context) => const JobsContent(),
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.4,
|
||||
initialChildSize: 0.6,
|
||||
builder: (final context, final scrollController) =>
|
||||
JobsContent(controller: scrollController),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/cards/outlined_card.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
|
||||
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
@RoutePage()
|
||||
class BackupDetailsPage extends StatefulWidget {
|
||||
const BackupDetailsPage({super.key});
|
||||
|
||||
@override
|
||||
State<BackupDetailsPage> createState() => _BackupDetailsPageState();
|
||||
}
|
||||
|
||||
class _BackupDetailsPageState extends State<BackupDetailsPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||
is ServerInstallationFinished;
|
||||
final bool isBackupInitialized =
|
||||
context.watch<BackupsCubit>().state.isInitialized;
|
||||
final BackupStatusEnum backupStatus =
|
||||
context.watch<BackupsCubit>().state.status;
|
||||
final StateType providerState = isReady && isBackupInitialized
|
||||
? (backupStatus == BackupStatusEnum.error
|
||||
? StateType.warning
|
||||
: StateType.stable)
|
||||
: StateType.uninitialized;
|
||||
final bool preventActions =
|
||||
context.watch<BackupsCubit>().state.preventActions;
|
||||
final double backupProgress = context.watch<BackupsCubit>().state.progress;
|
||||
final String backupError = context.watch<BackupsCubit>().state.error;
|
||||
final List<Backup> backups = context.watch<BackupsCubit>().state.backups;
|
||||
final bool refreshing = context.watch<BackupsCubit>().state.refreshing;
|
||||
|
||||
return BrandHeroScreen(
|
||||
heroIcon: BrandIcons.save,
|
||||
heroTitle: 'backup.card_title'.tr(),
|
||||
heroSubtitle: 'backup.description'.tr(),
|
||||
children: [
|
||||
if (isReady && !isBackupInitialized)
|
||||
BrandButton.rised(
|
||||
onPressed: preventActions
|
||||
? null
|
||||
: () async {
|
||||
await context.read<BackupsCubit>().createBucket();
|
||||
},
|
||||
text: 'backup.initialize'.tr(),
|
||||
),
|
||||
if (backupStatus == BackupStatusEnum.initializing)
|
||||
Text(
|
||||
'backup.waiting_for_rebuild'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
if (backupStatus != BackupStatusEnum.initializing &&
|
||||
backupStatus != BackupStatusEnum.noKey)
|
||||
OutlinedCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (backupStatus == BackupStatusEnum.initialized)
|
||||
ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () async {
|
||||
await context.read<BackupsCubit>().createBackup();
|
||||
},
|
||||
leading: const Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
),
|
||||
title: Text(
|
||||
'backup.create_new'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
if (backupStatus == BackupStatusEnum.backingUp)
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.creating'.tr(
|
||||
args: [(backupProgress * 100).round().toString()],
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
subtitle: LinearProgressIndicator(
|
||||
value: backupProgress,
|
||||
backgroundColor: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
if (backupStatus == BackupStatusEnum.restoring)
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.restoring'.tr(
|
||||
args: [(backupProgress * 100).round().toString()],
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
subtitle: LinearProgressIndicator(
|
||||
backgroundColor: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
if (backupStatus == BackupStatusEnum.error)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
Icons.error_outline,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
title: Text(
|
||||
'backup.error_pending'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Card with a list of existing backups
|
||||
// Each list item has a date
|
||||
// When clicked, starts the restore action
|
||||
if (backupStatus != BackupStatusEnum.initializing &&
|
||||
backupStatus != BackupStatusEnum.noKey)
|
||||
OutlinedCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.refresh,
|
||||
),
|
||||
title: Text(
|
||||
'backup.restore'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
if (backups.isEmpty)
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.error_outline,
|
||||
),
|
||||
title: Text('backup.no_backups'.tr()),
|
||||
),
|
||||
if (backups.isNotEmpty)
|
||||
Column(
|
||||
children: backups
|
||||
.map(
|
||||
(final Backup backup) => ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.restoring'.tr(),
|
||||
description: 'backup.restore_alert'.tr(
|
||||
args: [backup.time.toString()],
|
||||
),
|
||||
actionButtonTitle: 'modals.yes'.tr(),
|
||||
actionButtonOnPressed: () => {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.restoreBackup(backup.id)
|
||||
},
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}',
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
OutlinedCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.refresh'.tr(),
|
||||
),
|
||||
onTap: refreshing
|
||||
? null
|
||||
: () => {context.read<BackupsCubit>().updateBackups()},
|
||||
enabled: !refreshing,
|
||||
),
|
||||
if (providerState != StateType.uninitialized)
|
||||
Column(
|
||||
children: [
|
||||
const Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.refetch_backups'.tr(),
|
||||
),
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () => {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.forceUpdateBackups()
|
||||
},
|
||||
),
|
||||
const Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.reupload_key'.tr(),
|
||||
),
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () => {context.read<BackupsCubit>().reuploadKey()},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (backupStatus == BackupStatusEnum.error)
|
||||
Text(
|
||||
backupError.toString(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
308
lib/ui/pages/backups/backup_details.dart
Normal file
308
lib/ui/pages/backups/backup_details.dart
Normal file
|
@ -0,0 +1,308 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/buttons/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/change_period_modal.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/create_backups_modal.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BackupDetailsPage extends StatelessWidget {
|
||||
const BackupDetailsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||
is ServerInstallationFinished;
|
||||
final BackupsState backupsState = context.watch<BackupsCubit>().state;
|
||||
final bool isBackupInitialized = backupsState.isInitialized;
|
||||
final StateType providerState = isReady && isBackupInitialized
|
||||
? StateType.stable
|
||||
: StateType.uninitialized;
|
||||
final bool preventActions = backupsState.preventActions;
|
||||
final List<Backup> backups = backupsState.backups;
|
||||
final bool refreshing = backupsState.refreshing;
|
||||
final List<Service> services =
|
||||
context.watch<ServicesCubit>().state.servicesThatCanBeBackedUp;
|
||||
final Duration? autobackupPeriod = backupsState.autobackupPeriod;
|
||||
final List<ServerJob> backupJobs = context
|
||||
.watch<ServerJobsCubit>()
|
||||
.state
|
||||
.backupJobList
|
||||
.where((final job) => job.status != JobStatusEnum.finished)
|
||||
.toList();
|
||||
|
||||
if (!isReady) {
|
||||
return BrandHeroScreen(
|
||||
heroIcon: BrandIcons.save,
|
||||
heroTitle: 'backup.card_title'.tr(),
|
||||
heroSubtitle: 'not_ready_card.in_menu'.tr(),
|
||||
children: const [],
|
||||
);
|
||||
}
|
||||
|
||||
if (!isBackupInitialized) {
|
||||
return BrandHeroScreen(
|
||||
heroIcon: BrandIcons.save,
|
||||
heroTitle: 'backup.card_title'.tr(),
|
||||
heroSubtitle: 'backup.description'.tr(),
|
||||
children: [
|
||||
BrandButton.rised(
|
||||
onPressed: preventActions
|
||||
? null
|
||||
: () async {
|
||||
await context.read<BackupsCubit>().initializeBackups();
|
||||
},
|
||||
text: 'backup.initialize'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return BrandHeroScreen(
|
||||
heroIcon: BrandIcons.save,
|
||||
heroTitle: 'backup.card_title'.tr(),
|
||||
heroSubtitle: 'backup.description'.tr(),
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.4,
|
||||
initialChildSize: 0.6,
|
||||
builder: (final context, final scrollController) =>
|
||||
CreateBackupsModal(
|
||||
services: services,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
leading: const Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
),
|
||||
title: Text(
|
||||
'backup.create_new'.tr(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.4,
|
||||
initialChildSize: 0.6,
|
||||
builder: (final context, final scrollController) =>
|
||||
ChangeAutobackupsPeriodModal(
|
||||
scrollController: scrollController,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
leading: const Icon(
|
||||
Icons.manage_history_outlined,
|
||||
),
|
||||
title: Text(
|
||||
'backup.autobackup_period_title'.tr(),
|
||||
),
|
||||
subtitle: Text(
|
||||
autobackupPeriod != null
|
||||
? 'backup.autobackup_period_subtitle'.tr(
|
||||
namedArgs: {
|
||||
'period': autobackupPeriod.toPrettyString(context.locale)
|
||||
},
|
||||
)
|
||||
: 'backup.autobackup_period_never'.tr(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (backupJobs.isNotEmpty)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.pending_jobs'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final job in backupJobs)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: ServerJobCard(
|
||||
serverJob: job,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isBackupInitialized)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.latest_snapshots'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
'backup.latest_snapshots_subtitle'.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
),
|
||||
if (backups.isEmpty)
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
Icons.error_outline,
|
||||
),
|
||||
title: Text('backup.no_backups'.tr()),
|
||||
),
|
||||
if (backups.isNotEmpty)
|
||||
Column(
|
||||
children: backups.take(15).map(
|
||||
(final Backup backup) {
|
||||
final service = context
|
||||
.read<ServicesCubit>()
|
||||
.state
|
||||
.getServiceById(backup.serviceId);
|
||||
return ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.restoring'.tr(),
|
||||
description: 'backup.restore_alert'.tr(
|
||||
args: [backup.time.toString()],
|
||||
),
|
||||
actionButtonTitle: 'modals.yes'.tr(),
|
||||
actionButtonOnPressed: () => {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.restoreBackup(backup.id)
|
||||
},
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}',
|
||||
),
|
||||
subtitle: Text(
|
||||
service?.displayName ?? backup.fallbackServiceName,
|
||||
),
|
||||
leading: service != null
|
||||
? SvgPicture.string(
|
||||
service.svgIcon,
|
||||
height: 24,
|
||||
width: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
: const Icon(
|
||||
Icons.question_mark_outlined,
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
if (backups.isNotEmpty && backups.length > 15)
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.show_more'.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.arrow_drop_down,
|
||||
),
|
||||
onTap: () =>
|
||||
context.pushRoute(BackupsListRoute(service: null)),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.refresh'.tr(),
|
||||
),
|
||||
onTap: refreshing
|
||||
? null
|
||||
: () => {context.read<BackupsCubit>().updateBackups()},
|
||||
enabled: !refreshing,
|
||||
leading: const Icon(
|
||||
Icons.refresh_outlined,
|
||||
),
|
||||
),
|
||||
if (providerState != StateType.uninitialized)
|
||||
Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.refetch_backups'.tr(),
|
||||
),
|
||||
subtitle: Text(
|
||||
'backup.refetch_backups_subtitle'.tr(),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.cached_outlined,
|
||||
),
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () => {context.read<BackupsCubit>().forceUpdateBackups()},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(),
|
||||
const SizedBox(height: 8),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'backup.reupload_key'.tr(),
|
||||
),
|
||||
subtitle: Text(
|
||||
'backup.reupload_key_subtitle'.tr(),
|
||||
),
|
||||
leading: const Icon(
|
||||
Icons.warning_amber_outlined,
|
||||
),
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () => {context.read<BackupsCubit>().reuploadKey()},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
85
lib/ui/pages/backups/backups_list.dart
Normal file
85
lib/ui/pages/backups/backups_list.dart
Normal file
|
@ -0,0 +1,85 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/backup.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BackupsListPage extends StatelessWidget {
|
||||
const BackupsListPage({
|
||||
required this.service,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Service? service;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
// If the service is null, get all backups from state. If not null, call the
|
||||
// serviceBackups(serviceId) on the backups state.
|
||||
final List<Backup> backups = service == null
|
||||
? context.watch<BackupsCubit>().state.backups
|
||||
: context.watch<BackupsCubit>().state.serviceBackups(service!.id);
|
||||
final bool preventActions =
|
||||
context.watch<BackupsCubit>().state.preventActions;
|
||||
return BrandHeroScreen(
|
||||
heroTitle: 'backup.snapshots_title'.tr(),
|
||||
children: [
|
||||
if (backups.isEmpty)
|
||||
Center(
|
||||
child: Text(
|
||||
'backup.no_backups'.tr(),
|
||||
),
|
||||
)
|
||||
else
|
||||
...backups.map((final Backup backup) {
|
||||
final service = context
|
||||
.read<ServicesCubit>()
|
||||
.state
|
||||
.getServiceById(backup.serviceId);
|
||||
return ListTile(
|
||||
onTap: preventActions
|
||||
? null
|
||||
: () {
|
||||
showPopUpAlert(
|
||||
alertTitle: 'backup.restoring'.tr(),
|
||||
description: 'backup.restore_alert'.tr(
|
||||
args: [backup.time.toString()],
|
||||
),
|
||||
actionButtonTitle: 'modals.yes'.tr(),
|
||||
actionButtonOnPressed: () => {
|
||||
context.read<BackupsCubit>().restoreBackup(backup.id)
|
||||
},
|
||||
);
|
||||
},
|
||||
title: Text(
|
||||
'${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}',
|
||||
),
|
||||
subtitle: Text(
|
||||
service?.displayName ?? backup.fallbackServiceName,
|
||||
),
|
||||
leading: service != null
|
||||
? SvgPicture.string(
|
||||
service.svgIcon,
|
||||
height: 24,
|
||||
width: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
: const Icon(
|
||||
Icons.question_mark_outlined,
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
108
lib/ui/pages/backups/change_period_modal.dart
Normal file
108
lib/ui/pages/backups/change_period_modal.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||
|
||||
class ChangeAutobackupsPeriodModal extends StatefulWidget {
|
||||
const ChangeAutobackupsPeriodModal({
|
||||
required this.scrollController,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final ScrollController scrollController;
|
||||
|
||||
@override
|
||||
State<ChangeAutobackupsPeriodModal> createState() =>
|
||||
_ChangeAutobackupsPeriodModalState();
|
||||
}
|
||||
|
||||
class _ChangeAutobackupsPeriodModalState
|
||||
extends State<ChangeAutobackupsPeriodModal> {
|
||||
// This is a modal with radio buttons to select the autobackup period
|
||||
// Period might be none, selected from predefined list or custom
|
||||
// Store in state the selected period
|
||||
Duration? selectedPeriod;
|
||||
|
||||
static const List<Duration> autobackupPeriods = [
|
||||
Duration(hours: 12),
|
||||
Duration(days: 1),
|
||||
Duration(days: 2),
|
||||
Duration(days: 3),
|
||||
Duration(days: 7),
|
||||
];
|
||||
|
||||
// Set initial period to the one currently set
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedPeriod = context.read<BackupsCubit>().state.autobackupPeriod;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final Duration? initialAutobackupPeriod =
|
||||
context.watch<BackupsCubit>().state.autobackupPeriod;
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'backup.autobackup_period_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Select all services tile
|
||||
RadioListTile<Duration?>(
|
||||
onChanged: (final Duration? value) {
|
||||
setState(() {
|
||||
selectedPeriod = value;
|
||||
});
|
||||
},
|
||||
title: Text(
|
||||
'backup.autobackup_period_disable'.tr(),
|
||||
),
|
||||
value: null,
|
||||
groupValue: selectedPeriod,
|
||||
),
|
||||
const Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
...autobackupPeriods.map(
|
||||
(final Duration period) => RadioListTile<Duration?>(
|
||||
onChanged: (final Duration? value) {
|
||||
setState(() {
|
||||
selectedPeriod = value;
|
||||
});
|
||||
},
|
||||
title: Text(
|
||||
'backup.autobackup_period_every'.tr(
|
||||
namedArgs: {'period': period.toPrettyString(context.locale)},
|
||||
),
|
||||
),
|
||||
value: period,
|
||||
groupValue: selectedPeriod,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Create backup button
|
||||
FilledButton(
|
||||
onPressed: selectedPeriod == initialAutobackupPeriod
|
||||
? null
|
||||
: () {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.setAutobackupPeriod(selectedPeriod);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'backup.autobackup_set_period'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
161
lib/ui/pages/backups/create_backups_modal.dart
Normal file
161
lib/ui/pages/backups/create_backups_modal.dart
Normal file
|
@ -0,0 +1,161 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
|
||||
class CreateBackupsModal extends StatefulWidget {
|
||||
const CreateBackupsModal({
|
||||
required this.services,
|
||||
required this.scrollController,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<Service> services;
|
||||
final ScrollController scrollController;
|
||||
|
||||
@override
|
||||
State<CreateBackupsModal> createState() => _CreateBackupsModalState();
|
||||
}
|
||||
|
||||
class _CreateBackupsModalState extends State<CreateBackupsModal> {
|
||||
// Store in state the selected services to backup
|
||||
List<Service> selectedServices = [];
|
||||
|
||||
// Select all services on modal open
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final List<String> busyServices = context
|
||||
.read<ServerJobsCubit>()
|
||||
.state
|
||||
.backupJobList
|
||||
.where(
|
||||
(final ServerJob job) =>
|
||||
job.status == JobStatusEnum.running ||
|
||||
job.status == JobStatusEnum.created,
|
||||
)
|
||||
.map((final ServerJob job) => job.typeId.split('.')[1])
|
||||
.toList();
|
||||
selectedServices.addAll(
|
||||
widget.services
|
||||
.where((final Service service) => !busyServices.contains(service.id)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final List<String> busyServices = context
|
||||
.watch<ServerJobsCubit>()
|
||||
.state
|
||||
.backupJobList
|
||||
.where(
|
||||
(final ServerJob job) =>
|
||||
job.status == JobStatusEnum.running ||
|
||||
job.status == JobStatusEnum.created,
|
||||
)
|
||||
.map((final ServerJob job) => job.typeId.split('.')[1])
|
||||
.toList();
|
||||
|
||||
return ListView(
|
||||
controller: widget.scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'backup.create_new_select_heading'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Select all services tile
|
||||
CheckboxListTile(
|
||||
onChanged: (final bool? value) {
|
||||
setState(() {
|
||||
if (value ?? true) {
|
||||
setState(() {
|
||||
selectedServices.clear();
|
||||
selectedServices.addAll(
|
||||
widget.services.where(
|
||||
(final service) => !busyServices.contains(service.id),
|
||||
),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
selectedServices.clear();
|
||||
}
|
||||
});
|
||||
},
|
||||
title: Text(
|
||||
'backup.select_all'.tr(),
|
||||
),
|
||||
secondary: const Icon(
|
||||
Icons.checklist_outlined,
|
||||
),
|
||||
value: selectedServices.length >=
|
||||
widget.services.length - busyServices.length,
|
||||
),
|
||||
const Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
...widget.services.map(
|
||||
(final Service service) {
|
||||
final bool busy = busyServices.contains(service.id);
|
||||
return CheckboxListTile(
|
||||
onChanged: !busy
|
||||
? (final bool? value) {
|
||||
setState(() {
|
||||
if (value ?? true) {
|
||||
setState(() {
|
||||
selectedServices.add(service);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
selectedServices.remove(service);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
: null,
|
||||
title: Text(
|
||||
service.displayName,
|
||||
),
|
||||
subtitle: Text(
|
||||
busy ? 'backup.service_busy'.tr() : service.backupDescription,
|
||||
),
|
||||
secondary: SvgPicture.string(
|
||||
service.svgIcon,
|
||||
height: 24,
|
||||
width: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
busy
|
||||
? Theme.of(context).colorScheme.outlineVariant
|
||||
: Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
value: selectedServices.contains(service),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Create backup button
|
||||
FilledButton(
|
||||
onPressed: selectedServices.isEmpty
|
||||
? null
|
||||
: () {
|
||||
context
|
||||
.read<BackupsCubit>()
|
||||
.createMultipleBackups(selectedServices);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(
|
||||
'backup.start'.tr(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -44,11 +44,10 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
|||
),
|
||||
SwitchListTile(
|
||||
title: Text('developer_settings.ignore_tls'.tr()),
|
||||
subtitle:
|
||||
Text('developer_settings.ignore_tls_description'.tr()),
|
||||
subtitle: Text('developer_settings.ignore_tls_description'.tr()),
|
||||
value: TlsOptions.verifyCertificate,
|
||||
onChanged: (final bool value) => setState(
|
||||
() => TlsOptions.verifyCertificate = value,
|
||||
() => TlsOptions.verifyCertificate = value,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
|
|
|
@ -97,16 +97,15 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
// TODO: When backups are fixed, show this card
|
||||
if (isBackupInitialized)
|
||||
_Card(
|
||||
state: isBackupInitialized
|
||||
? StateType.stable
|
||||
: StateType.uninitialized,
|
||||
icon: BrandIcons.save,
|
||||
title: 'backup.card_title'.tr(),
|
||||
subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '',
|
||||
onTap: () => context.pushRoute(const BackupDetailsRoute()),
|
||||
),
|
||||
_Card(
|
||||
state: isBackupInitialized
|
||||
? StateType.stable
|
||||
: StateType.uninitialized,
|
||||
icon: BrandIcons.save,
|
||||
title: 'backup.card_title'.tr(),
|
||||
subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '',
|
||||
onTap: () => context.pushRoute(const BackupDetailsRoute()),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -107,7 +107,7 @@ class _SelectTimezoneState extends State<SelectTimezone> {
|
|||
Duration(
|
||||
milliseconds: location.currentTimeZone.offset,
|
||||
)
|
||||
.toDayHourMinuteFormat()
|
||||
.toTimezoneOffsetFormat()
|
||||
.contains(timezoneFilterValue!),
|
||||
)
|
||||
.toList()
|
||||
|
@ -137,7 +137,7 @@ class _SelectTimezoneState extends State<SelectTimezone> {
|
|||
location.name,
|
||||
),
|
||||
subtitle: Text(
|
||||
'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}',
|
||||
'GMT ${duration.toTimezoneOffsetFormat()} ${area.isNotEmpty ? '($area)' : ''}',
|
||||
),
|
||||
onTap: () {
|
||||
context.read<ServerDetailsCubit>().repository.setTimezone(
|
||||
|
|
|
@ -177,7 +177,17 @@ class _ServicesMigrationPageState extends State<ServicesMigrationPage> {
|
|||
context.router.popUntilRoot();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (final BuildContext context) => const JobsContent(),
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (final BuildContext context) =>
|
||||
DraggableScrollableSheet(
|
||||
expand: false,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.4,
|
||||
initialChildSize: 0.6,
|
||||
builder: (final context, final scrollController) =>
|
||||
JobsContent(controller: scrollController),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -141,6 +141,19 @@ class _ServicePageState extends State<ServicePage> {
|
|||
),
|
||||
enabled: !serviceDisabled && !serviceLocked,
|
||||
),
|
||||
if (service.canBeBackedUp)
|
||||
ListTile(
|
||||
iconColor: Theme.of(context).colorScheme.onBackground,
|
||||
// Open page ServicesMigrationPage
|
||||
onTap: () => context.pushRoute(
|
||||
BackupsListRoute(service: service),
|
||||
),
|
||||
leading: const Icon(Icons.settings_backup_restore_outlined),
|
||||
title: Text(
|
||||
'service_page.snapshots'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ import 'package:auto_route/auto_route.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/backup_details.dart';
|
||||
import 'package:selfprivacy/ui/pages/backups/backups_list.dart';
|
||||
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
||||
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/about_application.dart';
|
||||
|
@ -96,6 +97,7 @@ class RootRouter extends _$RootRouter {
|
|||
AutoRoute(page: ServerDetailsRoute.page),
|
||||
AutoRoute(page: DnsDetailsRoute.page),
|
||||
AutoRoute(page: BackupDetailsRoute.page),
|
||||
AutoRoute(page: BackupsListRoute.page),
|
||||
AutoRoute(page: ServerStorageRoute.page),
|
||||
AutoRoute(page: ExtendingVolumeRoute.page),
|
||||
],
|
||||
|
@ -141,6 +143,8 @@ String getRouteTitle(final String routeName) {
|
|||
return 'server.card_title';
|
||||
case 'BackupDetailsRoute':
|
||||
return 'backup.card_title';
|
||||
case 'BackupsListRoute':
|
||||
return 'backup.snapshots_title';
|
||||
case 'ServerStorageRoute':
|
||||
return 'storage.card_title';
|
||||
case 'ExtendingVolumeRoute':
|
||||
|
|
|
@ -15,16 +15,103 @@ abstract class _$RootRouter extends RootStackRouter {
|
|||
|
||||
@override
|
||||
final Map<String, PageFactory> pagesMap = {
|
||||
BackupDetailsRoute.name: (routeData) {
|
||||
DevicesRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const BackupDetailsPage(),
|
||||
child: const DevicesScreen(),
|
||||
);
|
||||
},
|
||||
RootRoute.name: (routeData) {
|
||||
DnsDetailsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: WrappedRoute(child: const RootPage()),
|
||||
child: const DnsDetailsPage(),
|
||||
);
|
||||
},
|
||||
AppSettingsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AppSettingsPage(),
|
||||
);
|
||||
},
|
||||
DeveloperSettingsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DeveloperSettingsPage(),
|
||||
);
|
||||
},
|
||||
AboutApplicationRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AboutApplicationPage(),
|
||||
);
|
||||
},
|
||||
MoreRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const MorePage(),
|
||||
);
|
||||
},
|
||||
ConsoleRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ConsolePage(),
|
||||
);
|
||||
},
|
||||
OnboardingRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const OnboardingPage(),
|
||||
);
|
||||
},
|
||||
ProvidersRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ProvidersPage(),
|
||||
);
|
||||
},
|
||||
RecoveryKeyRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryKeyPage(),
|
||||
);
|
||||
},
|
||||
ServerDetailsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ServerDetailsScreen(),
|
||||
);
|
||||
},
|
||||
ServicesMigrationRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServicesMigrationRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServicesMigrationPage(
|
||||
services: args.services,
|
||||
diskStatus: args.diskStatus,
|
||||
isMigration: args.isMigration,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ServerStorageRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServerStorageRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServerStoragePage(
|
||||
diskStatus: args.diskStatus,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ExtendingVolumeRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ExtendingVolumeRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ExtendingVolumePage(
|
||||
diskVolumeToResize: args.diskVolumeToResize,
|
||||
diskStatus: args.diskStatus,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ServiceRoute.name: (routeData) {
|
||||
|
@ -43,10 +130,16 @@ abstract class _$RootRouter extends RootStackRouter {
|
|||
child: const ServicesPage(),
|
||||
);
|
||||
},
|
||||
ServerDetailsRoute.name: (routeData) {
|
||||
InitializingRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ServerDetailsScreen(),
|
||||
child: const InitializingPage(),
|
||||
);
|
||||
},
|
||||
RecoveryRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryRouting(),
|
||||
);
|
||||
},
|
||||
UsersRoute.name: (routeData) {
|
||||
|
@ -71,274 +164,59 @@ abstract class _$RootRouter extends RootStackRouter {
|
|||
),
|
||||
);
|
||||
},
|
||||
AppSettingsRoute.name: (routeData) {
|
||||
RootRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AppSettingsPage(),
|
||||
child: WrappedRoute(child: const RootPage()),
|
||||
);
|
||||
},
|
||||
DeveloperSettingsRoute.name: (routeData) {
|
||||
BackupDetailsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DeveloperSettingsPage(),
|
||||
child: const BackupDetailsPage(),
|
||||
);
|
||||
},
|
||||
MoreRoute.name: (routeData) {
|
||||
BackupsListRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<BackupsListRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const MorePage(),
|
||||
);
|
||||
},
|
||||
AboutApplicationRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AboutApplicationPage(),
|
||||
);
|
||||
},
|
||||
ConsoleRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ConsolePage(),
|
||||
);
|
||||
},
|
||||
ProvidersRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ProvidersPage(),
|
||||
);
|
||||
},
|
||||
RecoveryKeyRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryKeyPage(),
|
||||
);
|
||||
},
|
||||
DnsDetailsRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DnsDetailsPage(),
|
||||
);
|
||||
},
|
||||
RecoveryRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryRouting(),
|
||||
);
|
||||
},
|
||||
InitializingRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const InitializingPage(),
|
||||
);
|
||||
},
|
||||
ServerStorageRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServerStorageRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServerStoragePage(
|
||||
diskStatus: args.diskStatus,
|
||||
child: BackupsListPage(
|
||||
service: args.service,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ExtendingVolumeRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ExtendingVolumeRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ExtendingVolumePage(
|
||||
diskVolumeToResize: args.diskVolumeToResize,
|
||||
diskStatus: args.diskStatus,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ServicesMigrationRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServicesMigrationRouteArgs>();
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServicesMigrationPage(
|
||||
services: args.services,
|
||||
diskStatus: args.diskStatus,
|
||||
isMigration: args.isMigration,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
DevicesRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DevicesScreen(),
|
||||
);
|
||||
},
|
||||
OnboardingRoute.name: (routeData) {
|
||||
return AutoRoutePage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const OnboardingPage(),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [BackupDetailsPage]
|
||||
class BackupDetailsRoute extends PageRouteInfo<void> {
|
||||
const BackupDetailsRoute({List<PageRouteInfo>? children})
|
||||
/// [DevicesScreen]
|
||||
class DevicesRoute extends PageRouteInfo<void> {
|
||||
const DevicesRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
BackupDetailsRoute.name,
|
||||
DevicesRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'BackupDetailsRoute';
|
||||
static const String name = 'DevicesRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RootPage]
|
||||
class RootRoute extends PageRouteInfo<void> {
|
||||
const RootRoute({List<PageRouteInfo>? children})
|
||||
/// [DnsDetailsPage]
|
||||
class DnsDetailsRoute extends PageRouteInfo<void> {
|
||||
const DnsDetailsRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
RootRoute.name,
|
||||
DnsDetailsRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'RootRoute';
|
||||
static const String name = 'DnsDetailsRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicePage]
|
||||
class ServiceRoute extends PageRouteInfo<ServiceRouteArgs> {
|
||||
ServiceRoute({
|
||||
required String serviceId,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServiceRoute.name,
|
||||
args: ServiceRouteArgs(
|
||||
serviceId: serviceId,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServiceRoute';
|
||||
|
||||
static const PageInfo<ServiceRouteArgs> page =
|
||||
PageInfo<ServiceRouteArgs>(name);
|
||||
}
|
||||
|
||||
class ServiceRouteArgs {
|
||||
const ServiceRouteArgs({
|
||||
required this.serviceId,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String serviceId;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServiceRouteArgs{serviceId: $serviceId, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicesPage]
|
||||
class ServicesRoute extends PageRouteInfo<void> {
|
||||
const ServicesRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
ServicesRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServicesRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServerDetailsScreen]
|
||||
class ServerDetailsRoute extends PageRouteInfo<void> {
|
||||
const ServerDetailsRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
ServerDetailsRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServerDetailsRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UsersPage]
|
||||
class UsersRoute extends PageRouteInfo<void> {
|
||||
const UsersRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
UsersRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'UsersRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [NewUserPage]
|
||||
class NewUserRoute extends PageRouteInfo<void> {
|
||||
const NewUserRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
NewUserRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'NewUserRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UserDetailsPage]
|
||||
class UserDetailsRoute extends PageRouteInfo<UserDetailsRouteArgs> {
|
||||
UserDetailsRoute({
|
||||
required String login,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
UserDetailsRoute.name,
|
||||
args: UserDetailsRouteArgs(
|
||||
login: login,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'UserDetailsRoute';
|
||||
|
||||
static const PageInfo<UserDetailsRouteArgs> page =
|
||||
PageInfo<UserDetailsRouteArgs>(name);
|
||||
}
|
||||
|
||||
class UserDetailsRouteArgs {
|
||||
const UserDetailsRouteArgs({
|
||||
required this.login,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String login;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserDetailsRouteArgs{login: $login, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AppSettingsPage]
|
||||
class AppSettingsRoute extends PageRouteInfo<void> {
|
||||
|
@ -367,20 +245,6 @@ class DeveloperSettingsRoute extends PageRouteInfo<void> {
|
|||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [MorePage]
|
||||
class MoreRoute extends PageRouteInfo<void> {
|
||||
const MoreRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
MoreRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'MoreRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AboutApplicationPage]
|
||||
class AboutApplicationRoute extends PageRouteInfo<void> {
|
||||
|
@ -395,6 +259,20 @@ class AboutApplicationRoute extends PageRouteInfo<void> {
|
|||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [MorePage]
|
||||
class MoreRoute extends PageRouteInfo<void> {
|
||||
const MoreRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
MoreRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'MoreRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ConsolePage]
|
||||
class ConsoleRoute extends PageRouteInfo<void> {
|
||||
|
@ -409,6 +287,20 @@ class ConsoleRoute extends PageRouteInfo<void> {
|
|||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [OnboardingPage]
|
||||
class OnboardingRoute extends PageRouteInfo<void> {
|
||||
const OnboardingRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
OnboardingRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'OnboardingRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ProvidersPage]
|
||||
class ProvidersRoute extends PageRouteInfo<void> {
|
||||
|
@ -438,45 +330,65 @@ class RecoveryKeyRoute extends PageRouteInfo<void> {
|
|||
}
|
||||
|
||||
/// generated route for
|
||||
/// [DnsDetailsPage]
|
||||
class DnsDetailsRoute extends PageRouteInfo<void> {
|
||||
const DnsDetailsRoute({List<PageRouteInfo>? children})
|
||||
/// [ServerDetailsScreen]
|
||||
class ServerDetailsRoute extends PageRouteInfo<void> {
|
||||
const ServerDetailsRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
DnsDetailsRoute.name,
|
||||
ServerDetailsRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'DnsDetailsRoute';
|
||||
static const String name = 'ServerDetailsRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RecoveryRouting]
|
||||
class RecoveryRoute extends PageRouteInfo<void> {
|
||||
const RecoveryRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
RecoveryRoute.name,
|
||||
/// [ServicesMigrationPage]
|
||||
class ServicesMigrationRoute extends PageRouteInfo<ServicesMigrationRouteArgs> {
|
||||
ServicesMigrationRoute({
|
||||
required List<Service> services,
|
||||
required DiskStatus diskStatus,
|
||||
required bool isMigration,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServicesMigrationRoute.name,
|
||||
args: ServicesMigrationRouteArgs(
|
||||
services: services,
|
||||
diskStatus: diskStatus,
|
||||
isMigration: isMigration,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'RecoveryRoute';
|
||||
static const String name = 'ServicesMigrationRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
static const PageInfo<ServicesMigrationRouteArgs> page =
|
||||
PageInfo<ServicesMigrationRouteArgs>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [InitializingPage]
|
||||
class InitializingRoute extends PageRouteInfo<void> {
|
||||
const InitializingRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
InitializingRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
class ServicesMigrationRouteArgs {
|
||||
const ServicesMigrationRouteArgs({
|
||||
required this.services,
|
||||
required this.diskStatus,
|
||||
required this.isMigration,
|
||||
this.key,
|
||||
});
|
||||
|
||||
static const String name = 'InitializingRoute';
|
||||
final List<Service> services;
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
final DiskStatus diskStatus;
|
||||
|
||||
final bool isMigration;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServicesMigrationRouteArgs{services: $services, diskStatus: $diskStatus, isMigration: $isMigration, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
|
@ -561,77 +473,213 @@ class ExtendingVolumeRouteArgs {
|
|||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicesMigrationPage]
|
||||
class ServicesMigrationRoute extends PageRouteInfo<ServicesMigrationRouteArgs> {
|
||||
ServicesMigrationRoute({
|
||||
required List<Service> services,
|
||||
required DiskStatus diskStatus,
|
||||
required bool isMigration,
|
||||
/// [ServicePage]
|
||||
class ServiceRoute extends PageRouteInfo<ServiceRouteArgs> {
|
||||
ServiceRoute({
|
||||
required String serviceId,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ServicesMigrationRoute.name,
|
||||
args: ServicesMigrationRouteArgs(
|
||||
services: services,
|
||||
diskStatus: diskStatus,
|
||||
isMigration: isMigration,
|
||||
ServiceRoute.name,
|
||||
args: ServiceRouteArgs(
|
||||
serviceId: serviceId,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ServicesMigrationRoute';
|
||||
static const String name = 'ServiceRoute';
|
||||
|
||||
static const PageInfo<ServicesMigrationRouteArgs> page =
|
||||
PageInfo<ServicesMigrationRouteArgs>(name);
|
||||
static const PageInfo<ServiceRouteArgs> page =
|
||||
PageInfo<ServiceRouteArgs>(name);
|
||||
}
|
||||
|
||||
class ServicesMigrationRouteArgs {
|
||||
const ServicesMigrationRouteArgs({
|
||||
required this.services,
|
||||
required this.diskStatus,
|
||||
required this.isMigration,
|
||||
class ServiceRouteArgs {
|
||||
const ServiceRouteArgs({
|
||||
required this.serviceId,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final List<Service> services;
|
||||
|
||||
final DiskStatus diskStatus;
|
||||
|
||||
final bool isMigration;
|
||||
final String serviceId;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServicesMigrationRouteArgs{services: $services, diskStatus: $diskStatus, isMigration: $isMigration, key: $key}';
|
||||
return 'ServiceRouteArgs{serviceId: $serviceId, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [DevicesScreen]
|
||||
class DevicesRoute extends PageRouteInfo<void> {
|
||||
const DevicesRoute({List<PageRouteInfo>? children})
|
||||
/// [ServicesPage]
|
||||
class ServicesRoute extends PageRouteInfo<void> {
|
||||
const ServicesRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
DevicesRoute.name,
|
||||
ServicesRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'DevicesRoute';
|
||||
static const String name = 'ServicesRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [OnboardingPage]
|
||||
class OnboardingRoute extends PageRouteInfo<void> {
|
||||
const OnboardingRoute({List<PageRouteInfo>? children})
|
||||
/// [InitializingPage]
|
||||
class InitializingRoute extends PageRouteInfo<void> {
|
||||
const InitializingRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
OnboardingRoute.name,
|
||||
InitializingRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'OnboardingRoute';
|
||||
static const String name = 'InitializingRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RecoveryRouting]
|
||||
class RecoveryRoute extends PageRouteInfo<void> {
|
||||
const RecoveryRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
RecoveryRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'RecoveryRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UsersPage]
|
||||
class UsersRoute extends PageRouteInfo<void> {
|
||||
const UsersRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
UsersRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'UsersRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [NewUserPage]
|
||||
class NewUserRoute extends PageRouteInfo<void> {
|
||||
const NewUserRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
NewUserRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'NewUserRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UserDetailsPage]
|
||||
class UserDetailsRoute extends PageRouteInfo<UserDetailsRouteArgs> {
|
||||
UserDetailsRoute({
|
||||
required String login,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
UserDetailsRoute.name,
|
||||
args: UserDetailsRouteArgs(
|
||||
login: login,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'UserDetailsRoute';
|
||||
|
||||
static const PageInfo<UserDetailsRouteArgs> page =
|
||||
PageInfo<UserDetailsRouteArgs>(name);
|
||||
}
|
||||
|
||||
class UserDetailsRouteArgs {
|
||||
const UserDetailsRouteArgs({
|
||||
required this.login,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String login;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserDetailsRouteArgs{login: $login, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RootPage]
|
||||
class RootRoute extends PageRouteInfo<void> {
|
||||
const RootRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
RootRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'RootRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [BackupDetailsPage]
|
||||
class BackupDetailsRoute extends PageRouteInfo<void> {
|
||||
const BackupDetailsRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
BackupDetailsRoute.name,
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'BackupDetailsRoute';
|
||||
|
||||
static const PageInfo<void> page = PageInfo<void>(name);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [BackupsListPage]
|
||||
class BackupsListRoute extends PageRouteInfo<BackupsListRouteArgs> {
|
||||
BackupsListRoute({
|
||||
required Service? service,
|
||||
Key? key,
|
||||
List<PageRouteInfo>? children,
|
||||
}) : super(
|
||||
BackupsListRoute.name,
|
||||
args: BackupsListRouteArgs(
|
||||
service: service,
|
||||
key: key,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'BackupsListRoute';
|
||||
|
||||
static const PageInfo<BackupsListRouteArgs> page =
|
||||
PageInfo<BackupsListRouteArgs>(name);
|
||||
}
|
||||
|
||||
class BackupsListRouteArgs {
|
||||
const BackupsListRouteArgs({
|
||||
required this.service,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final Service? service;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackupsListRouteArgs{service: $service, key: $key}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
// ignore_for_file: unnecessary_this
|
||||
|
||||
extension DurationFormatter on Duration {
|
||||
String toDayHourMinuteSecondFormat() => [
|
||||
this.inHours.remainder(24),
|
||||
this.inMinutes.remainder(60),
|
||||
this.inSeconds.remainder(60)
|
||||
].map((final int seg) => seg.toString().padLeft(2, '0')).join(':');
|
||||
import 'dart:ui';
|
||||
|
||||
String toDayHourMinuteFormat() {
|
||||
import 'package:duration/duration.dart';
|
||||
import 'package:duration/locale.dart';
|
||||
|
||||
extension DurationFormatter on Duration {
|
||||
String toTimezoneOffsetFormat() {
|
||||
final designator = this >= Duration.zero ? '+' : '-';
|
||||
|
||||
final Iterable<String> segments = [
|
||||
|
@ -18,15 +17,10 @@ extension DurationFormatter on Duration {
|
|||
return '$designator${segments.first}:${segments.last}';
|
||||
}
|
||||
|
||||
// WAT: https://flutterigniter.com/how-to-format-duration/
|
||||
String toHoursMinutesSecondsFormat() =>
|
||||
this.toString().split('.').first.padLeft(8, '0');
|
||||
|
||||
String toDayHourMinuteFormat2() {
|
||||
final Iterable<String> segments = [
|
||||
this.inHours.remainder(24),
|
||||
this.inMinutes.remainder(60),
|
||||
].map((final int seg) => seg.toString().padLeft(2, '0'));
|
||||
return '${segments.first} h ${segments.last} min';
|
||||
}
|
||||
String toPrettyString(final Locale locale) =>
|
||||
prettyDuration(this, locale: getDurationLocale(locale));
|
||||
}
|
||||
|
||||
DurationLocale getDurationLocale(final Locale locale) =>
|
||||
DurationLocale.fromLanguageCode(locale.languageCode) ??
|
||||
const EnglishDurationLocale();
|
||||
|
|
|
@ -3,12 +3,3 @@ import 'package:flutter/material.dart';
|
|||
Route materialRoute(final Widget widget) => MaterialPageRoute(
|
||||
builder: (final BuildContext context) => widget,
|
||||
);
|
||||
|
||||
Route noAnimationRoute(final Widget widget) => PageRouteBuilder(
|
||||
pageBuilder: (
|
||||
final BuildContext context,
|
||||
final Animation<double> animation1,
|
||||
final Animation<double> animation2,
|
||||
) =>
|
||||
widget,
|
||||
);
|
||||
|
|
|
@ -297,6 +297,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.2"
|
||||
duration:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: duration
|
||||
sha256: d0b29d0a345429e3986ac56d60e4aef65b37d11e653022b2b9a4b361332b777f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.12"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -15,6 +15,7 @@ dependencies:
|
|||
cubit_form: ^2.0.1
|
||||
device_info_plus: ^9.0.2
|
||||
dio: ^5.1.2
|
||||
duration: 3.0.12
|
||||
dynamic_color: ^1.6.5
|
||||
easy_localization: ^3.0.2
|
||||
either_option: ^2.0.1-dev.1
|
||||
|
|
Loading…
Reference in a new issue