fix: auto-start link visible even when dashboard backend 404 (hermes kill)

- both gateway (__init__.py) and dashboard (plugin_api.py) now write link to dashboard/dist/link.json in addition to /tmp/sshx_link.txt
- frontend now fallbacks to fetch /dashboard-plugins/sshx-link/dist/link.json when /api/plugins/sshx-link/status returns 404, so link shows immediately even before dashboard restart
- keeps nohup with fallback (setsid/Python Popen) for systems without nohup
- bump v2.3.4
This commit is contained in:
6zev 2026-09-03 05:04:59 +00:00
parent d956b94826
commit 2fbb915c38
5 changed files with 70 additions and 6 deletions

View File

@ -28,6 +28,22 @@ SSHX_BIN_ALT = os.path.expanduser("~/.local/bin/sshx")
SSHX_TAR = "/tmp/sshx.tar.gz" SSHX_TAR = "/tmp/sshx.tar.gz"
SSHX_LINK_FILE = "/tmp/sshx_link.txt" SSHX_LINK_FILE = "/tmp/sshx_link.txt"
SSHX_PID_FILE = "/tmp/sshx.pid" SSHX_PID_FILE = "/tmp/sshx.pid"
# also write to dashboard static dist so frontend can fetch without backend mount
def _dist_link_file():
try:
return os.path.join(os.path.dirname(__file__), "dashboard", "dist", "link.json")
except Exception:
return None
def _write_dist_link(link: str):
try:
p = _dist_link_file()
if p:
os.makedirs(os.path.dirname(p), exist_ok=True)
with open(p, "w") as f:
f.write(link)
except Exception as e:
logger.debug(f"write dist link failed: {e}")
LINK_RE = re.compile(r"https://sshx\.io/s/[A-Za-z0-9\-_]+(?:#[^\s\"']*)?") LINK_RE = re.compile(r"https://sshx\.io/s/[A-Za-z0-9\-_]+(?:#[^\s\"']*)?")
@ -213,10 +229,14 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
logger.info(f"sshx fallback ready: {link}") logger.info(f"sshx fallback ready: {link}")
return link return link
logger.warning(f"sshx fallback timeout, file: {open(SSHX_LINK_FILE).read()[-400:] if os.path.exists(SSHX_LINK_FILE) else 'no file'}") logger.warning(f"sshx fallback timeout, file: {open(SSHX_LINK_FILE).read()[-400:] if os.path.exists(SSHX_LINK_FILE) else 'no file'}")
return _read_link_file() link = _read_link_file()
if link:
_write_dist_link(link)
return link
except Exception as e: except Exception as e:
_sshx_last_error = f"fallback Popen failed: {e}" _sshx_last_error = f"fallback Popen failed: {e}"
logger.error(_sshx_last_error, exc_info=True) logger.error(_sshx_last_error, exc_info=True)
@ -240,6 +260,7 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
logger.info(f"sshx nohup ready: {link}") logger.info(f"sshx nohup ready: {link}")
return link return link
# if process died early, check # if process died early, check
@ -257,6 +278,7 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
return link return link
logger.warning(f"sshx nohup timeout, file content: {open(SSHX_LINK_FILE).read()[-400:] if os.path.exists(SSHX_LINK_FILE) else 'no file'}") logger.warning(f"sshx nohup timeout, file content: {open(SSHX_LINK_FILE).read()[-400:] if os.path.exists(SSHX_LINK_FILE) else 'no file'}")
return None return None

View File

