feat: expose controller through decky backend
Prepare signs in on load (never connects), system CA bundle for HTTPS, root-owned deploy extraction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4e27e29225
commit
384d0a75f0
8 files changed
+131
-5
No files matched your search
@@ -1,5 +1,7 @@
|
||||
"""HTTP calls to the local Mudfish launcher API and the Mudfish cloud API."""
|
||||
import json
|
||||
import os
|
||||
import ssl
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
@@ -25,10 +27,26 @@ class SigninError(MudfishError):
|
||||
pass
|
||||
|
||||
|
||||
# Decky's bundled Python ships without a CA store, so point it at SteamOS's system bundle.
|
||||
SYSTEM_CA_BUNDLES = ["/etc/ssl/certs/ca-certificates.crt", "/etc/ca-certificates/extracted/tls-ca-bundle.pem"]
|
||||
|
||||
|
||||
def ssl_context(cafiles=SYSTEM_CA_BUNDLES, factory=ssl.create_default_context):
|
||||
ctx = factory()
|
||||
for path in cafiles:
|
||||
if os.path.isfile(path):
|
||||
ctx.load_verify_locations(cafile=path)
|
||||
break
|
||||
return ctx
|
||||
|
||||
|
||||
_SSL = ssl_context()
|
||||
|
||||
|
||||
def urllib_transport(method, url, body, headers):
|
||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
with urllib.request.urlopen(req, timeout=10, context=_SSL) as resp:
|
||||
return resp.status, resp.read()
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read()
|
||||
|
||||
@@ -43,6 +43,18 @@ class Controller:
|
||||
with self._lock:
|
||||
return self._state()
|
||||
|
||||
def prepare(self) -> None:
|
||||
"""Plugin load: bring up the idle launcher and sign in so the node list is ready. Never connects."""
|
||||
with self._lock:
|
||||
installed = self.system.find_mudfish()
|
||||
if not installed:
|
||||
return
|
||||
try:
|
||||
st = self._signed_in(installed)
|
||||
self._static_nodes(st)
|
||||
except Exception as e:
|
||||
log.warning("prepare: %s", e)
|
||||
|
||||
def nodes(self) -> list:
|
||||
with self._lock:
|
||||
self._refresh_rtts()
|
||||
@@ -127,10 +139,10 @@ class Controller:
|
||||
return bool(st.get("core_running") and st.get("client_ready") and self.system.tap_up())
|
||||
|
||||
def _signed_in(self, installed) -> dict:
|
||||
self.system.ensure_daemon(installed[0], self._daemon_up)
|
||||
self.system.ensure_daemon(installed[0], self.daemon_up)
|
||||
return self.client.ensure_signed_in()
|
||||
|
||||
def _daemon_up(self) -> bool:
|
||||
def daemon_up(self) -> bool:
|
||||
try:
|
||||
self.client.status()
|
||||
return True
|
||||
|
||||
@@ -6,6 +6,7 @@ import time
|
||||
import urllib.request
|
||||
from typing import Callable, Optional, Tuple
|
||||
|
||||
from .client import ssl_context
|
||||
from .routes import delete_routes # noqa: F401 (re-exported for the controller)
|
||||
|
||||
MUDFISH_ROOT = "/opt/mudfish"
|
||||
@@ -66,7 +67,7 @@ def route_text(run=subprocess.run) -> str:
|
||||
|
||||
def public_ip() -> Optional[dict]:
|
||||
try:
|
||||
with urllib.request.urlopen("https://ipinfo.io/json", timeout=8) as resp:
|
||||
with urllib.request.urlopen("https://ipinfo.io/json", timeout=8, context=ssl_context()) as resp:
|
||||
data = json.load(resp)
|
||||
return {"ip": data.get("ip"), "country": data.get("country")}
|
||||
except Exception:
|
||||
|
||||
Reference in new issue
Block a user