's'
This commit is contained in:
@@ -60,6 +60,14 @@ type ServiceItem = {
|
||||
port?: string;
|
||||
message?: string;
|
||||
};
|
||||
type ShellCrashApplyResponse = {
|
||||
message?: string;
|
||||
path?: string;
|
||||
target_node?: string;
|
||||
default_groups?: string[];
|
||||
selected?: string;
|
||||
service?: ServiceItem;
|
||||
};
|
||||
type SystemSnapshot = {
|
||||
cpu_load?: LoadAvg;
|
||||
disk?: Capacity;
|
||||
@@ -120,6 +128,8 @@ type PreviewMode = "tap" | "double_tap" | "long_press";
|
||||
|
||||
const TABS: TabKey[] = ["overview", "business", "modules", "shellcrash", "android", "config"];
|
||||
const SHELLCRASH_ROOT_IDS = new Set(["shellcrash-configs", "shellcrash-yamls", "shellcrash-jsons"]);
|
||||
const SHELLCRASH_DEFAULT_PROFILE = "LsRzcR3pHoA9.yaml";
|
||||
const SHELLCRASH_DEFAULT_NODE = "5d723acc-singbox_tuic3";
|
||||
|
||||
const apiBase = () =>
|
||||
(typeof process !== "undefined" && process.env.NEXT_PUBLIC_API_URL) || "";
|
||||
@@ -711,6 +721,37 @@ export function LiveConsoleWorkspace({ embeddedShellCrash = false, portalLang }:
|
||||
}
|
||||
};
|
||||
|
||||
const applyShellCrashProfile = async (profilePath?: string) => {
|
||||
const nextPath =
|
||||
profilePath ||
|
||||
(rootId === "shellcrash-yamls" && filePath ? filePath : SHELLCRASH_DEFAULT_PROFILE);
|
||||
setBusy("shellcrash:apply");
|
||||
try {
|
||||
if (rootId === "shellcrash-yamls" && filePath === nextPath) {
|
||||
await fetchJson("/managed_file", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ root: rootId, path: filePath, content: fileContent }),
|
||||
});
|
||||
}
|
||||
const data = await fetchJson<ShellCrashApplyResponse>("/shellcrash/apply_profile", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ path: nextPath, target_node: SHELLCRASH_DEFAULT_NODE }),
|
||||
});
|
||||
const selected = data.selected ? ` | ${data.selected}` : "";
|
||||
notify(`${data.message || tr("ShellCrash 配置已应用", "ShellCrash profile applied")}${selected}`);
|
||||
await Promise.all([refreshServices(), refreshFiles("shellcrash-yamls", "config.yaml")]);
|
||||
setRootId("shellcrash-yamls");
|
||||
setFilePath("config.yaml");
|
||||
setConfigInsightVersion((value) => value + 1);
|
||||
} catch (reason) {
|
||||
notify((reason as Error).message);
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
};
|
||||
|
||||
const saveFile = async () => {
|
||||
if (!rootId || !filePath) return;
|
||||
setBusy("file:save");
|
||||
@@ -1301,12 +1342,19 @@ export function LiveConsoleWorkspace({ embeddedShellCrash = false, portalLang }:
|
||||
<Button className="bg-emerald-600" disabled={!shellcrashService.available || !!busy} onClick={() => void serviceAction("shellcrash", "start")}>{tr("启动", "Start")}</Button>
|
||||
<Button className="bg-amber-600" disabled={!shellcrashService.available || !!busy} onClick={() => void serviceAction("shellcrash", "restart")}>{tr("重启", "Restart")}</Button>
|
||||
<Button className="bg-rose-600" disabled={!shellcrashService.available || !!busy} onClick={() => void serviceAction("shellcrash", "stop")}>{tr("停止", "Stop")}</Button>
|
||||
<Button className="bg-sky-600" disabled={!shellcrashService.available || !!busy} onClick={() => void applyShellCrashProfile(SHELLCRASH_DEFAULT_PROFILE)}>{busy === "shellcrash:apply" ? tr("应用中...", "Applying...") : tr("应用默认配置", "Apply Default")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-4 rounded-2xl border border-sky-400/20 bg-sky-500/10 p-4 text-xs text-sky-100">
|
||||
<p className="font-semibold">{tr("默认代理节点", "Default proxy node")}</p>
|
||||
<p className="mt-2 break-all font-mono text-[11px] text-sky-200">{SHELLCRASH_DEFAULT_NODE}</p>
|
||||
<p className="mt-2 text-sky-100/80">{tr("应用后会写入 config.yaml、重启 ShellCrash,并把包含该节点的选择组切到此节点。", "Applying writes config.yaml, restarts ShellCrash, and selects this node in matching groups.")}</p>
|
||||
</div>
|
||||
<div className="mt-4 rounded-2xl border border-white/8 bg-black/20 p-4 text-xs text-slate-400">
|
||||
<p className="text-white">{tr("快捷文件", "Quick Files")}</p>
|
||||
<div className="mt-3 grid gap-2">
|
||||
<button className="btn-focus rounded-xl border border-white/10 px-3 py-2 text-left text-slate-200" onClick={() => void openManagedFile("shellcrash-yamls", SHELLCRASH_DEFAULT_PROFILE)}>{SHELLCRASH_DEFAULT_PROFILE}</button>
|
||||
<button className="btn-focus rounded-xl border border-white/10 px-3 py-2 text-left text-slate-200" onClick={() => void openManagedFile("shellcrash-yamls", "config.yaml")}>config.yaml</button>
|
||||
<button className="btn-focus rounded-xl border border-white/10 px-3 py-2 text-left text-slate-200" onClick={() => void openManagedFile("shellcrash-configs", "ShellCrash.cfg")}>ShellCrash.cfg</button>
|
||||
<button className="btn-focus rounded-xl border border-white/10 px-3 py-2 text-left text-slate-200" onClick={() => void openManagedFile("shellcrash-configs", "servers.list")}>servers.list</button>
|
||||
@@ -1325,6 +1373,7 @@ export function LiveConsoleWorkspace({ embeddedShellCrash = false, portalLang }:
|
||||
{busy === "file:import" ? tr("导入中...", "Importing...") : tr("导入/替换", "Import/Replace")}
|
||||
<input key={fileImportKey} className="hidden" disabled={!!busy} type="file" accept=".yaml,.yml,.json,.cfg,.env,.list,.txt" onChange={(event) => void importManagedFile(event)} />
|
||||
</label>
|
||||
<Button className="bg-sky-600" disabled={rootId !== "shellcrash-yamls" || !filePath || !!busy} onClick={() => void applyShellCrashProfile(filePath)}>{busy === "shellcrash:apply" ? tr("应用中...", "Applying...") : tr("应用当前 YAML", "Apply YAML")}</Button>
|
||||
<Button className="bg-cyan-600" disabled={!filePath || !!busy} onClick={() => void saveFile()}>{busy === "file:save" ? tr("保存中...", "Saving...") : tr("保存", "Save")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user