Веб-UI: логи kind create, старт/стоп кластеров, документация README
- Потоковые логи в job_store и UI; kind create через Popen с построчным выводом
- POST /clusters/{name}/start|stop; create по сохранённому kind-config.yaml
- Страница /documentation: GET /api/v1/docs/readme, marked+DOMPurify из static/vendor
- Иконки действий, плавающие подсказки, модалка подтверждения вместо confirm
- Makefile: make docker|podman rebuild; compose: монтирование README.md
- Dockerfile: COPY README.md; readme_doc: несколько путей к README
Автор: Сергей Антропов — https://devops.org.ru
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Страница /documentation: загрузка README через API и рендер Markdown.
|
||||
* Зависимости из репозитория: /static/js/vendor/marked.min.js, purify.min.js
|
||||
*
|
||||
* Автор: Сергей Антропов
|
||||
* Сайт: https://devops.org.ru
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const body = document.body;
|
||||
const API = (body.dataset.apiBase || "/api/v1").replace(/\/$/, "");
|
||||
|
||||
function showErr(el, msg) {
|
||||
if (!el) return;
|
||||
el.textContent = msg;
|
||||
el.classList.remove("hidden");
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const article = document.getElementById("readme-doc-article");
|
||||
const errEl = document.getElementById("readme-error");
|
||||
const loadEl = document.getElementById("readme-loading");
|
||||
if (!article || !loadEl) return;
|
||||
|
||||
if (typeof marked === "undefined" || typeof DOMPurify === "undefined") {
|
||||
showErr(errEl, "Не загружены скрипты marked или DOMPurify из /static/js/vendor/.");
|
||||
loadEl.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch(API + "/docs/readme", {
|
||||
headers: { Accept: "text/markdown, text/plain, */*" },
|
||||
});
|
||||
const text = await r.text();
|
||||
if (!r.ok) {
|
||||
var detail = text;
|
||||
try {
|
||||
var j = JSON.parse(text);
|
||||
if (j.detail) detail = typeof j.detail === "string" ? j.detail : JSON.stringify(j.detail);
|
||||
} catch (e) {
|
||||
/* сырой текст */
|
||||
}
|
||||
showErr(errEl, "Не удалось загрузить README: " + (detail || r.statusText));
|
||||
loadEl.classList.add("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
/* marked v15: переносы строк в параграфах (breaks). */
|
||||
try {
|
||||
if (marked.defaults && typeof marked.defaults === "object") {
|
||||
marked.defaults.breaks = true;
|
||||
marked.defaults.gfm = true;
|
||||
}
|
||||
} catch (e) {
|
||||
/* игнорируем, если defaults защищены от записи */
|
||||
}
|
||||
var rawHtml =
|
||||
typeof marked.parse === "function" ? marked.parse(text, { async: false }) : marked(text);
|
||||
article.innerHTML = DOMPurify.sanitize(rawHtml);
|
||||
article.removeAttribute("hidden");
|
||||
loadEl.classList.add("hidden");
|
||||
} catch (e) {
|
||||
showErr(errEl, "Ошибка: " + (e.message || String(e)));
|
||||
loadEl.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", run);
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user