2023-01-23 11:15:05 +00:00
|
|
|
"""Backup"""
|
2024-06-10 01:52:26 +00:00
|
|
|
|
2023-01-23 11:15:05 +00:00
|
|
|
# pylint: disable=too-few-public-methods
|
|
|
|
import typing
|
|
|
|
import strawberry
|
2023-05-29 16:12:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
from selfprivacy_api.backup import Backups
|
|
|
|
from selfprivacy_api.backup.local_secret import LocalBackupSecret
|
2024-07-26 11:23:41 +00:00
|
|
|
from selfprivacy_api.backup.tasks import which_snapshots_to_full_restore
|
2023-05-29 16:12:22 +00:00
|
|
|
from selfprivacy_api.graphql.queries.providers import BackupProvider
|
2023-06-13 23:52:10 +00:00
|
|
|
from selfprivacy_api.graphql.common_types.service import (
|
|
|
|
Service,
|
|
|
|
ServiceStatusEnum,
|
|
|
|
SnapshotInfo,
|
|
|
|
service_to_graphql_service,
|
|
|
|
)
|
2023-08-28 18:24:29 +00:00
|
|
|
from selfprivacy_api.graphql.common_types.backup import AutobackupQuotas
|
2024-07-24 15:15:31 +00:00
|
|
|
from selfprivacy_api.services import ServiceManager
|
2024-07-26 11:23:41 +00:00
|
|
|
from selfprivacy_api.models.backup.snapshot import Snapshot
|
2023-05-29 16:12:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
@strawberry.type
|
|
|
|
class BackupConfiguration:
|
|
|
|
provider: BackupProvider
|
2023-06-13 23:52:10 +00:00
|
|
|
# When server is lost, the app should have the key to decrypt backups
|
|
|
|
# on a new server
|
2023-05-29 16:12:22 +00:00
|
|
|
encryption_key: str
|
2023-06-13 20:54:02 +00:00
|
|
|
# False when repo is not initialized and not ready to be used
|
|
|
|
is_initialized: bool
|
2023-05-29 16:12:22 +00:00
|
|
|
# If none, autobackups are disabled
|
2023-06-13 21:43:01 +00:00
|
|
|
autobackup_period: typing.Optional[int]
|
2023-08-28 18:24:29 +00:00
|
|
|
# None is equal to all quotas being unlimited (-1). Optional for compatibility reasons.
|
2023-08-30 09:03:19 +00:00
|
|
|
autobackup_quotas: AutobackupQuotas
|
2023-05-29 16:12:22 +00:00
|
|
|
# Bucket name for Backblaze, path for some other providers
|
2023-06-13 21:43:01 +00:00
|
|
|
location_name: typing.Optional[str]
|
|
|
|
location_id: typing.Optional[str]
|
2023-01-23 11:15:05 +00:00
|
|
|
|
|
|
|
|
2024-05-24 12:30:27 +00:00
|
|
|
# TODO: Ideally this should not be done in API but making an internal Service requires more work
|
|
|
|
# than to make an API record about a service
|
|
|
|
def tombstone_service(service_id: str) -> Service:
|
|
|
|
return Service(
|
|
|
|
id=service_id,
|
|
|
|
display_name=f"{service_id} (Orphaned)",
|
|
|
|
description="",
|
|
|
|
svg_icon="",
|
|
|
|
is_movable=False,
|
|
|
|
is_required=False,
|
|
|
|
is_enabled=False,
|
|
|
|
status=ServiceStatusEnum.OFF,
|
|
|
|
url=None,
|
|
|
|
can_be_backed_up=False,
|
|
|
|
backup_description="",
|
2024-07-26 19:59:32 +00:00
|
|
|
is_installed=False,
|
2024-05-24 12:30:27 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2024-07-26 11:23:41 +00:00
|
|
|
def snapshot_to_api(snap: Snapshot):
|
|
|
|
api_service = None
|
|
|
|
service = ServiceManager.get_service_by_id(snap.service_name)
|
|
|
|
|
|
|
|
if service is None:
|
|
|
|
api_service = tombstone_service(snap.service_name)
|
|
|
|
else:
|
|
|
|
api_service = service_to_graphql_service(service)
|
|
|
|
if api_service is None:
|
|
|
|
raise NotImplementedError(
|
|
|
|
f"Could not construct API Service record for:{snap.service_name}. This should be unreachable and is a bug if you see it."
|
|
|
|
)
|
|
|
|
return SnapshotInfo(
|
|
|
|
id=snap.id,
|
|
|
|
service=api_service,
|
|
|
|
created_at=snap.created_at,
|
|
|
|
reason=snap.reason,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-01-23 11:15:05 +00:00
|
|
|
@strawberry.type
|
|
|
|
class Backup:
|
2023-05-29 16:12:22 +00:00
|
|
|
@strawberry.field
|
2023-06-13 21:43:01 +00:00
|
|
|
def configuration(self) -> BackupConfiguration:
|
|
|
|
return BackupConfiguration(
|
2023-06-23 11:36:16 +00:00
|
|
|
provider=Backups.provider().name,
|
2023-06-13 22:40:53 +00:00
|
|
|
encryption_key=LocalBackupSecret.get(),
|
2023-06-13 21:43:01 +00:00
|
|
|
is_initialized=Backups.is_initted(),
|
|
|
|
autobackup_period=Backups.autobackup_period_minutes(),
|
|
|
|
location_name=Backups.provider().location,
|
|
|
|
location_id=Backups.provider().repo_id,
|
2023-08-28 18:24:29 +00:00
|
|
|
autobackup_quotas=Backups.autobackup_quotas(),
|
2023-06-13 21:43:01 +00:00
|
|
|
)
|
2023-01-23 11:15:05 +00:00
|
|
|
|
|
|
|
@strawberry.field
|
2023-05-29 16:12:22 +00:00
|
|
|
def all_snapshots(self) -> typing.List[SnapshotInfo]:
|
2023-06-13 23:52:10 +00:00
|
|
|
if not Backups.is_initted():
|
|
|
|
return []
|
2023-06-05 11:36:58 +00:00
|
|
|
snapshots = Backups.get_all_snapshots()
|
2024-07-26 11:23:41 +00:00
|
|
|
return [snapshot_to_api(snap) for snap in snapshots]
|
2024-05-24 12:30:27 +00:00
|
|
|
|
2024-07-26 11:23:41 +00:00
|
|
|
# A query for seeing which snapshots will be restored when migrating
|
|
|
|
@strawberry.field
|
|
|
|
def last_slice(self) -> typing.List[SnapshotInfo]:
|
|
|
|
if not Backups.is_initted():
|
|
|
|
return []
|
|
|
|
result = [snapshot_to_api(snap) for snap in which_snapshots_to_full_restore()]
|