diff options
| author | Jonas Dellinger <jonas@dellinger.dev> | 2022-05-13 19:14:47 +0200 |
|---|---|---|
| committer | Jonas Dellinger <jonas@dellinger.dev> | 2022-05-13 19:14:47 +0200 |
| commit | 74438a31458af8bddd08d90eacc6d63677bab844 (patch) | |
| tree | a7bfc044941f65c7f9971c5386c463eac31be768 /backend/browser.py | |
| parent | 945db5de4788feefebc845817752472419051640 (diff) | |
| download | decky-loader-74438a31458af8bddd08d90eacc6d63677bab844.tar.gz decky-loader-74438a31458af8bddd08d90eacc6d63677bab844.zip | |
Work on react frontend loader
Diffstat (limited to 'backend/browser.py')
| -rw-r--r-- | backend/browser.py | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/backend/browser.py b/backend/browser.py new file mode 100644 index 00000000..ffec26b3 --- /dev/null +++ b/backend/browser.py @@ -0,0 +1,89 @@ +from injector import get_tab +from logging import getLogger +from os import path, rename +from shutil import rmtree +from aiohttp import ClientSession, web +from io import BytesIO +from zipfile import ZipFile +from concurrent.futures import ProcessPoolExecutor +from asyncio import get_event_loop +from time import time +from hashlib import sha256 +from subprocess import Popen + +class PluginInstallContext: + def __init__(self, gh_url, version, hash) -> None: + self.gh_url = gh_url + self.version = version + self.hash = hash + +class PluginBrowser: + def __init__(self, plugin_path, server_instance, store_url) -> None: + self.log = getLogger("browser") + self.plugin_path = plugin_path + self.store_url = store_url + self.install_requests = {} + + server_instance.add_routes([ + web.post("/browser/install_plugin", self.install_plugin), + web.get("/browser/iframe", self.redirect_to_store) + ]) + + def _unzip_to_plugin_dir(self, zip, name, hash): + zip_hash = sha256(zip.getbuffer()).hexdigest() + if zip_hash != hash: + return False + zip_file = ZipFile(zip) + zip_file.extractall(self.plugin_path) + rename(path.join(self.plugin_path, zip_file.namelist()[0]), path.join(self.plugin_path, name)) + Popen(["chown", "-R", "deck:deck", self.plugin_path]) + Popen(["chmod", "-R", "555", self.plugin_path]) + return True + + async def _install(self, artifact, version, hash): + name = artifact.split("/")[-1] + rmtree(path.join(self.plugin_path, name), ignore_errors=True) + self.log.info(f"Installing {artifact} (Version: {version})") + async with ClientSession() as client: + url = f"https://github.com/{artifact}/archive/refs/tags/{version}.zip" + self.log.debug(f"Fetching {url}") + res = await client.get(url) + if res.status == 200: + self.log.debug("Got 200. Reading...") + data = await res.read() + self.log.debug(f"Read {len(data)} bytes") + res_zip = BytesIO(data) + with ProcessPoolExecutor() as executor: + self.log.debug("Unzipping...") + ret = await get_event_loop().run_in_executor( + executor, + self._unzip_to_plugin_dir, + res_zip, + name, + hash + ) + if ret: + self.log.info(f"Installed {artifact} (Version: {version})") + else: + self.log.fatal(f"SHA-256 Mismatch!!!! {artifact} (Version: {version})") + else: + self.log.fatal(f"Could not fetch from github. {await res.text()}") + + async def redirect_to_store(self, request): + return web.Response(status=302, headers={"Location": self.store_url}) + + async def install_plugin(self, request): + data = await request.post() + get_event_loop().create_task(self.request_plugin_install(data["artifact"], data["version"], data["hash"])) + return web.Response(text="Requested plugin install") + + async def request_plugin_install(self, artifact, version, hash): + request_id = str(time()) + self.install_requests[request_id] = PluginInstallContext(artifact, version, hash) + tab = await get_tab("QuickAccess") + await tab.open_websocket() + await tab.evaluate_js(f"addPluginInstallPrompt('{artifact}', '{version}', '{request_id}')") + + async def confirm_plugin_install(self, request_id): + request = self.install_requests.pop(request_id) + await self._install(request.gh_url, request.version, request.hash)
\ No newline at end of file |