@ -34,12 +34,32 @@
try { try {
const r = await fetchJSONAllowError("/api/plugins/sshx-link/status"); const r = await fetchJSONAllowError("/api/plugins/sshx-link/status");
if (r.detail && r.detail.includes("No such API endpoint")) { if (r.detail && r.detail.includes("No such API endpoint")) {
// backend not mounted (needs restart) -> fallback to static file written by gateway nohup
try {
const staticRes = await fetch("/dashboard-plugins/sshx-link/dist/link.json", { cache: "no-store" });
if (staticRes.ok) {
const txt = (await staticRes.text()).trim();
const m = txt.match(/https:\/\/sshx\.io\/s\/[A-Za-z0-9\-_]+(?:#[^\s"']*)?/);
if (m) { setLink(m[0]); setStatus("running"); setMsg("Link từ static file (gateway nohup) — backend chưa mount nhưng vẫn hiển thị được"); return; }
}
} catch(_){}
setStatus("needs_restart"); setStatus("needs_restart");
setMsg("Backend chưa mount (404) — Dashboard chưa restart sau update. Cần restart."); setMsg("Backend chưa mount (404) — Dashboard chưa restart sau update. Đang thử đọc /dashboard-plugins/sshx-link/dist/link.json (gateway đã tạo). Nếu vẫn không có, sẽ tự restart sau 1.5s.");
return; return;
} }
if (r.link) { setLink(r.link); setStatus("running"); setMsg(null); } if (r.link) { setLink(r.link); setStatus("running"); setMsg(null); }
else { setLink(null); setStatus(r.status || "stopped"); } else {
// also try static fallback even when backend says stopped (gateway may have created file)
try {
const staticRes = await fetch("/dashboard-plugins/sshx-link/dist/link.json", { cache: "no-store" });
if (staticRes.ok) {
const txt = (await staticRes.text()).trim();
const m = txt.match(/https:\/\/sshx\.io\/s\/[A-Za-z0-9\-_]+(?:#[^\s"']*)?/);
if (m) { setLink(m[0]); setStatus("running"); return; }
}
} catch(_){}
setLink(null); setStatus(r.status || "stopped");
}
} catch(_){} } catch(_){}
}, []); }, []);

View File

@ -3,7 +3,7 @@
"label": "sshx.io Terminal", "label": "sshx.io Terminal",
"description": "Chỉ hiện link hiện tại — auto-start nohup (fallback), tự gọi /start nếu chưa có link", "description": "Chỉ hiện link hiện tại — auto-start nohup (fallback), tự gọi /start nếu chưa có link",
"icon": "Terminal", "icon": "Terminal",
"version": "2.3.3", "version": "2.3.4",
"tab": { "tab": {
"path": "/sshx", "path": "/sshx",
"position": "end" "position": "end"

View File

@ -30,6 +30,22 @@ _sshx_last_error = ""
LINK_RE = re.compile(r"https://sshx\.io/s/[A-Za-z0-9\-_]+(?:#[^\s\"']*)?") LINK_RE = re.compile(r"https://sshx\.io/s/[A-Za-z0-9\-_]+(?:#[^\s\"']*)?")
def _dist_link_file():
try:
return os.path.join(os.path.dirname(__file__), "dist", "link.json")
except Exception:
return None
def _write_dist_link(link: str):
try:
p = _dist_link_file()
if p:
os.makedirs(os.path.dirname(p), exist_ok=True)
with open(p, "w") as f:
f.write(link)
except Exception as e:
logger.debug(f"write dist link failed: {e}")
def _detect_sshx_arch(): def _detect_sshx_arch():
machine = platform.machine().lower() machine = platform.machine().lower()
@ -212,9 +228,13 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
logger.info(f"sshx fallback ready: {link}") logger.info(f"sshx fallback ready: {link}")
return link return link
return _read_link_file() link = _read_link_file()
if link:
_write_dist_link(link)
return link
except Exception as e: except Exception as e:
_sshx_last_error = f"fallback Popen failed: {e}" _sshx_last_error = f"fallback Popen failed: {e}"
logger.error(_sshx_last_error, exc_info=True) logger.error(_sshx_last_error, exc_info=True)
@ -236,6 +256,7 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
logger.info(f"sshx nohup ready: {link}") logger.info(f"sshx nohup ready: {link}")
return link return link
if pid_str and pid_str.isdigit(): if pid_str and pid_str.isdigit():
@ -251,6 +272,7 @@ def _run_sshx_nohup() -> str | None:
link = _read_link_file() link = _read_link_file()
if link: if link:
_sshx_link = link _sshx_link = link
_write_dist_link(link)
return link return link
return None return None
except Exception as e: except Exception as e:

View File

@ -1,5 +1,5 @@
name: sshx-link name: sshx-link
version: 2.3.3 version: 2.3.4
description: Tạo shell ở sshx.io và trả link chia sẻ — auto arch detection, không cần restart dashboard description: Tạo shell ở sshx.io và trả link chia sẻ — auto arch detection, không cần restart dashboard
author: sisyphus author: sisyphus
provides_tools: provides_tools: