fix: dashboard shows empty - restore auto-start via nohup with frontend auto-call
- frontend was passive and never called /start, so /tmp/sshx_link.txt never created -> always 'Chưa có shell' - now frontend auto-calls /start (nohup with fallback) 1.5s after detecting stopped, shows starting state - keep passive display (only current link) but add auto-start + Restart button for 404 - bump v2.3.2
This commit is contained in:
parent
33672c7458
commit
77514c9470
|
|
@ -2,11 +2,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
const SDK = window.__HERMES_PLUGIN_SDK__;
|
const SDK = window.__HERMES_PLUGIN_SDK__;
|
||||||
const { React } = SDK;
|
const { React } = SDK;
|
||||||
const { Card, CardHeader, CardTitle, CardContent } = SDK.components;
|
const { Card, CardHeader, CardTitle, CardContent, Button } = SDK.components;
|
||||||
|
|
||||||
async function fetchJSONAllowError(path) {
|
async function fetchJSONAllowError(path, opts) {
|
||||||
if (SDK && SDK.fetchJSON) {
|
if (SDK && SDK.fetchJSON) {
|
||||||
try { return await SDK.fetchJSON(path); } catch (e) { if (e && e.data) return e.data; throw e; }
|
try { return await SDK.fetchJSON(path, opts || {}); } catch (e) { if (e && e.data) return e.data; throw e; }
|
||||||
}
|
}
|
||||||
let url = path;
|
let url = path;
|
||||||
try {
|
try {
|
||||||
|
|
@ -18,46 +18,113 @@
|
||||||
url = u.pathname + u.search;
|
url = u.pathname + u.search;
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
const res = await fetch(url);
|
const res = await fetch(url, opts || {});
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
try { return JSON.parse(text); } catch (_) { return { status: "error", message: text.slice(0,800) }; }
|
try { return JSON.parse(text); } catch (_) { return { status: "error", message: text.slice(0,800), detail: text.slice(0,800), http_status: res.status }; }
|
||||||
}
|
}
|
||||||
|
|
||||||
function SSHXPage() {
|
function SSHXPage() {
|
||||||
const [link, setLink] = React.useState(null);
|
const [link, setLink] = React.useState(null);
|
||||||
const [status, setStatus] = React.useState("idle");
|
const [status, setStatus] = React.useState("idle");
|
||||||
|
const [msg, setMsg] = React.useState(null);
|
||||||
|
const [triedAuto, setTriedAuto] = React.useState(false);
|
||||||
|
|
||||||
const check = React.useCallback(async function(){
|
const check = React.useCallback(async function(){
|
||||||
try {
|
try {
|
||||||
const r = await fetchJSONAllowError("/api/plugins/sshx-link/status");
|
const r = await fetchJSONAllowError("/api/plugins/sshx-link/status");
|
||||||
if (r.link) { setLink(r.link); setStatus("running"); }
|
if (r.detail && r.detail.includes("No such API endpoint")) {
|
||||||
|
setStatus("needs_restart");
|
||||||
|
setMsg("Backend chưa mount (404) — Dashboard chưa restart sau update. Cần restart.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (r.link) { setLink(r.link); setStatus("running"); setMsg(null); }
|
||||||
else { setLink(null); setStatus(r.status || "stopped"); }
|
else { setLink(null); setStatus(r.status || "stopped"); }
|
||||||
} catch(_){}
|
} catch(_){}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const doStart = React.useCallback(async function(){
|
||||||
|
setStatus("starting");
|
||||||
|
setMsg("Đang khởi tạo sshx qua nohup (fallback nếu thiếu nohup)...");
|
||||||
|
try {
|
||||||
|
const r = await fetchJSONAllowError("/api/plugins/sshx-link/start");
|
||||||
|
if (r.link) { setLink(r.link); setStatus("running"); setMsg(null); }
|
||||||
|
else if (r.detail && r.detail.includes("No such API")) { setStatus("needs_restart"); setMsg(r.detail); }
|
||||||
|
else { setMsg(r.message || r.detail || JSON.stringify(r)); setStatus("error"); }
|
||||||
|
} catch(e){
|
||||||
|
const d = e.data ? (e.data.detail || e.data.message) : "";
|
||||||
|
if (d.includes("No such API")) { setStatus("needs_restart"); setMsg(d); }
|
||||||
|
else { setMsg(e.message + (d ? " | "+d : "")); setStatus("error"); }
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
React.useEffect(function(){ check(); const t=setInterval(check, 3000); return function(){clearInterval(t);} }, [check]);
|
React.useEffect(function(){ check(); const t=setInterval(check, 3000); return function(){clearInterval(t);} }, [check]);
|
||||||
|
|
||||||
|
// auto-start once if no link after initial check
|
||||||
|
React.useEffect(function(){
|
||||||
|
if (triedAuto) return;
|
||||||
|
if (status === "stopped") {
|
||||||
|
setTriedAuto(true);
|
||||||
|
setTimeout(doStart, 1500);
|
||||||
|
}
|
||||||
|
}, [status, triedAuto, doStart]);
|
||||||
|
|
||||||
|
const restartDashboard = React.useCallback(async function(){
|
||||||
|
setMsg("Đang gửi lệnh restart Gateway/Dashboard...");
|
||||||
|
try { await fetchJSONAllowError("/api/gateway/restart", {method:"POST"}); } catch(_){}
|
||||||
|
try { await fetchJSONAllowError("/api/dashboard/plugins/rescan"); } catch(_){}
|
||||||
|
setMsg("Đã gửi restart, đợi 7s rồi reload...");
|
||||||
|
setTimeout(function(){ window.location.reload(); }, 7000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (status === "needs_restart") {
|
||||||
|
return React.createElement(Card, null,
|
||||||
|
React.createElement(CardHeader, null,
|
||||||
|
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
||||||
|
React.createElement("p", { className:"text-xs text-red-500", style:{fontWeight:700}}, "● Cần restart Dashboard (404)")
|
||||||
|
),
|
||||||
|
React.createElement(CardContent, null,
|
||||||
|
React.createElement("pre", { style:{whiteSpace:"pre-wrap", fontSize:"0.85rem", color:"#fbbf24", background:"#1a1a0a", padding:"0.75rem", borderRadius:"6px", marginBottom:"0.75rem"}}, msg),
|
||||||
|
React.createElement(Button, { onClick: restartDashboard }, "Restart Dashboard (không cần SSH)"),
|
||||||
|
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Sau restart backend sẽ mount lại, auto-start bằng nohup & sẽ tạo link.")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
return React.createElement(Card, null,
|
return React.createElement(Card, null,
|
||||||
React.createElement(CardHeader, null,
|
React.createElement(CardHeader, null,
|
||||||
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
||||||
React.createElement("p", { className:"text-xs text-green-500", style:{fontWeight:600}}, "● Active")
|
React.createElement("p", { className:"text-xs text-green-500", style:{fontWeight:600}}, "● Active — auto-start bằng nohup (tránh hermes kill)")
|
||||||
),
|
),
|
||||||
React.createElement(CardContent, null,
|
React.createElement(CardContent, null,
|
||||||
React.createElement("a", { href: link, target:"_blank", rel:"noopener noreferrer", style:{ color:"#60a5fa", fontSize:"1.05rem", wordBreak:"break-all", textDecoration:"underline", display:"block", fontWeight:600 }}, link),
|
React.createElement("a", { href: link, target:"_blank", rel:"noopener noreferrer", style:{ color:"#60a5fa", fontSize:"1.05rem", wordBreak:"break-all", textDecoration:"underline", display:"block", fontWeight:600 }}, link),
|
||||||
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Link hiện tại của shell sshx. Chỉ hiển thị — không tự chạy nền (hermes kill shell gây lỗi). Chạy thủ công trên host: nohup sshx --quiet > /tmp/sshx_link.txt 2>&1 &")
|
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Link hiện tại, tự chạy nền bằng nohup (có fallback khi thiếu nohup). Tự refresh mỗi 3s.")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status === "starting" || status === "installing") {
|
||||||
|
return React.createElement(Card, null,
|
||||||
|
React.createElement(CardHeader, null,
|
||||||
|
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
||||||
|
React.createElement("p", { className:"text-xs text-yellow-500", style:{fontWeight:600}}, "● Đang khởi tạo...")
|
||||||
|
),
|
||||||
|
React.createElement(CardContent, null,
|
||||||
|
React.createElement("p", { style:{whiteSpace:"pre-wrap", fontSize:"0.85rem", color:"#eab308"}}, msg || "Đang tải sshx và chạy nohup..."),
|
||||||
|
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Đã dùng nohup & (fallback Python Popen nếu thiếu nohup) để tránh hermes kill shell.")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return React.createElement(Card, null,
|
return React.createElement(Card, null,
|
||||||
React.createElement(CardHeader, null,
|
React.createElement(CardHeader, null,
|
||||||
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
React.createElement(CardTitle, null, "sshx.io — Current Link"),
|
||||||
React.createElement("p", { className:"text-xs text-muted-foreground"}, "Chưa có shell đang chạy")
|
React.createElement("p", { className:"text-xs text-muted-foreground"}, "Chưa có shell đang chạy")
|
||||||
),
|
),
|
||||||
React.createElement(CardContent, null,
|
React.createElement(CardContent, null,
|
||||||
React.createElement("p", { className:"text-sm text-muted-foreground"}, "Không có link hiện tại. Plugin ở chế độ passive — không tự chạy sshx nền (hermes kill). Hãy chạy thủ công trên server:"),
|
React.createElement("p", { className:"text-sm text-muted-foreground", style:{marginBottom:"0.5rem"}}, msg || "Đang tự khởi tạo sshx bằng nohup... (có fallback khi thiếu nohup). Nếu sau 10s vẫn chưa có link, dashboard sẽ tự gọi /start."),
|
||||||
React.createElement("pre", { style:{marginTop:"0.5rem", background:"#111", padding:"0.5rem", borderRadius:"6px", fontSize:"0.75rem", overflow:"auto"}}, "nohup sshx --quiet > /tmp/sshx_link.txt 2>&1 &\ncat /tmp/sshx_link.txt"),
|
React.createElement(Button, { onClick: doStart }, "Khởi tạo ngay (nohup)"),
|
||||||
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Dashboard sẽ tự đọc /tmp/sshx_link.txt và hiện link ở đây, đồng thời tự refresh mỗi 3s.")
|
React.createElement("p", { className:"text-xs text-muted-foreground", style:{marginTop:"0.5rem"}}, "Sau khi chạy, link sẽ lưu ở /tmp/sshx_link.txt và hiện ở đây.")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "sshx-link",
|
"name": "sshx-link",
|
||||||
"label": "sshx.io Terminal",
|
"label": "sshx.io Terminal",
|
||||||
"description": "Chỉ hiện link hiện tại — auto-start nohup với fallback khi thiếu nohup",
|
"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.1",
|
"version": "2.3.2",
|
||||||
"tab": {
|
"tab": {
|
||||||
"path": "/sshx",
|
"path": "/sshx",
|
||||||
"position": "end"
|
"position": "end"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: sshx-link
|
name: sshx-link
|
||||||
version: 2.3.1
|
version: 2.3.2
|
||||||
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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue