From 8c44f78bbb83f0d71141dd91acd5abc22a0f9b69 Mon Sep 17 00:00:00 2001
From: Houkime <>
Date: Wed, 24 Jul 2024 10:12:59 +0000
Subject: [PATCH] feature(services): add config service
---
.../services/config_service/__init__.py | 191 ++++++++++++++++++
.../services/config_service/bitwarden.svg | 3 +
.../services/config_service/icon.py | 5 +
3 files changed, 199 insertions(+)
create mode 100644 selfprivacy_api/services/config_service/__init__.py
create mode 100644 selfprivacy_api/services/config_service/bitwarden.svg
create mode 100644 selfprivacy_api/services/config_service/icon.py
diff --git a/selfprivacy_api/services/config_service/__init__.py b/selfprivacy_api/services/config_service/__init__.py
new file mode 100644
index 0000000..e0229c3
--- /dev/null
+++ b/selfprivacy_api/services/config_service/__init__.py
@@ -0,0 +1,191 @@
+"""Class representing the configs of our API
+Mostly for backupping purposes
+"""
+import base64
+import typing
+
+from typing import List
+from os import path,mkdir
+from pathlib import Path
+
+# from enum import Enum
+
+from selfprivacy_api.services.service import Service, ServiceStatus
+
+from selfprivacy_api.services.test_service.icon import BITWARDEN_ICON
+from selfprivacy_api.utils import USERDATA_FILE, DKIM_DIR, SECRETS_FILE
+from selfprivacy_api.utils.block_devices import BlockDevices
+from shutil import copyfile, copytree, rmtree
+
+
+from selfprivacy_api.services.bitwarden import Bitwarden
+from selfprivacy_api.services.gitea import Gitea
+from selfprivacy_api.services.jitsimeet import JitsiMeet
+from selfprivacy_api.services.mailserver import MailServer
+from selfprivacy_api.services.nextcloud import Nextcloud
+from selfprivacy_api.services.pleroma import Pleroma
+from selfprivacy_api.services.ocserv import Ocserv
+
+
+
+DEFAULT_DELAY = 0
+CONFIG_STASH_DIR = "/tmp/selfprivacy_config_dump"
+
+
+
+# it is too intimately tied to Services
+# that's why it is so awkward.
+# service list is below
+
+class ConfigService(Service):
+ """A fake service to store our configs"""
+
+ folders: List[str] = [CONFIG_STASH_DIR]
+
+ @staticmethod
+ def get_id() -> str:
+ """Return service id."""
+ return "testservice"
+
+ @staticmethod
+ def get_display_name() -> str:
+ """Return service display name."""
+ return "Test Service"
+
+ @staticmethod
+ def get_description() -> str:
+ """Return service description."""
+ return "A small service used for test purposes. Does nothing."
+
+ @staticmethod
+ def get_svg_icon() -> str:
+ """Read SVG icon from file and return it as base64 encoded string."""
+ # return ""
+ return base64.b64encode(BITWARDEN_ICON.encode("utf-8")).decode("utf-8")
+
+ @staticmethod
+ def get_url() -> typing.Optional[str]:
+ """Return service url."""
+ domain = "test.com"
+ return f"https://password.{domain}"
+
+ @staticmethod
+ def get_subdomain() -> typing.Optional[str]:
+ return "password"
+
+ @classmethod
+ def is_movable(cls) -> bool:
+ return False
+
+ @staticmethod
+ def is_required() -> bool:
+ return False
+
+ @staticmethod
+ def get_backup_description() -> str:
+ return "How did we get here?"
+
+ @classmethod
+ def status_file(cls) -> str:
+ dir = cls.folders[0]
+ # We do not want to store our state in our declared folders
+ # Because they are moved and tossed in tests wildly
+ parent = Path(dir).parent
+
+ return path.join(parent, "service_status")
+
+ @classmethod
+ def set_status(cls, status: ServiceStatus):
+ pass
+
+ @classmethod
+ def get_status(cls) -> ServiceStatus:
+ return ServiceStatus.ACTIVE
+
+ @classmethod
+ def can_be_backed_up(cls) -> bool:
+ """`True` if the service can be backed up."""
+ return True
+
+ @staticmethod
+ def merge_settings(restored_settings_folder: str):
+ # For now we will just copy settings EXCEPT the locations of services
+ # Stash locations as they are set by user right now
+ locations = {}
+ for service in services:
+ locations[service.get_id()] = service.get_drive()
+
+ # Copy files
+ userdata_name = path.basename(USERDATA_FILE)
+ secretfile_name = path.basename(SECRETS_FILE)
+ dkim_dirname = path.basename(DKIM_DIR)
+
+ copyfile(path.join(restored_settings_folder, userdata_name), USERDATA_FILE)
+ copyfile(path.join(restored_settings_folder, secretfile_name), SECRETS_FILE)
+ copytree(path.join(restored_settings_folder, dkim_dirname), DKIM_DIR)
+
+ # Pop locations
+ for service in services:
+ device = BlockDevices().get_block_device(locations[service.get_id()])
+ if device is not None:
+ service.set_location(device.name)
+
+
+
+ @classmethod
+ def stop(cls):
+ # simulate a failing service unable to stop
+ if not cls.get_status() == ServiceStatus.FAILED:
+ cls.set_status(ServiceStatus.DEACTIVATING)
+ cls.change_status_with_async_delay(
+ ServiceStatus.INACTIVE, cls.startstop_delay
+ )
+
+ @classmethod
+ def start(cls):
+ pass
+
+ @classmethod
+ def restart(cls):
+ pass
+
+ @staticmethod
+ def get_logs():
+ return ""
+
+ @classmethod
+ def get_drive(cls) -> str:
+ return BlockDevices().get_root_block_device().name
+
+ @classmethod
+ def get_folders(cls) -> List[str]:
+ return cls.folders
+
+ @classmethod
+ def pre_backup(cls):
+ tempdir = cls.folders[0]
+ rmtree(tempdir,ignore_errors=True)
+ mkdir(tempdir)
+
+
+ copyfile(USERDATA_FILE, tempdir)
+ copyfile(SECRETS_FILE, tempdir)
+ copytree(DKIM_DIR, tempdir)
+
+ @classmethod
+ def post_restore(cls):
+ tempdir = cls.folders[0]
+ cls.merge_settings(tempdir)
+ rmtree(tempdir,ignore_errors=True)
+
+# It is here because our thing needs to include itself
+services: list[Service] = [
+ Bitwarden(),
+ Gitea(),
+ MailServer(),
+ Nextcloud(),
+ Pleroma(),
+ Ocserv(),
+ JitsiMeet(),
+ ConfigService(),
+]
diff --git a/selfprivacy_api/services/config_service/bitwarden.svg b/selfprivacy_api/services/config_service/bitwarden.svg
new file mode 100644
index 0000000..ced270c
--- /dev/null
+++ b/selfprivacy_api/services/config_service/bitwarden.svg
@@ -0,0 +1,3 @@
+
diff --git a/selfprivacy_api/services/config_service/icon.py b/selfprivacy_api/services/config_service/icon.py
new file mode 100644
index 0000000..f9280e0
--- /dev/null
+++ b/selfprivacy_api/services/config_service/icon.py
@@ -0,0 +1,5 @@
+BITWARDEN_ICON = """
+
+"""