mirror of
https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git
synced 2025-01-11 18:39:30 +00:00
Merge remote-tracking branch 'upstream/restic-rewrite-api' into restic-rewrite
This commit is contained in:
commit
8554879dc2
|
@ -15,13 +15,13 @@ redis = RedisPool().get_connection()
|
||||||
|
|
||||||
class LocalBackupSecret:
|
class LocalBackupSecret:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get():
|
def get() -> str:
|
||||||
"""A secret string which backblaze/other clouds do not know.
|
"""A secret string which backblaze/other clouds do not know.
|
||||||
Serves as encryption key.
|
Serves as encryption key.
|
||||||
"""
|
"""
|
||||||
if not LocalBackupSecret.exists():
|
if not LocalBackupSecret.exists():
|
||||||
LocalBackupSecret.reset()
|
LocalBackupSecret.reset()
|
||||||
return redis.get(REDIS_KEY)
|
return redis.get(REDIS_KEY) # type: ignore
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set(secret: str):
|
def set(secret: str):
|
||||||
|
@ -38,7 +38,7 @@ class LocalBackupSecret:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def exists() -> bool:
|
def exists() -> bool:
|
||||||
return redis.exists(REDIS_KEY)
|
return redis.exists(REDIS_KEY) == 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate() -> str:
|
def _generate() -> str:
|
||||||
|
|
|
@ -4,3 +4,5 @@ from selfprivacy_api.backup.restic_backuper import ResticBackuper
|
||||||
|
|
||||||
class Backblaze(AbstractBackupProvider):
|
class Backblaze(AbstractBackupProvider):
|
||||||
backuper = ResticBackuper("--b2-account", "--b2-key", ":b2:")
|
backuper = ResticBackuper("--b2-account", "--b2-key", ":b2:")
|
||||||
|
|
||||||
|
name = "BACKBLAZE"
|
||||||
|
|
|
@ -5,6 +5,8 @@ from selfprivacy_api.backup.restic_backuper import ResticBackuper
|
||||||
class LocalFileBackup(AbstractBackupProvider):
|
class LocalFileBackup(AbstractBackupProvider):
|
||||||
backuper = ResticBackuper("", "", "memory")
|
backuper = ResticBackuper("", "", "memory")
|
||||||
|
|
||||||
|
name = "FILE"
|
||||||
|
|
||||||
# login and key args are for compatibility with generic provider methods. They are ignored.
|
# login and key args are for compatibility with generic provider methods. They are ignored.
|
||||||
def __init__(self, filename: str, login: str = "", key: str = ""):
|
def __init__(self, filename: str, login: str = "", key: str = ""):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
|
@ -4,3 +4,5 @@ from selfprivacy_api.backup.restic_backuper import ResticBackuper
|
||||||
|
|
||||||
class InMemoryBackup(AbstractBackupProvider):
|
class InMemoryBackup(AbstractBackupProvider):
|
||||||
backuper = ResticBackuper("", "", ":memory:")
|
backuper = ResticBackuper("", "", ":memory:")
|
||||||
|
|
||||||
|
name = "MEMORY"
|
||||||
|
|
|
@ -12,6 +12,8 @@ class AbstractBackupProvider(ABC):
|
||||||
def backuper(self) -> AbstractBackuper:
|
def backuper(self) -> AbstractBackuper:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
name = "NONE"
|
||||||
|
|
||||||
def __init__(self, login="", key="", location="", repo_id=""):
|
def __init__(self, login="", key="", location="", repo_id=""):
|
||||||
self.backuper.set_creds(login, key, location)
|
self.backuper.set_creds(login, key, location)
|
||||||
self.login = login
|
self.login = login
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import datetime
|
|
||||||
import strawberry
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
|
||||||
class SnapshotInfo:
|
|
||||||
id: str
|
|
||||||
service_name: str
|
|
||||||
created_at: datetime.datetime
|
|
|
@ -3,7 +3,6 @@ import typing
|
||||||
import strawberry
|
import strawberry
|
||||||
import datetime
|
import datetime
|
||||||
from selfprivacy_api.graphql.common_types.dns import DnsRecord
|
from selfprivacy_api.graphql.common_types.dns import DnsRecord
|
||||||
from selfprivacy_api.graphql.common_types.backup_snapshot import SnapshotInfo
|
|
||||||
|
|
||||||
from selfprivacy_api.services import get_service_by_id, get_services_by_location
|
from selfprivacy_api.services import get_service_by_id, get_services_by_location
|
||||||
from selfprivacy_api.services import Service as ServiceInterface
|
from selfprivacy_api.services import Service as ServiceInterface
|
||||||
|
@ -104,14 +103,14 @@ class Service:
|
||||||
return get_storage_usage(self)
|
return get_storage_usage(self)
|
||||||
|
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
def backup_snapshots(self) -> typing.Optional[typing.List[SnapshotInfo]]:
|
def backup_snapshots(self) -> typing.Optional[typing.List["SnapshotInfo"]]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class SnapshotInfo:
|
class SnapshotInfo:
|
||||||
id: str
|
id: str
|
||||||
service: "Service"
|
service: Service
|
||||||
created_at: datetime.datetime
|
created_at: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,41 +7,70 @@ import strawberry
|
||||||
from selfprivacy_api.backup import Backups
|
from selfprivacy_api.backup import Backups
|
||||||
from selfprivacy_api.backup.local_secret import LocalBackupSecret
|
from selfprivacy_api.backup.local_secret import LocalBackupSecret
|
||||||
from selfprivacy_api.graphql.queries.providers import BackupProvider
|
from selfprivacy_api.graphql.queries.providers import BackupProvider
|
||||||
from selfprivacy_api.graphql.common_types.service import SnapshotInfo
|
from selfprivacy_api.graphql.common_types.service import (
|
||||||
|
Service,
|
||||||
|
ServiceStatusEnum,
|
||||||
|
SnapshotInfo,
|
||||||
|
service_to_graphql_service,
|
||||||
|
)
|
||||||
|
from selfprivacy_api.services import get_service_by_id
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class BackupConfiguration:
|
class BackupConfiguration:
|
||||||
provider: BackupProvider
|
provider: BackupProvider
|
||||||
# When server is lost, the app should have the key to decrypt backups on a new server
|
# When server is lost, the app should have the key to decrypt backups
|
||||||
|
# on a new server
|
||||||
encryption_key: str
|
encryption_key: str
|
||||||
# False when repo is not initialized and not ready to be used
|
# False when repo is not initialized and not ready to be used
|
||||||
is_initialized: bool
|
is_initialized: bool
|
||||||
# If none, autobackups are disabled
|
# If none, autobackups are disabled
|
||||||
autobackup_period: typing.Optional[int] = None
|
autobackup_period: typing.Optional[int]
|
||||||
# Bucket name for Backblaze, path for some other providers
|
# Bucket name for Backblaze, path for some other providers
|
||||||
location_name: typing.Optional[str] = None
|
location_name: typing.Optional[str]
|
||||||
location_id: typing.Optional[str] = None
|
location_id: typing.Optional[str]
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class Backup:
|
class Backup:
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
def configuration() -> BackupConfiguration:
|
def configuration(self) -> BackupConfiguration:
|
||||||
config = BackupConfiguration()
|
return BackupConfiguration(
|
||||||
config.encryption_key = LocalBackupSecret.get()
|
provider=BackupProvider[Backups.provider().name],
|
||||||
config.is_initialized = Backups.is_initted()
|
encryption_key=LocalBackupSecret.get(),
|
||||||
config.autobackup_period = Backups.autobackup_period_minutes()
|
is_initialized=Backups.is_initted(),
|
||||||
config.location_name = Backups.provider().location
|
autobackup_period=Backups.autobackup_period_minutes(),
|
||||||
config.location_id = Backups.provider().repo_id
|
location_name=Backups.provider().location,
|
||||||
|
location_id=Backups.provider().repo_id,
|
||||||
|
)
|
||||||
|
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
def all_snapshots(self) -> typing.List[SnapshotInfo]:
|
def all_snapshots(self) -> typing.List[SnapshotInfo]:
|
||||||
|
if not Backups.is_initted():
|
||||||
|
return []
|
||||||
result = []
|
result = []
|
||||||
snapshots = Backups.get_all_snapshots()
|
snapshots = Backups.get_all_snapshots()
|
||||||
for snap in snapshots:
|
for snap in snapshots:
|
||||||
|
service = get_service_by_id(snap.service_name)
|
||||||
|
if service is None:
|
||||||
|
service = Service(
|
||||||
|
id=snap.service_name,
|
||||||
|
display_name=f"{snap.service_name} (Orphaned)",
|
||||||
|
description="",
|
||||||
|
svg_icon="",
|
||||||
|
is_movable=False,
|
||||||
|
is_required=False,
|
||||||
|
is_enabled=False,
|
||||||
|
status=ServiceStatusEnum.OFF,
|
||||||
|
url=None,
|
||||||
|
dns_records=None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
service = service_to_graphql_service(service)
|
||||||
graphql_snap = SnapshotInfo(
|
graphql_snap = SnapshotInfo(
|
||||||
id=snap.id, service=snap.service_name, created_at=snap.created_at
|
id=snap.id,
|
||||||
|
service=service,
|
||||||
|
created_at=snap.created_at,
|
||||||
)
|
)
|
||||||
result.append(graphql_snap)
|
result.append(graphql_snap)
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -19,6 +19,7 @@ class ServerProvider(Enum):
|
||||||
@strawberry.enum
|
@strawberry.enum
|
||||||
class BackupProvider(Enum):
|
class BackupProvider(Enum):
|
||||||
BACKBLAZE = "BACKBLAZE"
|
BACKBLAZE = "BACKBLAZE"
|
||||||
|
NONE = "NONE"
|
||||||
# for testing purposes, make sure not selectable in prod.
|
# for testing purposes, make sure not selectable in prod.
|
||||||
MEMORY = "MEMORY"
|
MEMORY = "MEMORY"
|
||||||
FILE = "FILE"
|
FILE = "FILE"
|
||||||
|
|
Loading…
Reference in a new issue