mirror of
https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git
synced 2024-11-23 12:31:27 +00:00
304 lines
9.4 KiB
Python
304 lines
9.4 KiB
Python
import typing
|
|
import strawberry
|
|
|
|
from selfprivacy_api.utils.graphql import api_job_mutation_error
|
|
|
|
from selfprivacy_api.jobs import Jobs
|
|
|
|
from selfprivacy_api.graphql import IsAuthenticated
|
|
from selfprivacy_api.graphql.mutations.mutation_interface import (
|
|
GenericMutationReturn,
|
|
GenericJobMutationReturn,
|
|
MutationReturnInterface,
|
|
)
|
|
from selfprivacy_api.graphql.queries.backup import BackupConfiguration
|
|
from selfprivacy_api.graphql.queries.backup import Backup
|
|
from selfprivacy_api.graphql.queries.providers import BackupProvider
|
|
from selfprivacy_api.graphql.common_types.jobs import job_to_api_job
|
|
from selfprivacy_api.graphql.common_types.backup import (
|
|
AutobackupQuotasInput,
|
|
RestoreStrategy,
|
|
)
|
|
|
|
from selfprivacy_api.backup import Backups
|
|
from selfprivacy_api.services import ServiceManager
|
|
from selfprivacy_api.backup.tasks import (
|
|
start_backup,
|
|
restore_snapshot,
|
|
prune_autobackup_snapshots,
|
|
full_restore,
|
|
total_backup,
|
|
)
|
|
from selfprivacy_api.backup.jobs import (
|
|
add_backup_job,
|
|
add_restore_job,
|
|
add_total_restore_job,
|
|
add_total_backup_job,
|
|
)
|
|
from selfprivacy_api.backup.local_secret import LocalBackupSecret
|
|
|
|
|
|
@strawberry.input
|
|
class InitializeRepositoryInput:
|
|
"""Initialize repository input"""
|
|
|
|
provider: BackupProvider
|
|
# The following field may become optional for other providers?
|
|
# Backblaze takes bucket id and name
|
|
location_id: str
|
|
location_name: str
|
|
# Key ID and key for Backblaze
|
|
login: str
|
|
password: str
|
|
# For migration. If set, no new secret is generated
|
|
local_secret: typing.Optional[str] = None
|
|
|
|
|
|
@strawberry.type
|
|
class GenericBackupConfigReturn(MutationReturnInterface):
|
|
"""Generic backup config return"""
|
|
|
|
configuration: typing.Optional[BackupConfiguration]
|
|
|
|
|
|
@strawberry.type
|
|
class BackupMutations:
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def initialize_repository(
|
|
self, repository: InitializeRepositoryInput
|
|
) -> GenericBackupConfigReturn:
|
|
"""Initialize a new repository"""
|
|
Backups.set_provider(
|
|
kind=repository.provider,
|
|
login=repository.login,
|
|
key=repository.password,
|
|
location=repository.location_name,
|
|
repo_id=repository.location_id,
|
|
)
|
|
|
|
secret = repository.local_secret
|
|
if secret is not None:
|
|
LocalBackupSecret.set(secret)
|
|
Backups.force_snapshot_cache_reload()
|
|
else:
|
|
Backups.init_repo()
|
|
return GenericBackupConfigReturn(
|
|
success=True,
|
|
message="",
|
|
code=200,
|
|
configuration=Backup().configuration(),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def remove_repository(self) -> GenericBackupConfigReturn:
|
|
"""Remove repository"""
|
|
Backups.reset()
|
|
return GenericBackupConfigReturn(
|
|
success=True,
|
|
message="",
|
|
code=200,
|
|
configuration=Backup().configuration(),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def set_autobackup_period(
|
|
self, period: typing.Optional[int] = None
|
|
) -> GenericBackupConfigReturn:
|
|
"""Set autobackup period. None is to disable autobackup"""
|
|
if period is not None:
|
|
Backups.set_autobackup_period_minutes(period)
|
|
else:
|
|
Backups.set_autobackup_period_minutes(0)
|
|
|
|
return GenericBackupConfigReturn(
|
|
success=True,
|
|
message="",
|
|
code=200,
|
|
configuration=Backup().configuration(),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def set_autobackup_quotas(
|
|
self, quotas: AutobackupQuotasInput
|
|
) -> GenericBackupConfigReturn:
|
|
"""
|
|
Set autobackup quotas.
|
|
Values <=0 for any timeframe mean no limits for that timeframe.
|
|
To disable autobackup use autobackup period setting, not this mutation.
|
|
"""
|
|
|
|
job = Jobs.add(
|
|
name="Trimming autobackup snapshots",
|
|
type_id="backups.autobackup_trimming",
|
|
description="Pruning the excessive snapshots after the new autobackup quotas are set",
|
|
)
|
|
|
|
try:
|
|
Backups.set_autobackup_quotas(quotas)
|
|
# this task is async and can fail with only a job to report the error
|
|
prune_autobackup_snapshots(job)
|
|
return GenericBackupConfigReturn(
|
|
success=True,
|
|
message="",
|
|
code=200,
|
|
configuration=Backup().configuration(),
|
|
)
|
|
|
|
except Exception as e:
|
|
return GenericBackupConfigReturn(
|
|
success=False,
|
|
message=type(e).__name__ + ":" + str(e),
|
|
code=400,
|
|
configuration=Backup().configuration(),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def start_backup(self, service_id: str) -> GenericJobMutationReturn:
|
|
"""Start backup"""
|
|
|
|
service = ServiceManager.get_service_by_id(service_id)
|
|
if service is None:
|
|
return GenericJobMutationReturn(
|
|
success=False,
|
|
code=300,
|
|
message=f"nonexistent service: {service_id}",
|
|
job=None,
|
|
)
|
|
|
|
job = add_backup_job(service)
|
|
start_backup(service_id)
|
|
|
|
return GenericJobMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="Backup job queued",
|
|
job=job_to_api_job(job),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def total_backup(self) -> GenericJobMutationReturn:
|
|
"""Back up all the enabled services at once
|
|
Useful when migrating
|
|
"""
|
|
|
|
try:
|
|
job = add_total_backup_job()
|
|
total_backup(job)
|
|
except Exception as error:
|
|
return api_job_mutation_error(error)
|
|
|
|
return GenericJobMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="Total backup task queued",
|
|
job=job_to_api_job(job),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def restore_all(self) -> GenericJobMutationReturn:
|
|
"""
|
|
Restore all restorable and enabled services according to last autobackup snapshots
|
|
This happens in sync with partial merging of old configuration for compatibility
|
|
"""
|
|
|
|
try:
|
|
job = add_total_restore_job()
|
|
full_restore(job)
|
|
except Exception as error:
|
|
return GenericJobMutationReturn(
|
|
success=False,
|
|
code=400,
|
|
message=str(error),
|
|
job=None,
|
|
)
|
|
|
|
return GenericJobMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="restore job created",
|
|
job=job_to_api_job(job),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def restore_backup(
|
|
self,
|
|
snapshot_id: str,
|
|
strategy: RestoreStrategy = RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE,
|
|
) -> GenericJobMutationReturn:
|
|
"""Restore backup"""
|
|
snap = Backups.get_snapshot_by_id(snapshot_id)
|
|
if snap is None:
|
|
return GenericJobMutationReturn(
|
|
success=False,
|
|
code=404,
|
|
message=f"No such snapshot: {snapshot_id}",
|
|
job=None,
|
|
)
|
|
|
|
service = ServiceManager.get_service_by_id(snap.service_name)
|
|
if service is None:
|
|
return GenericJobMutationReturn(
|
|
success=False,
|
|
code=404,
|
|
message=f"nonexistent service: {snap.service_name}",
|
|
job=None,
|
|
)
|
|
|
|
try:
|
|
job = add_restore_job(snap)
|
|
except ValueError as error:
|
|
return GenericJobMutationReturn(
|
|
success=False,
|
|
code=400,
|
|
message=str(error),
|
|
job=None,
|
|
)
|
|
|
|
restore_snapshot(snap, strategy)
|
|
|
|
return GenericJobMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="restore job created",
|
|
job=job_to_api_job(job),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def forget_snapshot(self, snapshot_id: str) -> GenericMutationReturn:
|
|
"""Forget a snapshot.
|
|
Makes it inaccessible from the server.
|
|
After some time, the data (encrypted) will not be recoverable
|
|
from the backup server too, but not immediately"""
|
|
|
|
snap = Backups.get_snapshot_by_id(snapshot_id)
|
|
if snap is None:
|
|
return GenericMutationReturn(
|
|
success=False,
|
|
code=404,
|
|
message=f"snapshot {snapshot_id} not found",
|
|
)
|
|
|
|
try:
|
|
Backups.forget_snapshot(snap)
|
|
return GenericMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="",
|
|
)
|
|
except Exception as error:
|
|
return GenericMutationReturn(
|
|
success=False,
|
|
code=400,
|
|
message=str(error),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
def force_snapshots_reload(self) -> GenericMutationReturn:
|
|
"""Force snapshots reload"""
|
|
Backups.force_snapshot_cache_reload()
|
|
return GenericMutationReturn(
|
|
success=True,
|
|
code=200,
|
|
message="",
|
|
)
|