Files
polygon/site/templates/index.html
T

251 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Polygon — Mock Nubes API</title>
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<!-- ── Header ── -->
<header class="header">
<div class="header-top">
<img src="{{ url_for('static', filename='logo.svg') }}" alt="Nubes" class="logo" width="150" height="33">
<span class="badge badge-blue">polygon v{{ version }}</span>
<a href="/swagger" class="swagger-link">📋 Swagger</a>
</div>
<div class="header-status">
<span class="dot dot-green"></span>
<span class="status-text">Работает</span>
<span class="sep">·</span>
<span>Сервисов: <strong>{{ svc_count }}</strong></span>
<span class="sep">·</span>
<span>Инстансов: <strong>{{ inst_count }}</strong></span>
<span class="sep">·</span>
<span>Задержка: <strong>{{ delay }}s</strong></span>
</div>
</header>
<main>
<!-- ── Стенды ── -->
{% if stands_list|length > 1 %}
<section class="card card-stands">
<div class="card-header">
<h2>🖥 Стенды</h2>
<span class="muted">Вы находитесь на стенде <strong>{{ current_stand }}</strong></span>
</div>
<div class="card-body stands-grid">
{% for s in stands_list %}
<a href="/{{ s.id }}/" class="stand-card {% if s.id == current_stand %}stand-active{% endif %}">
<span class="stand-name">{{ s.id }}</span>
<span class="stand-count">{{ s.count }} сервисов</span>
<span class="stand-arrow"></span>
</a>
{% endfor %}
</div>
</section>
{% endif %}
<!-- ── Что такое Polygon ── -->
<section class="card">
<div class="card-header">
<h2>Что такое Polygon?</h2>
</div>
<div class="card-body">
<p>
<strong>Polygon</strong> — это эмулятор REST API облачной платформы
<strong>Nubes</strong> для интеграционных тестов.
Он полностью повторяет контракты реального API: сервисы, инстансы,
операции, параметры, валидацию — но работает <strong>без реальной
инфраструктуры</strong>, в памяти, с мгновенным откликом.
</p>
<div class="grid-2col">
<div>
<h3 class="h4">Зачем нужен</h3>
<ul>
<li>Тестировать создание/изменение/удаление инстансов без реальных ресурсов</li>
<li>Отлаживать UI автодеплоя (<code>app-autotest</code>) на мок-данных</li>
<li>Писать интеграционные тесты с детерминированным состоянием</li>
<li>Проверять краевые случаи (ошибки валидации, сбои операций)</li>
</ul>
</div>
<div>
<h3 class="h4">Как устроен</h3>
<ul>
<li>Flask 3.0 + gunicorn, 1 воркер (состояние в памяти)</li>
<li>Конфигурация сервисов — из YAML (генерируется из STANDS)</li>
<li>Все операции — синхронные, с настраиваемой задержкой</li>
<li>Управляемый сброс состояния через <code>/_mock/reset</code></li>
</ul>
</div>
</div>
</div>
</section>
<!-- ── Как использовать ── -->
<section class="card">
<div class="card-header">
<h2>Как использовать</h2>
{% if stands_list|length > 1 %}<span class="muted">Стенд: <strong>{{ current_stand }}</strong></span>{% endif %}
</div>
<div class="card-body">
<h3 class="h4">📡 Базовые команды</h3>
<p><strong>Получить список сервисов:</strong></p>
<pre class="code-block"><code>curl -s {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/services | python3 -m json.tool</code></pre>
<p><strong>Создать инстанс:</strong></p>
<pre class="code-block"><code>curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/instances \
-H "Content-Type: application/json" \
-d '{"serviceId": 1, "displayName": "my-instance"}' | python3 -m json.tool</code></pre>
<p><strong>Создать операцию, установить параметры, запустить:</strong></p>
<pre class="code-block"><code># 1. Создать операцию (получить opUid из ответа)
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/instanceOperations \
-H "Content-Type: application/json" \
-d '{"instanceUid": "&lt;uid&gt;", "operation": "create", "svcOperationId": 18}'
# 2. Установить каждый параметр (повторить для всех)
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/instanceOperationCfsParams \
-H "Content-Type: application/json" \
-d '{"instanceOperationUid": "&lt;opUid&gt;", "svcOperationCfsParamId": 242, "paramValue": "my-value"}'
# 3. Запустить операцию (будет ждать {{ delay }}s)
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/instanceOperations/&lt;opUid&gt;/run</code></pre>
<hr style="margin: 20px 0; border: none; border-top: 1px solid var(--border);">
<h3 class="h4">🛠 Управление полигоном (служебные эндпоинты)</h3>
<div class="feature-grid">
<div class="feature-item">
<h4>⏱ Задержка операций</h4>
<p>Каждый <code>POST /run</code> ждёт <strong>{{ delay }} секунд</strong> перед ответом — эмуляция времени выполнения в облаке.</p>
<p><strong>Текущая:</strong> <code>{{ delay }}s</code></p>
<pre class="code-block"><code># Сделать операции мгновенными
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/delay/0
# Поставить задержку 3 секунды
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/delay/3
# Максимум — 5 секунд (защита healthcheck'а)</code></pre>
</div>
<div class="feature-item">
<h4>🧹 Сброс состояния</h4>
<p>Удаляет <strong>все</strong> инстансы и операции на текущем стенде. Полезно перед каждым тестом.</p>
<pre class="code-block"><code>curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/reset</code></pre>
</div>
<div class="feature-item">
<h4>💥 Симуляция ошибок</h4>
<p>Следующий <code>POST /run</code> <strong>упадёт</strong> с ошибкой. One-shot: после использования флаг сбрасывается.</p>
<pre class="code-block"><code># Включить
curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/fail-next
# Следующий run → {"ok": false, "error": "mock failure..."}
# После этого — снова работает нормально</code></pre>
</div>
<div class="feature-item">
<h4>📊 Отладка</h4>
<p>Посмотреть текущее состояние всех инстансов и операций:</p>
<pre class="code-block"><code>curl -s {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/state | python3 -m json.tool</code></pre>
<p>Список загруженных сервисов:</p>
<pre class="code-block"><code>curl -s {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }}/{% endif %}api/v1/svc/_mock/services | python3 -m json.tool</code></pre>
</div>
</div>
<hr style="margin: 20px 0; border: none; border-top: 1px solid var(--border);">
<h3 class="h4">🔁 Жизненный цикл операции</h3>
<ol style="line-height: 2;">
<li><code>GET /instanceOperations/default/{svcOperationId}</code> — узнать какие параметры нужны</li>
<li><code>POST /instanceOperations</code> — создать операцию (получить opUid)</li>
<li><code>POST /instanceOperationCfsParams</code> × N — установить каждый параметр</li>
<li><code>GET /{opUid}/validate-cfs</code> — проверить корректность</li>
<li><code>POST /{opUid}/run</code> — выполнить (ждёт <strong>{{ delay }}s</strong>)</li>
<li><code>GET /{opUid}?fields=dtFinish,isSuccessful</code> — проверить результат</li>
</ol>
</div>
</section>
<!-- ── API ── -->
<section class="card">
<div class="card-header">
<h2>API эндпоинты</h2>
</div>
<div class="card-body table-wrap">
<table class="table">
<thead>
<tr>
<th>Метод</th>
<th>Путь</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr><td><span class="method method-get">GET</span></td><td><code>/health</code></td><td>Healthcheck</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/services</code></td><td>Список сервисов</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/services/&lt;id&gt;</code></td><td>Детали сервиса</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/instances</code></td><td>Список инстансов</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/instances/&lt;uid&gt;</code></td><td>Детали инстанса</td></tr>
<tr><td><span class="method method-post">POST</span></td><td><code>/api/v1/svc/instances</code></td><td>Создать инстанс</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/instanceOperations</code></td><td>Список операций</td></tr>
<tr><td><span class="method method-post">POST</span></td><td><code>/api/v1/svc/instanceOperations/&lt;uid&gt;/run</code></td><td>Запустить операцию</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/instanceOperations/&lt;uid&gt;/status</code></td><td>Статус операции</td></tr>
<tr><td><span class="method method-post">POST</span></td><td><code>/api/v1/svc/_mock/reset</code></td><td>Сброс состояния</td></tr>
<tr><td><span class="method method-get">GET</span></td><td><code>/api/v1/svc/_mock/state</code></td><td>Дамп состояния</td></tr>
<tr><td><span class="method method-post">POST</span></td><td><code>/api/v1/svc/_mock/delay/&lt;s&gt;</code></td><td>Задать задержку</td></tr>
<tr><td><span class="method method-post">POST</span></td><td><code>/api/v1/svc/_mock/fail-next</code></td><td>Следующая операция упадёт</td></tr>
</tbody>
</table>
</div>
</section>
<!-- ── Сервисы ── -->
<section class="card">
<div class="card-header">
<h2>Загруженные сервисы <span class="muted">({{ svc_count }})</span></h2>
</div>
<div class="card-body table-wrap">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Название</th>
<th>Операций</th>
<th>CfsParams</th>
<th>StateParams</th>
<th>StateOut</th>
</tr>
</thead>
<tbody>
{% for svc in svc_list %}
<tr>
<td><code>{{ svc.id }}</code></td>
<td>{{ svc.display_name }}</td>
<td>{{ svc.op_count }}</td>
<td>{% if svc.has_cfs %}<span class="dot dot-green"></span>{% else %}<span class="dot dot-gray"></span>{% endif %}</td>
<td>{% if svc.has_state %}<span class="dot dot-green"></span>{% else %}<span class="dot dot-gray"></span>{% endif %}</td>
<td>{% if svc.has_out %}<span class="dot dot-green"></span>{% else %}<span class="dot dot-gray"></span>{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
</main>
<footer class="footer">
polygon v{{ version }} · Nubes Mock API · Flask + gunicorn
</footer>
</body>
</html>