diff --git a/site/config/loader.py b/site/config/loader.py index 06fb878..5c8538b 100644 --- a/site/config/loader.py +++ b/site/config/loader.py @@ -80,7 +80,7 @@ SERVICES = _DF["SERVICES"] OPS_INDEX = _DF["OPS_INDEX"] DELAY = float(os.getenv("MOCK_OP_DELAY", "0.1")) -VERSION = "0.5.0" +VERSION = "0.5.1" def get_services(stand_id): diff --git a/site/routes/openapi.py b/site/routes/openapi.py index 34bc181..56e144b 100644 --- a/site/routes/openapi.py +++ b/site/routes/openapi.py @@ -60,10 +60,16 @@ def _build_spec(): "Polygon полностью повторяет контракты реального API: сервисы, инстансы, " "операции, параметры, валидацию — но работает **без реальной инфраструктуры**, " "в памяти, с мгновенным откликом.\n\n" + "### Стенды\n" + "**3 изолированных стенда** — переключайте в выпадающем списке серверов выше:\n" + "- **dev** — 37 сервисов (разработка)\n" + "- **test** — 37 сервисов (тестирование)\n" + "- **prod** — 35 сервисов (продуктив)\n" + "У каждого стенда **свои инстансы, свои операции, своё состояние**.\n\n" "### Особенности\n" "- Все операции синхронные (настраиваемая задержка через `/_mock/delay`)\n" "- Состояние в памяти (1 gunicorn-воркер)\n" - "- Конфигурация сервисов из YAML (генерируется из STANDS)\n" + "- Конфигурация сервисов из YAML (генерируется из terraform)\n" "- Управляемый сброс через `/_mock/reset`\n" "- Симуляция ошибок через `/_mock/fail-next`\n\n" "### Авторизация\n" diff --git a/site/routes/root.py b/site/routes/root.py index 4c1d448..caaa03b 100644 --- a/site/routes/root.py +++ b/site/routes/root.py @@ -49,6 +49,16 @@ def index(): svc_count = len(_cfg.SERVICES) inst_count = len(state.instances) + # Список стендов + stands_list = [] + for sid in _cfg.list_stands(): + svc = _cfg.get_services(sid) + stands_list.append({ + "id": sid, + "count": len(svc), + }) + current_stand = g.stand_id + # Список сервисов для таблицы: name, id, кол-во операций svc_list = [] for sid, svc in sorted(_cfg.SERVICES.items(), key=lambda x: x[1].get("name", x[0])): @@ -69,6 +79,8 @@ def index(): inst_count=inst_count, delay=_cfg.DELAY, svc_list=svc_list, + stands_list=stands_list, + current_stand=current_stand, endpoint=_POLYGON_ENDPOINT, ) diff --git a/site/static/style.css b/site/static/style.css index 83a5769..f05bcfc 100644 --- a/site/static/style.css +++ b/site/static/style.css @@ -115,6 +115,66 @@ main { color: #fff; } +/* ── Stands cards ── */ +.card-stands .card-header { + display: flex; + align-items: baseline; + gap: 12px; +} + +.stands-grid { + display: flex; + gap: 12px; +} + +.stand-card { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 16px 12px; + background: var(--brand-grey-light); + border: 2px solid var(--border); + border-radius: var(--radius); + text-decoration: none; + color: var(--text); + transition: all 0.15s; +} + +.stand-card:hover { + border-color: var(--brand-primary); + background: #eff6ff; +} + +.stand-active { + border-color: var(--brand-primary); + background: #eff6ff; +} + +.stand-name { + font-size: 18px; + font-weight: 700; + text-transform: uppercase; + color: var(--brand-primary); +} + +.stand-count { + font-size: 13px; + color: var(--text-muted); +} + +.stand-arrow { + font-size: 12px; + color: var(--brand-primary); + opacity: 0; + transition: opacity 0.15s; +} + +.stand-card:hover .stand-arrow { + opacity: 1; +} + /* ── Dots ── */ .dot { display: inline-block; diff --git a/site/templates/index.html b/site/templates/index.html index 4111c78..b0231c4 100644 --- a/site/templates/index.html +++ b/site/templates/index.html @@ -30,6 +30,25 @@
+ +{% if stands_list|length > 1 %} +
+
+

🖥 Стенды

+ Вы находитесь на стенде {{ current_stand }} +
+
+ {% for s in stands_list %} + + {{ s.id }} + {{ s.count }} сервисов + + + {% endfor %} +
+
+{% endif %} +