"""Backup onboarding views.""" from __future__ import annotations from collections.abc import Callable, Coroutine from functools import wraps from http import HTTPStatus from typing import TYPE_CHECKING, Any, Concatenate from aiohttp import web from aiohttp.web_exceptions import HTTPUnauthorized import voluptuous as vol from homeassistant.components.http import KEY_HASS from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.onboarding import ( BaseOnboardingView, NoAuthBaseOnboardingView, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.backup import async_get_manager as async_get_backup_manager from . import BackupManager, Folder, IncorrectPasswordError, http as backup_http if TYPE_CHECKING: from homeassistant.components.onboarding import OnboardingStoreData async def async_setup_views(hass: HomeAssistant, data: OnboardingStoreData) -> None: """Set up the backup views.""" hass.http.register_view(BackupInfoView(data)) hass.http.register_view(RestoreBackupView(data)) hass.http.register_view(UploadBackupView(data)) def with_backup_manager[_ViewT: BaseOnboardingView, **_P]( func: Callable[ Concatenate[_ViewT, BackupManager, web.Request, _P], Coroutine[Any, Any, web.Response], ], ) -> Callable[Concatenate[_ViewT, web.Request, _P], Coroutine[Any, Any, web.Response]]: """Home Assistant API decorator to check onboarding and inject manager.""" @wraps(func) async def with_backup( self: _ViewT, request: web.Request, *args: _P.args, **kwargs: _P.kwargs, ) -> web.Response: """Check admin and call function.""" if self._data["done"]: raise HTTPUnauthorized manager = await async_get_backup_manager(request.app[KEY_HASS]) return await func(self, manager, request, *args, **kwargs) return with_backup class BackupInfoView(NoAuthBaseOnboardingView): """Get backup info view.""" url = "/api/onboarding/backup/info" name = "api:onboarding:backup:info" @with_backup_manager async def get(self, manager: BackupManager, request: web.Request) -> web.Response: """Return backup info.""" backups, _ = await manager.async_get_backups() return self.json( { "backups": list(backups.values()), "state": manager.state, "last_action_event": manager.last_action_event, } ) class RestoreBackupView(NoAuthBaseOnboardingView): """Restore backup view.""" url = "/api/onboarding/backup/restore" name = "api:onboarding:backup:restore" @RequestDataValidator( vol.Schema( { vol.Required("backup_id"): str, vol.Required("agent_id"): str, vol.Optional("password"): str, vol.Optional("restore_addons"): [str], vol.Optional("restore_database", default=True): bool, vol.Optional("restore_folders"): [vol.Coerce(Folder)], } ) ) @with_backup_manager async def post( self, manager: BackupManager, request: web.Request, data: dict[str, Any] ) -> web.Response: """Restore a backup.""" try: await manager.async_restore_backup( data["backup_id"], agent_id=data["agent_id"], password=data.get("password"), restore_addons=data.get("restore_addons"), restore_database=data["restore_database"], restore_folders=data.get("restore_folders"), restore_homeassistant=True, ) except IncorrectPasswordError: return self.json( {"code": "incorrect_password"}, status_code=HTTPStatus.BAD_REQUEST ) except HomeAssistantError as err: return self.json( {"code": "restore_failed", "message": str(err)}, status_code=HTTPStatus.BAD_REQUEST, ) return web.Response(status=HTTPStatus.OK) class UploadBackupView(NoAuthBaseOnboardingView, backup_http.UploadBackupView): """Upload backup view.""" url = "/api/onboarding/backup/upload" name = "api:onboarding:backup:upload" @with_backup_manager async def post(self, manager: BackupManager, request: web.Request) -> web.Response: """Upload a backup file.""" return await self._post(request)