import asyncio import contextlib from typing import Optional from core.logger import get_logger from core.scheduler import ScheduleConfig, Scheduler logger = get_logger("core.backup_service") class BackupService: def __init__(self, app: "OverUB") -> None: self.app = app cfg = app.config.get().get("backup", {}) self._auto = bool(cfg.get("auto", False)) schedule_time = cfg.get("schedule", "03:30") self._scheduler = Scheduler( ScheduleConfig( time=schedule_time, postpone_on_activity=bool(cfg.get("postpone_on_activity", True)), max_downtime=int(cfg.get("max_downtime", 120)), retry_failed=bool(cfg.get("retry_failed", True)), retry_interval=int(cfg.get("retry_interval", 3600)), ) ) self._interval = int(cfg.get("check_interval", 60)) self._scopes = cfg.get("scopes", ["core", "modules", "plugins"]) self._task: Optional[asyncio.Task] = None def start(self) -> None: if self._task and not self._task.done(): return self._task = asyncio.create_task(self._run_loop()) async def stop(self) -> None: if self._task: self._task.cancel() with contextlib.suppress(asyncio.CancelledError): await self._task async def _run_loop(self) -> None: while True: try: self._refresh_config() if self._auto and self._scheduler.should_run(self.app.last_activity): await self._run_backup() except Exception: logger.exception("Scheduled backup failed") self._scheduler.mark_failed() self.app.update_service.record_event( action="backup", status="failed", meta={"scopes": self._scopes}, ) await asyncio.sleep(self._interval) async def _run_backup(self) -> None: for scope in self._scopes: self.app.backups.create(scope) self._scheduler.mark_run() self.app.update_service.record_event( action="backup", status="success", meta={"scopes": self._scopes}, ) def _refresh_config(self) -> None: cfg = self.app.config.get().get("backup", {}) self._auto = bool(cfg.get("auto", False)) schedule_time = cfg.get("schedule", self._scheduler.config.time) self._scheduler.config.time = schedule_time self._scheduler.config.postpone_on_activity = bool(cfg.get("postpone_on_activity", True)) self._scheduler.config.max_downtime = int(cfg.get("max_downtime", 120)) self._scheduler.config.retry_failed = bool(cfg.get("retry_failed", True)) self._scheduler.config.retry_interval = int(cfg.get("retry_interval", 3600)) self._interval = int(cfg.get("check_interval", self._interval)) self._scopes = cfg.get("scopes", self._scopes)