#5 api_history: try/finally для conn/cur (как везде) #6 stand_name: точное сравнение URL вместо substring #8 get_real_client: удалён мёртвый алиас #9 UNIQUE индекс idx_runs_op_uid для предотвращения гонок #10 flock flush: f.flush() перед LOCK_UN в ротации лога #7 (отступ scenario.py) пропущен — только косметика, риск сломать else:
This commit is contained in:
@@ -102,15 +102,6 @@ def get_client():
|
||||
return HttpClient(_cached_detect(token), token)
|
||||
|
||||
|
||||
def get_real_client():
|
||||
"""Алиас get_client() — историческая совместимость.
|
||||
|
||||
Раньше get_real_client() всегда ходил в реальный API (для сервисов).
|
||||
Теперь в эмуляции сервисы тоже из полигона → обе функции идентичны.
|
||||
Оставлено чтобы не ломать все места вызова."""
|
||||
return get_client()
|
||||
|
||||
|
||||
def get_client_id():
|
||||
"""Извлечь ClientID из payload JWT-токена (base64url, без проверки подписи).
|
||||
|
||||
|
||||
@@ -52,12 +52,11 @@ def detect_endpoint(token):
|
||||
|
||||
|
||||
def stand_name(endpoint):
|
||||
"""Определить имя стенда по URL.
|
||||
"""Определить имя стенда по URL — точное совпадение endpoint.
|
||||
|
||||
Ищет подстроку 'dev' или 'test' в URL.
|
||||
Если не найдено — '?' (неизвестный стенд)."""
|
||||
for name in ("dev", "test"):
|
||||
if name in (endpoint or ""):
|
||||
Возвращает 'dev'/'test' или '?' если не совпало."""
|
||||
for name, url in (("dev", STANDS[0]), ("test", STANDS[1])):
|
||||
if (endpoint or "").rstrip("/") == url.rstrip("/"):
|
||||
return name
|
||||
return "?"
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ from routes.api_scenario_defs import bp_defs as api_scenario_defs_bp
|
||||
|
||||
# Версия — показывается в топбаре UI. Меняется при КАЖДОМ изменении кода.
|
||||
# Нужна для фильтрации истории (пользователь видит только записи своей версии).
|
||||
VERSION = "1.2.42"
|
||||
VERSION = "1.2.43"
|
||||
|
||||
# Flask-приложение с Jinja2-шаблонами из папки templates/
|
||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||
|
||||
@@ -84,6 +84,10 @@ ALTER TABLE runs ADD COLUMN IF NOT EXISTS app_version VARCHAR(16);
|
||||
ALTER TABLE runs ADD COLUMN IF NOT EXISTS scenario_run_id INTEGER;
|
||||
ALTER TABLE runs ADD COLUMN IF NOT EXISTS step_number INTEGER;
|
||||
ALTER TABLE runs ADD COLUMN IF NOT EXISTS instance_meta JSONB;
|
||||
|
||||
-- Уникальный индекс на op_uid для предотвращения гонок в save_run
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_runs_op_uid
|
||||
ON runs (op_uid) WHERE op_uid IS NOT NULL;
|
||||
"""
|
||||
|
||||
# ── DDL для scenario_runs (запуски сценариев) ──
|
||||
|
||||
+11
-12
@@ -31,7 +31,7 @@ import json
|
||||
import fcntl
|
||||
|
||||
from api.http_client import HttpClient, detect_endpoint, stand_name
|
||||
from api.auth import get_token, get_client, get_real_client, get_client_id, get_stand, get_token_info
|
||||
from api.auth import get_token, get_client, get_client_id, get_stand, get_token_info
|
||||
from api.utils import find_uid, uid_from_location
|
||||
from operations.get_services import get_services, get_service_detail
|
||||
from operations.get_instances import get_instances
|
||||
@@ -70,6 +70,7 @@ def _log(msg):
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(rest)
|
||||
f.flush() # сбросить буфер перед снятием лока
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
except Exception:
|
||||
pass # молча — не ронять запрос из-за лога
|
||||
@@ -395,6 +396,8 @@ def api_log():
|
||||
@bp.route("/api/history")
|
||||
def api_history():
|
||||
"""Последние 50 записей истории тестов (фильтр по client_id + stand)."""
|
||||
conn = None
|
||||
cur = None
|
||||
try:
|
||||
from db.pool import get_conn, put_conn
|
||||
conn = get_conn()
|
||||
@@ -414,20 +417,16 @@ def api_history():
|
||||
rows = cur.fetchall()
|
||||
cols = [d[0] for d in cur.description]
|
||||
result = [dict(zip(cols, r)) for r in rows]
|
||||
# Конвертировать datetime в строку
|
||||
for r in result:
|
||||
if r["created_at"]:
|
||||
r["created_at"] = r["created_at"].isoformat()
|
||||
cur.close()
|
||||
put_conn(conn)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
try:
|
||||
cur.close()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
put_conn(conn)
|
||||
except Exception:
|
||||
pass
|
||||
return jsonify({"error": str(e)}), 500
|
||||
finally:
|
||||
if cur:
|
||||
try: cur.close()
|
||||
except Exception: pass
|
||||
if conn:
|
||||
try: put_conn(conn)
|
||||
except Exception: pass
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ GET /api/operations/<svc_id> — операции и autotest-инста
|
||||
from flask import Blueprint, current_app, render_template, request, make_response, jsonify, redirect
|
||||
|
||||
from api.http_client import HttpClient, detect_endpoint, create_client, stand_name
|
||||
from api.auth import get_token, get_client_id, get_token_info, get_token_masked, get_client, get_real_client, get_stand, get_mode, get_polygon_stand
|
||||
from api.auth import get_token, get_client_id, get_token_info, get_token_masked, get_client, get_stand, get_mode, get_polygon_stand
|
||||
from operations.get_instances import get_organization, get_instances
|
||||
from operations.get_services import get_services, get_service_detail
|
||||
from operations.service_list import load_service_ids
|
||||
|
||||
Reference in New Issue
Block a user