CH-J Server Managerspráva serverov nad SSH
Menu
Uverejnený zdroj

CH-J Server Manager

Prechádzať adresáre a súbory pre konkrétne vydanie aplikácie.

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

CH-J Proprietary Software License 1.14 · Zdroj sa poskytuje v rámci CH-J Proprietary Software License 1.14. Jeho dostupnosť nemení licenčné podmienky ani neposkytuje dodatočné práva.

Archív SHA-2563df08cf5e899262a6ce92d7ec2caf5c42b683f1f2b57055662b845ff7dde67a8