UI: автообновление, прогресс, отмена; порт 8080; меню-пилюли и отдельные окна

- Порт хоста по умолчанию 8080 (Chrome ERR_UNSAFE_PORT на 6000); compose, setup, config, README.
- Дашборд: одна hero-карточка, прогресс создания, POST /jobs/{id}/cancel, JobView progress_*.
- job_store: отмена и прогресс (thread-safe); cluster_lifecycle этапы и откат.
- Навигация: стили nav-pill; Swagger/ReDoc/Health через window.open.
- main.py: TemplateResponse(request, …) для Starlette.
- Документация: README, app/docs (api_routes, README); Makefile ps; .gitignore clusters.
This commit is contained in:
Sergey Antropoff
2026-04-04 05:58:11 +03:00
parent 74538423d5
commit 02f4c655b9
17 changed files with 618 additions and 181 deletions

View File

@@ -24,10 +24,10 @@
<span class="nav-tag muted">kind · локальные кластеры</span>
</div>
<nav class="nav-links" aria-label="Разделы">
<a href="/" class="nav-link">Панель</a>
<a href="/docs" class="nav-link" target="_blank" rel="noopener">Swagger</a>
<a href="/redoc" class="nav-link" target="_blank" rel="noopener">ReDoc</a>
<a href="/api/v1/health" class="nav-link" target="_blank" rel="noopener">Health</a>
<a href="/" class="nav-link nav-pill nav-pill--home">Панель</a>
<a href="/docs" class="nav-link nav-pill nav-pill--ext" data-open-window="kind_swagger">Swagger</a>
<a href="/redoc" class="nav-link nav-pill nav-pill--ext" data-open-window="kind_redoc">ReDoc</a>
<a href="/api/v1/health" class="nav-link nav-pill nav-pill--ext" data-open-window="kind_health">Health</a>
</nav>
</div>
</header>
@@ -38,12 +38,31 @@
{% block content %}{% endblock %}
</main>
<footer class="footer muted app-footer">
<footer class="footer app-footer">
{% block footer %}
<span>Данные: том <code>clusters/</code> на хосте · <code>app/docs/api_routes.md</code></span>
<div class="footer-inner">
<p class="muted footer-line">Данные: том <code>clusters/</code> на хосте</p>
<p class="footer-copyright">
© kind-k8s-develop ·
<a href="https://devops.org.ru" target="_blank" rel="noopener">devops.org.ru</a>
</p>
</div>
{% endblock %}
</footer>
{# Отдельное окно браузера для документации и health (не вкладка). #}
<script>
(function () {
var opts = "noopener,noreferrer,width=1240,height=840,scrollbars=yes,resizable=yes";
document.querySelectorAll(".nav-link[data-open-window]").forEach(function (a) {
a.addEventListener("click", function (e) {
e.preventDefault();
var name = a.getAttribute("data-open-window") || "kind_ext";
window.open(a.getAttribute("href"), name, opts);
});
});
})();
</script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@@ -4,39 +4,41 @@
{% extends "base.html" %}
{% block footer %}
<span>Документация API: <code>app/docs/api_routes.md</code> · том данных <code>clusters/</code></span>
<div class="footer-inner">
<p class="footer-line muted">
Документация API: <code>app/docs/api_routes.md</code> · том данных <code>clusters/</code> (не в Git)
</p>
<p class="footer-copyright">
© kind-k8s-develop ·
<a href="https://devops.org.ru" target="_blank" rel="noopener">devops.org.ru</a>
</p>
</div>
{% endblock %}
{% block content %}
<div class="page-intro">
<h1 class="page-title">Панель управления</h1>
<p class="muted page-lead">
Создание и удаление кластеров kind, kubeconfig и просмотр узлов/подов через kubectl внутри контейнера.
</p>
</div>
<div id="status-banner" class="status-banner muted" role="status" aria-live="polite">
Проверка среды…
</div>
<div class="toolbar row spread">
<div class="row" style="margin-top:0;align-items:center;gap:0.75rem">
<button type="button" id="btn-refresh-all" class="btn-secondary">Обновить всё</button>
<label class="row" style="margin-top:0;align-items:center;gap:0.35rem">
<input type="checkbox" id="auto-refresh" />
авто каждые 15 с
</label>
<section class="card hero-panel" id="hero-panel" aria-labelledby="hero-title">
<div class="hero-panel-main">
<h1 class="page-title" id="hero-title">Панель управления</h1>
<p class="muted page-lead">
Создание и удаление кластеров kind, kubeconfig и просмотр узлов/подов через kubectl внутри контейнера.
Данные обновляются автоматически.
</p>
</div>
</div>
<div
id="status-banner"
class="hero-panel-status muted"
role="status"
aria-live="polite"
>
Проверка среды…
</div>
</section>
<section class="grid">
<div class="card">
<h2>Статистика</h2>
<dl id="stats-dl" class="stats-dl" aria-label="Сводка по кластерам и заданиям"></dl>
<p id="stats-err" class="msg hidden" role="alert"></p>
<div class="row">
<button type="button" id="btn-refresh-stats">Обновить статистику</button>
</div>
</div>
<div class="card">
@@ -74,21 +76,43 @@
<label for="fld-workers">Worker-ноды (020)</label>
<input id="fld-workers" name="workers" type="number" min="0" max="20" value="2" />
<button type="submit">Создать кластер</button>
<div class="row create-actions">
<button type="submit">Создать кластер</button>
</div>
</form>
<div id="create-progress-wrap" class="create-progress-wrap hidden" aria-hidden="true">
<p class="create-progress-hint muted" id="create-progress-hint">
Идёт создание — шаг <code>kind create</code> может занять несколько минут при первом pull образов.
</p>
<div
class="progress-track"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="0"
aria-labelledby="create-progress-label"
id="create-progress-track"
>
<div class="progress-bar" id="create-progress-bar" style="width:0%"></div>
</div>
<p class="progress-label" id="create-progress-label"></p>
<button type="button" id="btn-cancel-create" class="btn-secondary">Отменить создание</button>
</div>
<p id="create-msg" class="msg" aria-live="polite"></p>
<details id="job-details" class="job-details hidden">
<summary>Ход задания (JSON)</summary>
<summary>Технические детали (JSON)</summary>
<pre id="job-json" class="mono job-json-pre" tabindex="0"></pre>
</details>
</div>
</section>
<section class="card clusters-card">
<div class="row spread">
<h2>Кластеры</h2>
<button type="button" class="btn-secondary" id="btn-refresh-list">Обновить список</button>
</div>
<h2>Кластеры</h2>
<p class="muted git-hint">
Каталог <code>clusters/</code> в Git не коммитится — только локальные kubeconfig и meta.
</p>
<div class="table-wrap" data-busy="clusters">
<table id="tbl-clusters" aria-label="Список кластеров">
<thead>
@@ -108,10 +132,7 @@
</section>
<section class="card jobs-card">
<div class="row spread">
<h2>Последние задания</h2>
<button type="button" class="btn-secondary" id="btn-refresh-jobs">Обновить</button>
</div>
<h2>Последние задания</h2>
<div class="table-wrap" data-busy="jobs">
<table id="tbl-jobs" aria-label="Последние задания создания">
<thead>