2662619e40
- Потоковые логи в 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
76 lines
2.5 KiB
JavaScript
76 lines
2.5 KiB
JavaScript
/**
|
|
* Страница /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();
|
|
}
|
|
})();
|