fix: Unauthorized on 13.140.132.105 - use SDK.fetchJSON + profile forwarding
- frontend used raw fetch without auth -> 401 Unauthorized behind nginx on /api/plugins/* - now uses SDK.fetchJSON (auth-aware) first, fallback raw fetch with ?profile= forwarded from window.location - fetchJSONAllowError returns JSON even on 401 so UI shows detail instead of generic error - bump v2.0.2
This commit is contained in:
parent
39981e6f18
commit
44744cf279
|
|
@ -6,12 +6,76 @@
|
|||
const { useState, useEffect, useCallback } = SDK.hooks;
|
||||
const { Card, CardHeader, CardTitle, CardContent, Button } = SDK.components;
|
||||
|
||||
// Auth-aware fetch: use SDK.fetchJSON if available (handles profile token/headers),
|
||||
// fallback to raw fetch with ?profile= forwarding
|
||||
async function safeFetchJSON(path, opts) {
|
||||
const res = await fetch(path, opts || {});
|
||||
// Prefer SDK's auth-aware helper
|
||||
if (SDK && SDK.fetchJSON) {
|
||||
try {
|
||||
const data = await SDK.fetchJSON(path, opts || {});
|
||||
// SDK.fetchJSON returns parsed JSON directly
|
||||
return data;
|
||||
} catch (e) {
|
||||
// SDK.fetchJSON may throw with body, try to extract
|
||||
if (e && e.data) return e.data;
|
||||
// fallback to raw fetch below
|
||||
if (e && e.message && e.message.includes("Unauthorized")) {
|
||||
// will try raw with profile below
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback: raw fetch but forward ?profile= from current URL (hermes needs it behind nginx)
|
||||
let url = path;
|
||||
try {
|
||||
const cur = new URL(window.location.href);
|
||||
const profile = cur.searchParams.get("profile");
|
||||
if (profile) {
|
||||
const u = new URL(path, window.location.origin);
|
||||
if (!u.searchParams.get("profile")) u.searchParams.set("profile", profile);
|
||||
url = u.pathname + u.search;
|
||||
}
|
||||
} catch (_) { url = path; }
|
||||
const res = await fetch(url, opts || {});
|
||||
const text = await res.text();
|
||||
try { return JSON.parse(text); } catch (_) {
|
||||
let json;
|
||||
try { json = JSON.parse(text); } catch (_) {
|
||||
throw new Error("Server returned non-JSON (status " + res.status + "): " + text.slice(0,800));
|
||||
}
|
||||
if (!res.ok) {
|
||||
// attach body for error display but also return json so UI can show debug
|
||||
const err = new Error(json.message || json.detail || ("HTTP " + res.status + ": " + text.slice(0,300)));
|
||||
err.data = json;
|
||||
err.status = res.status;
|
||||
// if 401/403, include profile hint
|
||||
if (res.status === 401) err.message += " (Unauthorized — thiếu profile/token, đang thử lại với ?profile=)";
|
||||
throw err;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
// Wrapper that returns JSON even on error status so UI can show debug
|
||||
async function fetchJSONAllowError(path, opts) {
|
||||
if (SDK && SDK.fetchJSON) {
|
||||
try { return await SDK.fetchJSON(path, opts || {}); } catch (e) {
|
||||
if (e && e.data) return e.data;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
let url = path;
|
||||
try {
|
||||
const cur = new URL(window.location.href);
|
||||
const profile = cur.searchParams.get("profile");
|
||||
if (profile) {
|
||||
const u = new URL(path, window.location.origin);
|
||||
if (!u.searchParams.get("profile")) u.searchParams.set("profile", profile);
|
||||
url = u.pathname + u.search;
|
||||
}
|
||||
} catch (_) {}
|
||||
const res = await fetch(url, opts || {});
|
||||
const text = await res.text();
|
||||
try { return JSON.parse(text); } catch (_) { return { status: "error", message: text.slice(0,800), http_status: res.status }; }
|
||||
}
|
||||
|
||||
function SSHXPage() {
|
||||
|
|
@ -21,7 +85,7 @@
|
|||
const [debug, setDebug] = useState(null);
|
||||
|
||||
const fetchDebug = useCallback(async function(){
|
||||
try { const d = await safeFetchJSON("/api/plugins/sshx-link/debug"); setDebug(d); } catch(_){}
|
||||
try { const d = await fetchJSONAllowError("/api/plugins/sshx-link/debug"); setDebug(d); } catch(e){ setDebug({error: e.message}); }
|
||||
}, []);
|
||||
|
||||
const start = useCallback(async function () {
|
||||
|
|
@ -29,7 +93,10 @@
|
|||
setError(null);
|
||||
setDebug(null);
|
||||
try {
|
||||
var st = await safeFetchJSON("/api/plugins/sshx-link/status");
|
||||
var st = await fetchJSONAllowError("/api/plugins/sshx-link/status");
|
||||
if (st && st.detail === "Unauthorized") {
|
||||
throw new Error("Unauthorized — thử dùng SDK.fetchJSON (đã fix). Hãy hard reload Ctrl+Shift+R sau khi update plugin v2.0.2");
|
||||
}
|
||||
if (st.status === "installing" || st.status === "starting") {
|
||||
setStatus("installing");
|
||||
setError(st.message || "Đang xử lý...");
|
||||
|
|
@ -41,11 +108,10 @@
|
|||
setStatus("running");
|
||||
return;
|
||||
}
|
||||
if (st.status === "error" && st.message) {
|
||||
// show previous error but try to restart anyway
|
||||
var res = await fetchJSONAllowError("/api/plugins/sshx-link/start");
|
||||
if (res.detail === "Unauthorized") {
|
||||
throw new Error("Unauthorized khi gọi /start — plugin frontend chưa dùng SDK auth. Đã fix ở v2.0.2, hãy hard reload.");
|
||||
}
|
||||
var res = await safeFetchJSON("/api/plugins/sshx-link/start");
|
||||
// res may be 202 starting
|
||||
if (res.link) {
|
||||
setLink(res.link);
|
||||
setStatus("running");
|
||||
|
|
@ -54,13 +120,13 @@
|
|||
setError(res.message || "sshx đang khởi động, chờ 2s rồi thử lại... " + (res.output_tail || ""));
|
||||
fetchDebug();
|
||||
} else {
|
||||
setError((res.message || JSON.stringify(res)) + (res.output_tail ? "\nTail: " + res.output_tail : "") + (res.debug ? "\nDebug: " + JSON.stringify(res.debug) : ""));
|
||||
setError((res.message || res.detail || JSON.stringify(res)) + (res.output_tail ? "\nTail: " + res.output_tail : "") + (res.debug ? "\nDebug: " + JSON.stringify(res.debug) : ""));
|
||||
setStatus("error");
|
||||
fetchDebug();
|
||||
}
|
||||
} catch (err) {
|
||||
// err may contain JSON body in message
|
||||
setError(err.message || "Failed to start sshx");
|
||||
const detail = err.data ? (err.data.detail || err.data.message || JSON.stringify(err.data)) : "";
|
||||
setError((err.message || "Failed to start sshx") + (detail ? " | detail: " + detail : ""));
|
||||
setStatus("error");
|
||||
fetchDebug();
|
||||
}
|
||||
|
|
@ -68,7 +134,7 @@
|
|||
|
||||
const stop = useCallback(async function () {
|
||||
try {
|
||||
await safeFetchJSON("/api/plugins/sshx-link/stop", { method: "POST" });
|
||||
await fetchJSONAllowError("/api/plugins/sshx-link/stop", { method: "POST" });
|
||||
setLink(null);
|
||||
setStatus("idle");
|
||||
setError(null);
|
||||
|
|
@ -80,7 +146,8 @@
|
|||
|
||||
const checkStatus = useCallback(async function () {
|
||||
try {
|
||||
const res = await safeFetchJSON("/api/plugins/sshx-link/status");
|
||||
const res = await fetchJSONAllowError("/api/plugins/sshx-link/status");
|
||||
if (res.detail === "Unauthorized") return;
|
||||
if (res.status === "running" && res.link) {
|
||||
setLink(res.link);
|
||||
setStatus("running");
|
||||
|
|
@ -213,7 +280,7 @@
|
|||
}, status === "loading" ? "Đang tạo shell..." : "Start sshx → Lấy link"),
|
||||
React.createElement(Button, { onClick: fetchDebug, variant:"outline", size:"sm"}, "Debug")
|
||||
),
|
||||
React.createElement("p", { className: "text-xs text-muted-foreground", style: { marginTop: "0.75rem"}}, "Nếu lỗi 'No link returned' hoặc timeout, bấm Debug để xem arch/url/binary, kiểm tra server có ra được s3.amazonaws.com và sshx.io không (firewall/security group).")
|
||||
React.createElement("p", { className: "text-xs text-muted-foreground", style: { marginTop: "0.75rem"}}, "Nếu lỗi Unauthorized, hãy hard reload (Ctrl+Shift+R) để load v2.0.2 — bản này dùng SDK.fetchJSON có auth và tự forward ?profile=.")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"label": "sshx.io Terminal",
|
||||
"description": "Tạo shell ở sshx.io và trả link chia sẻ — auto arch, không cần restart",
|
||||
"icon": "Terminal",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"tab": {
|
||||
"path": "/sshx",
|
||||
"position": "end"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: sshx-link
|
||||
version: 2.0.1
|
||||
version: 2.0.2
|
||||
description: Tạo shell ở sshx.io và trả link chia sẻ — auto arch detection, không cần restart dashboard
|
||||
author: sisyphus
|
||||
provides_tools:
|
||||
|
|
|
|||
Loading…
Reference in New Issue