CH-J Toolsspráva serverů přes SSH
Menu
Vydané zdroje

CH-J Tools

Procházejte adresáře a soubory konkrétního vydání aplikace.

Stáhnout zdroje ZIP
CH-J / Server Manager 406 souborů
core-20261024T135205Z-7c487d719de2 Přidáno: · Sestaveno:
index.js Přidáno: Datum není evidováno 10,6 KB · 164 řádků
Kód Stáhnout soubor
1"use strict";
3const api = window.chjPlugin;
4const i18n = window.chjPluginI18n.createI18n("cs");
5const t = i18n.t;
6const elements = Object.fromEntries([
7 "pluginVersion", "closeButton", "sessionSelect", "refreshButton", "sudoPassword", "testButton", "reloadButton", "status",
8 "nginxVersion", "serviceState", "mainConfig", "configCount", "dumpButton", "configFilter", "configList", "editorMode",
9 "editorTitle", "editorMeta", "revertButton", "saveButton", "configEditor", "commandOutput", "clearOutputButton"
10].map((id) => [id, document.querySelector(`#${id}`)]));
12let sessions = [];
13let inspection = null;
14let selectedPath = "";
15let selectedKind = "";
16let originalText = "";
17let busy = false;
19function selectedSession() { return sessions.find((session) => session.sessionId === elements.sessionSelect.value) || null; }
20function sudoPassword() { return elements.sudoPassword.value; }
21function setStatus(message, error = false) { elements.status.textContent = message; elements.status.classList.toggle("error", error); }
22function setOutput(text, error = false) { elements.commandOutput.textContent = String(text || "—"); elements.commandOutput.classList.toggle("error", error); }
23function isDirty() { return Boolean(selectedPath) && elements.configEditor.value !== originalText; }
24function selectedWritable() { return selectedKind === "file" && Boolean(selectedPath); }
26function syncControls() {
27 const connected = Boolean(elements.sessionSelect.value);
28 elements.sessionSelect.disabled = busy;
29 elements.refreshButton.disabled = busy;
30 elements.testButton.disabled = busy || !connected || !inspection?.installed;
31 elements.reloadButton.disabled = busy || !connected || !inspection?.installed;
32 elements.dumpButton.disabled = busy || !connected || !inspection?.installed;
33 elements.revertButton.disabled = busy || !selectedPath;
34 elements.saveButton.disabled = busy || !selectedWritable() || !isDirty();
35 elements.configEditor.readOnly = busy || !selectedWritable();
38function setBusy(value) { busy = value; syncControls(); }
40function renderIdentity() {
41 elements.nginxVersion.textContent = inspection?.installed ? (inspection.version || t("nainstalován")) : t("nenalezen");
42 elements.serviceState.textContent = inspection?.serviceState || "—";
43 elements.serviceState.className = inspection?.serviceState === "active" ? "state-active" : "state-inactive";
44 elements.mainConfig.textContent = inspection?.configPath || "—";
45 elements.mainConfig.title = inspection?.configPath || "";
46 elements.configCount.textContent = String(inspection?.configs?.length || 0);
49function renderConfigList() {
50 const query = elements.configFilter.value.trim().toLowerCase();
51 const files = (inspection?.configs || []).filter((item) => item.path.toLowerCase().includes(query));
52 const fragment = document.createDocumentFragment();
53 for (const file of files) {
54 const button = document.createElement("button");
55 button.type = "button";
56 button.className = `config-item${file.path === selectedPath ? " active" : ""}`;
57 button.dataset.path = file.path;
58 button.dataset.kind = file.kind;
59 const path = document.createElement("span"); path.textContent = file.path;
60 const type = document.createElement("small"); type.textContent = file.kind === "symlink" ? t("symlink · pouze čtení") : t("soubor");
61 button.append(path, type); fragment.append(button);
62 }
63 elements.configList.replaceChildren(fragment);
64 if (!files.length) { const empty = document.createElement("p"); empty.className = "empty"; empty.textContent = t("Filtru neodpovídá žádný konfigurační soubor."); elements.configList.replaceChildren(empty); }
67async function loadSessions() {
68 const previous = elements.sessionSelect.value;
69 sessions = (await api.sessions.list()).filter((session) => session.state === "connected");
70 elements.sessionSelect.replaceChildren(new Option(sessions.length ? t("Vyberte relaci…") : t("Žádná připojená relace"), ""));
71 for (const session of sessions) elements.sessionSelect.add(new Option(`${session.label} — ${session.username}@${session.host}`, session.sessionId));
72 elements.sessionSelect.value = sessions.some((item) => item.sessionId === previous) ? previous : (sessions[0]?.sessionId || "");
75async function inspect() {
76 if (!elements.sessionSelect.value) { inspection = null; renderIdentity(); renderConfigList(); syncControls(); return; }
77 setBusy(true); setStatus(t("Zjišťuji instalaci, službu a konfigurační soubory NGINX…"));
78 try {
79 inspection = await api.nginx.inspect(elements.sessionSelect.value);
80 renderIdentity(); renderConfigList();
81 setStatus(inspection.installed ? t("NGINX {version} je dostupný; nalezeno {count} souborů.", { version: inspection.version || "", count: inspection.configs.length }) : t("NGINX nebyl na serveru nalezen."), !inspection.installed);
82 } catch (error) { inspection = null; renderIdentity(); renderConfigList(); setStatus(error?.message || String(error), true); }
83 finally { setBusy(false); }
86async function readConfig(path, kind) {
87 if (isDirty() && !window.confirm(t("Zahodit neuložené změny aktuální konfigurace?"))) return;
88 setBusy(true); setStatus(t("Načítám {path}…", { path }));
89 try {
90 const result = await api.nginx.readConfig(elements.sessionSelect.value, { path, sudoPassword: sudoPassword() });
91 selectedPath = result.path; selectedKind = kind; originalText = result.text;
92 elements.configEditor.value = result.text;
93 elements.editorMode.textContent = kind === "symlink" ? t("NÁHLED SYMLINKU") : "EDITOR";
94 elements.editorTitle.textContent = result.path;
95 elements.editorMeta.textContent = `${result.size.toLocaleString(i18n.locale())} B · ${kind === "symlink" ? t("upravte zdroj v sites-available") : t("změny se ukládají se zálohou")}`;
96 renderConfigList(); setStatus(t("Konfigurace byla načtena."));
97 } catch (error) { setStatus(error?.message || String(error), true); }
98 finally { elements.sudoPassword.value = ""; setBusy(false); }
101async function runTest() {
102 setBusy(true); setStatus(t("Spouštím nginx -t…"));
103 try {
104 const result = await api.nginx.testConfig(elements.sessionSelect.value, sudoPassword());
105 setOutput(result.output, !result.ok); setStatus(result.ok ? t("Konfigurace NGINX je platná.") : t("Test konfigurace selhal."), !result.ok);
106 } catch (error) { setOutput(error?.message || String(error), true); setStatus(error?.message || String(error), true); }
107 finally { elements.sudoPassword.value = ""; setBusy(false); }
110async function dumpEffectiveConfig() {
111 if (isDirty() && !window.confirm(t("Zahodit neuložené změny a zobrazit efektivní konfiguraci?"))) return;
112 setBusy(true); setStatus(t("Načítám efektivní konfiguraci pomocí nginx -T…"));
113 try {
114 const result = await api.nginx.dumpConfig(elements.sessionSelect.value, sudoPassword());
115 selectedPath = ""; selectedKind = ""; originalText = result.text; elements.configEditor.value = result.text;
116 elements.editorMode.textContent = t("EFEKTIVNÍ KONFIGURACE"); elements.editorTitle.textContent = "nginx -T";
117 elements.editorMeta.textContent = t("Kombinovaný výstup je pouze pro čtení."); renderConfigList(); setStatus(t("Efektivní konfigurace byla načtena."));
118 } catch (error) { setOutput(error?.message || String(error), true); setStatus(error?.message || String(error), true); }
119 finally { elements.sudoPassword.value = ""; setBusy(false); }
122async function saveConfig() {
123 if (!selectedWritable() || !isDirty()) return;
124 if (!window.confirm(t("Uložit {path}, vytvořit zálohu a spustit nginx -t? Neplatná konfigurace se automaticky vrátí.", { path: selectedPath }))) return;
125 setBusy(true); setStatus(t("Ukládám zálohu a ověřuji novou konfiguraci…"));
126 try {
127 const result = await api.nginx.saveConfig(elements.sessionSelect.value, { path: selectedPath, text: elements.configEditor.value, sudoPassword: sudoPassword() });
128 originalText = elements.configEditor.value; setOutput(`${result.testOutput}\n\n${t("Záloha: {path}", { path: result.backupPath })}`); setStatus(t("Konfigurace byla uložena a nginx -t uspěl. Reload zatím nebyl proveden."));
129 } catch (error) { setOutput(error?.message || String(error), true); setStatus(error?.message || String(error), true); }
130 finally { elements.sudoPassword.value = ""; setBusy(false); }
133async function reloadNginx() {
134 if (isDirty()) { setStatus(t("Nejprve uložte nebo zahoďte neuložené změny."), true); return; }
135 if (!window.confirm(t("Provést graceful reload NGINX? Core před reloadem znovu spustí nginx -t."))) return;
136 setBusy(true); setStatus(t("Ověřuji konfiguraci a provádím graceful reload…"));
137 try {
138 const result = await api.nginx.reload(elements.sessionSelect.value, sudoPassword(), true);
139 setOutput(result.output); setStatus(t("NGINX byl bezpečně reloadován.")); await inspect();
140 } catch (error) { setOutput(error?.message || String(error), true); setStatus(error?.message || String(error), true); }
141 finally { elements.sudoPassword.value = ""; setBusy(false); }
144elements.closeButton.addEventListener("click", () => api.close());
145elements.refreshButton.addEventListener("click", async () => { await loadSessions(); await inspect(); });
146elements.sessionSelect.addEventListener("change", async () => { selectedPath = ""; selectedKind = ""; originalText = ""; elements.configEditor.value = ""; await inspect(); });
147elements.configFilter.addEventListener("input", renderConfigList);
148elements.configList.addEventListener("click", (event) => { const item = event.target.closest("button[data-path]"); if (item) void readConfig(item.dataset.path, item.dataset.kind); });
149elements.configEditor.addEventListener("input", syncControls);
150elements.revertButton.addEventListener("click", () => { if (selectedPath) void readConfig(selectedPath, selectedKind); });
151elements.saveButton.addEventListener("click", saveConfig);
152elements.testButton.addEventListener("click", runTest);
153elements.dumpButton.addEventListener("click", dumpEffectiveConfig);
154elements.reloadButton.addEventListener("click", reloadNginx);
155elements.clearOutputButton.addEventListener("click", () => setOutput(""));
156window.addEventListener("beforeunload", (event) => { if (isDirty()) { event.preventDefault(); event.returnValue = ""; } });
158(async () => {
159 try { i18n.setLanguage(await api.ui.getLanguage()); } catch {}
160 i18n.apply(document);
161 api.ui.onLanguageChanged((language) => { i18n.setLanguage(language); i18n.apply(document); renderIdentity(); renderConfigList(); });
162 const info = await api.getInfo(); elements.pluginVersion.textContent = `${info.name} v${info.version} · ${info.publisher}`;
163 await loadSessions(); await inspect();
164})().catch((error) => setStatus(error?.message || String(error), true));
SHA-25647d7f2fe57cd11c862134f06df336b082f9af08442d8ed76c2f65c5b8291714b

CH-J Proprietary Software License 1.14 · Zdroje jsou zveřejněny pod CH-J Proprietary Software License 1.14. Jejich dostupnost nemění licenční podmínky ani neposkytuje další oprávnění.

SHA-256 archivu476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6
Soubory