hermes-sshx-link/dashboard/dist/index.js

196 lines
7.5 KiB
JavaScript

(function () {
"use strict";
const SDK = window.__HERMES_PLUGIN_SDK__;
const { React } = SDK;
const { useState, useEffect, useCallback } = SDK.hooks;
const { Card, CardHeader, CardTitle, CardContent, Button } = SDK.components;
async function safeFetchJSON(path, opts) {
const res = await fetch(path, opts || {});
const text = await res.text();
try { return JSON.parse(text); } catch (_) {
throw new Error("Server returned non-JSON (status " + res.status + "): " + text.slice(0,300));
}
}
function SSHXPage() {
const [link, setLink] = useState(null);
const [status, setStatus] = useState("idle");
const [error, setError] = useState(null);
const start = useCallback(async function () {
setStatus("loading");
setError(null);
try {
var res = await safeFetchJSON("/api/plugins/sshx-link/status");
if (res.status === "installing") {
setStatus("installing");
return;
}
if (res.status === "running" && res.link) {
setLink(res.link);
setStatus("running");
return;
}
res = await safeFetchJSON("/api/plugins/sshx-link/start");
if (res.link) {
setLink(res.link);
setStatus("running");
} else if (res.status === "starting") {
setStatus("installing");
setError("sshx đang khởi động, thử lại sau 2s...");
setTimeout(function(){ safeFetchJSON("/api/plugins/sshx-link/status").then(function(r){ if(r.link){ setLink(r.link); setStatus("running"); setError(null);} }); }, 2000);
} else {
setError(res.message || "No link returned");
setStatus("error");
}
} catch (err) {
setError(err.message || "Failed to start sshx");
setStatus("error");
}
}, []);
const stop = useCallback(async function () {
try {
await safeFetchJSON("/api/plugins/sshx-link/stop", { method: "POST" });
setLink(null);
setStatus("idle");
setError(null);
} catch (err) {
setError(err.message);
}
}, []);
const checkStatus = useCallback(async function () {
try {
const res = await safeFetchJSON("/api/plugins/sshx-link/status");
if (res.status === "running" && res.link) {
setLink(res.link);
setStatus("running");
} else if (res.status === "installing" || res.status === "starting") {
setStatus("installing");
} else if (res.status === "stopped") {
if (status === "running") {
setLink(null);
setStatus("idle");
}
}
} catch (_) {}
}, [status]);
useEffect(function () {
checkStatus();
}, [checkStatus]);
useEffect(function () {
if (status !== "installing") return;
var timer = setInterval(function () {
checkStatus();
}, 2000);
return function () { clearInterval(timer); };
}, [status, checkStatus]);
if (status === "installing") {
return React.createElement(Card, null,
React.createElement(CardHeader, null,
React.createElement(CardTitle, null, "sshx.io Shared Terminal")
),
React.createElement(CardContent, null,
React.createElement("p", {
className: "text-sm text-yellow-500",
style: { fontWeight: 600 }
}, "Đang cài / khởi động sshx (auto arch detect)..."),
React.createElement("p", { className: "text-xs text-muted-foreground", style: { marginTop: "0.5rem" } }, "Đang tải binary từ s3.amazonaws.com/sshx, không cần restart dashboard. Tự động thử lại sau 2s."),
error && React.createElement("p", { className: "text-xs text-yellow-700", style: { marginTop: "0.5rem"}}, error)
)
);
}
if (status === "running" && link) {
return React.createElement(Card, null,
React.createElement(CardHeader, null,
React.createElement(CardTitle, null, "sshx.io Shared Terminal"),
React.createElement("p", { className: "text-xs text-green-500", style: { fontWeight: 600 } }, "● Running — shell đã tạo ở sshx.io")
),
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",
marginBottom: "0.75rem",
fontWeight: 600
}
}, link),
React.createElement("div", { style: { display: "flex", gap: "0.5rem", marginBottom: "1rem", flexWrap: "wrap" } },
React.createElement(Button, {
onClick: function() { navigator.clipboard && navigator.clipboard.writeText(link); },
variant: "outline",
size: "sm"
}, "Copy Link"),
React.createElement(Button, {
onClick: function() { window.open(link, "_blank"); },
variant: "outline",
size: "sm"
}, "Open"),
React.createElement(Button, {
onClick: stop,
variant: "destructive",
size: "sm"
}, "Stop")
),
React.createElement("div", {
style: {
border: "1px solid #333",
borderRadius: "8px",
overflow: "hidden",
height: "560px",
background: "#000"
}
},
React.createElement("iframe", {
src: link,
style: { width: "100%", height: "100%", border: "0" },
title: "sshx Terminal",
allow: "clipboard-read; clipboard-write"
})
),
React.createElement("p", { className: "text-xs text-muted-foreground", style: { marginTop: "0.5rem" } },
"Terminal nhúng ở trên (infinite canvas, cộng tác realtime). Nếu iframe bị chặn, bấm Open để mở tab mới. Link có dạng https://sshx.io/s/<id>#<key> — chia sẻ cho user là xong."
)
)
);
}
return React.createElement(Card, null,
React.createElement(CardHeader, null,
React.createElement(CardTitle, null, "sshx.io Shared Terminal"),
React.createElement("p", { className: "text-xs text-muted-foreground" }, "Tạo shell ở sshx.io và trả link chia sẻ — auto arch, không cần restart")
),
React.createElement(CardContent, null,
React.createElement("p", {
className: "text-sm text-muted-foreground",
style: { marginBottom: "1rem" }
}, "Bấm Start để tạo shell cộng tác trên sshx.io. Plugin sẽ tự tải binary sshx (S3), chạy sshx và parse link https://sshx.io/s/... trả về cho user."),
error && React.createElement("p", {
className: "text-sm text-red-500",
style: { marginBottom: "1rem", whiteSpace: "pre-wrap" }
}, "Error: " + error),
React.createElement(Button, {
onClick: start,
disabled: status === "loading"
}, status === "loading" ? "Đang tạo shell ở sshx.io..." : "Start sshx → Lấy link"),
React.createElement("p", { className: "text-xs text-muted-foreground", style: { marginTop: "0.75rem"}}, "Tương đương lệnh: curl -sSf https://sshx.io/get | sh -s run — nhưng tự động hoàn toàn trong dashboard.")
)
);
}
window.__HERMES_PLUGINS__.register("sshx-link", SSHXPage);
})();