docs: clarify Polygon auth and API contracts

This commit is contained in:
Repinoid
2026-10-25 14:20:35 +03:00
parent e3a031ce50
commit 474bfb2600
9 changed files with 128 additions and 17 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ SERVICES = _DF["SERVICES"]
OPS_INDEX = _DF["OPS_INDEX"]
DELAY = float(os.getenv("MOCK_OP_DELAY", "0.1"))
VERSION = "0.5.17"
VERSION = "0.5.18"
def get_services(stand_id):
+1 -1
View File
@@ -71,7 +71,7 @@ def create_instance():
Также возвращает {"instanceUid": uid} в теле — двойная защита
на случай если заголовок не дойдёт.
"""
body = request.get_json(silent=True) or {}
body = request.get_json(silent=True)
# ⛔ body должен быть dict, не массив и не примитив
if not isinstance(body, dict):
+24 -4
View File
@@ -82,7 +82,11 @@ def _build_spec():
"### Авторизация\n"
"- Основные эндпоинты — без авторизации\n"
"- `_mock/*` требуют заголовок `X-Mock-Auth`, только если на сервере "
"задан `MOCK_AUTH_TOKEN`"
"задан `MOCK_AUTH_TOKEN`. Администратор задаёт его в переменных "
"окружения сервиса; Swagger не может получить это значение с сервера.\n"
"- В Swagger нажмите **Authorize**, вставьте токен в поле `X-Mock-Auth` "
"и нажмите **Authorize** в диалоге. Для curl передавайте "
"`-H \"X-Mock-Auth: <token>\"`. Если токен не настроен, заголовок не нужен."
),
"contact": {
"name": "Nubes Platform",
@@ -104,7 +108,11 @@ def _build_spec():
"type": "apiKey",
"in": "header",
"name": "X-Mock-Auth",
"description": "Нужен для _mock/* только если MOCK_AUTH_TOKEN задан на сервере.",
"description": (
"Если MOCK_AUTH_TOKEN задан администратором сервиса, введите его "
"в поле Value и нажмите Authorize. Значение берётся из конфигурации "
"окружения сервера; Swagger его не показывает."
),
},
},
"schemas": {
@@ -553,7 +561,10 @@ def _build_paths():
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOperationResponse"}}},
},
"400": {
"description": "instanceUid обязателен",
"description": (
"Некорректный JSON или тип поля, отсутствует обязательное поле, "
"либо operation не соответствует svcOperationId"
),
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}},
},
"404": {
@@ -696,7 +707,12 @@ def _build_paths():
},
"responses": {
"200": {
"description": "Параметр установлен (пустое тело)",
"description": "Параметр установлен",
"content": {
"application/json": {
"schema": {"type": "object", "example": {}},
},
},
},
"400": {
"description": "Не указан instanceOperationUid или svcOperationCfsParamId",
@@ -706,6 +722,10 @@ def _build_paths():
"description": "Операция не найдена",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}},
},
"409": {
"description": "Операция уже завершена",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}},
},
},
},
},
+5 -1
View File
@@ -169,12 +169,16 @@ def create_operation():
)
if target_op is None:
return jsonify({"error": f"operation '{operation}' not found for service"}), 404
if target_op.get("operation") != operation:
return jsonify({"error": "operation does not match svcOperationId"}), 400
kind = target_op.get("kind", "instance")
action = target_op.get("action", operation)
# Создать операцию в MockState (dtFinish=None — ещё не выполнена)
op_uid = state.create_operation(instance_uid, svc_op_id, operation, kind, action)
op_uid = state.create_operation(
instance_uid, svc_op_id, target_op["operation"], kind, action
)
# ⛔ Location ОБЯЗАТЕЛЕН — без него executor не получит opUid
resp = make_response(jsonify({"instanceOperationUid": op_uid}), 201)
+3
View File
@@ -78,6 +78,7 @@ def index():
svc_list=svc_list,
stands_list=stands_list,
current_stand=current_stand,
swagger_url=(f"/{current_stand}/swagger" if len(stands_list) > 1 else "/swagger"),
endpoint=_POLYGON_ENDPOINT,
)
@@ -92,8 +93,10 @@ def swagger():
from routes.openapi import _build_spec
import json as _json
spec_json = _json.dumps(_build_spec(), ensure_ascii=False)
home_url = f"/{g.stand_id}/" if len(_cfg.list_stands()) > 1 else "/"
return render_template(
"swagger.html",
version=_cfg.VERSION,
spec_json=spec_json,
home_url=home_url,
)
+7 -3
View File
@@ -23,7 +23,7 @@
{% endfor %}
<span class="sep">·</span>
{% endif %}
<a href="/swagger" class="swagger-link">📋 Swagger</a>
<a href="{{ swagger_url }}" class="swagger-link">📋 Swagger</a>
<span class="sep">·</span>
<span class="dot dot-green"></span>
<span class="status-text">Работает</span>
@@ -112,8 +112,12 @@ curl -s -X POST {{ endpoint }}/{% if stands_list|length > 1 %}{{ current_stand }
<hr style="margin: 20px 0; border: none; border-top: 1px solid var(--border);">
<h3 class="h4">🛠 Управление полигоном (служебные эндпоинты)</h3>
<p>Если на сервере задан <code>MOCK_AUTH_TOKEN</code>, для запросов к <code>/_mock/*</code> добавьте
заголовок <code>-H "X-Mock-Auth: &lt;token&gt;"</code>.</p>
<p><strong>Авторизация служебных запросов:</strong> администратор задаёт <code>MOCK_AUTH_TOKEN</code>
в переменных окружения сервиса. Сам токен здесь не показывается. Если защита включена, в Swagger
нажмите <strong>Authorize</strong>, вставьте токен в поле <code>X-Mock-Auth</code> и подтвердите
кнопкой <strong>Authorize</strong>. Для curl добавьте к запросу
<code>-H "X-Mock-Auth: &lt;token&gt;"</code>. Если переменная не задана, токен не нужен.
</p>
<div class="feature-grid">
<div class="feature-item">
+21 -2
View File
@@ -86,11 +86,30 @@
.swagger-ui .info .title {
font-family: system-ui, -apple-system, sans-serif;
}
.auth-help {
max-width: 1460px;
margin: 16px auto;
padding: 12px 16px;
background: #fff;
border: 1px solid #d1d5db;
color: #374151;
font: 14px/1.5 system-ui, sans-serif;
}
</style>
</head>
<body>
<aside class="auth-help">
<strong>Токен для служебных запросов</strong>
<p>Если администратор настроил <code>MOCK_AUTH_TOKEN</code> в переменных окружения сервиса, нажмите
<strong>Authorize</strong> в Swagger, вставьте выданное вам значение в поле <code>X-Mock-Auth</code>
(Value) и подтвердите кнопкой <strong>Authorize</strong>. Токен выдаёт администратор; Swagger его не
показывает. Если защита не включена, авторизация не требуется.
</p>
</aside>
<div id="swagger-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js" crossorigin></script>
@@ -123,12 +142,12 @@
if (link) {
link.innerHTML = '<img class="logo" src="{{ url_for("static", filename="logo.svg") }}" alt="Nubes">' +
'<span class="version-badge">polygon v{{ version }}</span>';
link.href = "/";
link.href = "{{ home_url }}";
}
// Добавляем ссылку «← На главную»
const navLink = document.createElement("a");
navLink.className = "nav-link";
navLink.href = "/";
navLink.href = "{{ home_url }}";
navLink.textContent = "← На главную";
topbar.appendChild(navLink);
}