feat: install, update and remove Mudfish from the panel
- Install: download the latest package from mudfish.net, verify the Makeself MD5, unpack the bzip2 tar ourselves (no installer script is executed; path traversal and links rejected) into /opt/mudfish/<ver>. - Update: checked at most daily; button-only; refused while the tunnel is up; carries the saved login (.conf, .conf.db) into the new version, restarts the daemon and keeps only the previous version. - Remove: button with confirmation, and automatically when the plugin is uninstalled: VPN off, daemon stopped, mudfish-tap removed, /opt/mudfish deleted, plugin settings cleared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
027a029b47
commit
3c28c9dac2
9 files changed
+718
-27
No files matched your search
@@ -8,13 +8,14 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "py_modules"))
|
||||
|
||||
import decky # noqa: E402
|
||||
|
||||
from mudfish_decky import system # noqa: E402
|
||||
from mudfish_decky import installer, system # noqa: E402
|
||||
from mudfish_decky.client import MudfishClient # noqa: E402
|
||||
from mudfish_decky.controller import Controller # noqa: E402
|
||||
from mudfish_decky.relay import LoginRelay # noqa: E402
|
||||
from mudfish_decky.store import Store # noqa: E402
|
||||
|
||||
RESUME_POLL_SECONDS = 2
|
||||
LATEST_POLL_TICKS = 1800 # every hour at 2s per tick
|
||||
LOGIN_RELAY_SECONDS = 300
|
||||
|
||||
|
||||
@@ -30,6 +31,7 @@ class Plugin:
|
||||
self.controller = Controller(
|
||||
MudfishClient(base), system, store,
|
||||
relay_factory=lambda: LoginRelay(target=(target.hostname, target.port or 80), ttl=LOGIN_RELAY_SECONDS),
|
||||
installer=installer,
|
||||
)
|
||||
return self.controller
|
||||
|
||||
@@ -39,6 +41,15 @@ class Plugin:
|
||||
async def stop_login(self) -> dict:
|
||||
return await asyncio.to_thread(self._get_controller().stop_login)
|
||||
|
||||
async def install_mudfish(self) -> dict:
|
||||
return await asyncio.to_thread(self._get_controller().install)
|
||||
|
||||
async def update_mudfish(self) -> dict:
|
||||
return await asyncio.to_thread(self._get_controller().update)
|
||||
|
||||
async def uninstall_mudfish(self) -> dict:
|
||||
return await asyncio.to_thread(self._get_controller().uninstall)
|
||||
|
||||
async def get_state(self) -> dict:
|
||||
return await asyncio.to_thread(self._get_controller().state)
|
||||
|
||||
@@ -64,13 +75,23 @@ class Plugin:
|
||||
await asyncio.to_thread(controller.prepare)
|
||||
decky.logger.info("Mudfish VPN plugin loaded")
|
||||
# Watch for sleep/resume even while the panel is closed; a resumed tunnel is turned off.
|
||||
# Also look for a newer Mudfish release now and then (the controller limits it to once a day).
|
||||
ticks = 0
|
||||
while True:
|
||||
try:
|
||||
await asyncio.to_thread(controller.check_resume, system.sleep_offset())
|
||||
if ticks % LATEST_POLL_TICKS == 0:
|
||||
await asyncio.to_thread(controller.refresh_latest)
|
||||
except Exception as e:
|
||||
decky.logger.warning("resume check failed: %s", e)
|
||||
decky.logger.warning("background check failed: %s", e)
|
||||
ticks += 1
|
||||
await asyncio.sleep(RESUME_POLL_SECONDS)
|
||||
|
||||
async def _unload(self):
|
||||
await asyncio.to_thread(self._get_controller().shutdown)
|
||||
decky.logger.info("Mudfish VPN plugin unloaded")
|
||||
|
||||
async def _uninstall(self):
|
||||
# Removing the plugin removes Mudfish too (user decision): VPN off, daemon stopped, /opt/mudfish gone.
|
||||
await asyncio.to_thread(self._get_controller().uninstall)
|
||||
decky.logger.info("Mudfish removed with the plugin")
|
||||
Reference in new issue
Block a user