Подробные комментарии ко всем JS-файлам фронтенда

This commit is contained in:
2026-07-31 17:25:44 +04:00
parent 0783319fd1
commit c50aaf59d8
17 changed files with 1155 additions and 196 deletions
+76 -12
View File
@@ -1,13 +1,28 @@
"""
CRUD для таблицы scenario_definitions.
CRUD для таблицы scenario_definitions — определения сценариев.
Сценарии редактируются через UI (модальный редактор), хранятся в БД.
Это позволяет менять сценарии БЕЗ редеплоя приложения.
ОПТИМИСТИЧНАЯ БЛОКИРОВКА (version):
update_definition проверяет version — если другой пользователь уже изменил
сценарий, возвращает conflict=true → UI показывает "Конфликт версий".
МЯГКОЕ УДАЛЕНИЕ:
delete_definition ставит is_active=FALSE — не удаляет физически.
Это позволяет восстановить случайно удалённый сценарий.
SEED-СЦЕНАРИИ:
Сценарии с client_id="" и stand="" — seed (дефолтные, из YAML).
Они доступны всем пользователям но не могут быть удалены через UI.
Функции:
list_definitions(client_id, stand) — список активных
get_definition(def_id, cid, stand) — один
create_definition(cid, stand, name, steps, updated_by) → id
update_definition(def_id, cid, stand, name, steps, version, updated_by) → ok|conflict
delete_definition(def_id, cid, stand) — мягкое (is_active=false)
lock_check(cid, stand) — есть ли RUNNING запуск → 409 если да
list_definitions(client_id, stand) — список активных (+seed)
get_definition(def_id, cid, stand) — один (по id)
create_definition(...) → {id, version}
update_definition(...) → {ok, version} | {conflict: true}
delete_definition(...) → bool
lock_check(cid, stand) — есть ли RUNNING → False
"""
import json
@@ -15,11 +30,20 @@ from db.pool import get_conn, put_conn
def list_definitions(client_id, stand):
"""Список ВСЕХ активных определений: пользовательские + seed.
Seed-сценарии (client_id="" AND stand="") видны всем.
Пользовательские — только для текущего пользователя и стенда.
Returns:
list[dict] с ключами: id, name, steps, version, is_active,
created_at, updated_at, client_id, stand, is_seed (bool)."""
conn = get_conn()
if not conn:
return []
try:
cur = conn.cursor()
# Два условия через OR: пользовательские + seed
cur.execute("""
SELECT id, name, steps, version, is_active, created_at, updated_at,
client_id, stand
@@ -33,8 +57,10 @@ def list_definitions(client_id, stand):
result = []
for r in rows:
d = dict(zip(cols, r))
# Конвертируем datetime → ISO-строка (JSON-совместимо)
d["created_at"] = d["created_at"].isoformat() if d["created_at"] else None
d["updated_at"] = d["updated_at"].isoformat() if d["updated_at"] else None
# is_seed: client_id пустой → seed (из YAML)
d["is_seed"] = (d.get("client_id") == "" and d.get("stand") == "")
result.append(d)
return result
@@ -46,6 +72,12 @@ def list_definitions(client_id, stand):
def get_definition(def_id, client_id, stand):
"""Получить ОДНО определение по id.
Доступ: пользовательские (client_id+stand) ИЛИ seed (пустые).
Returns:
dict или None если не найдено."""
conn = get_conn()
if not conn:
return None
@@ -72,6 +104,16 @@ def get_definition(def_id, client_id, stand):
def create_definition(client_id, stand, name, steps, updated_by=""):
"""Создать ИЛИ перезаписать определение (upsert).
ON CONFLICT (client_id, stand, LOWER(name)):
Если сценарий с таким именем уже существует → UPDATE (перезапись steps, version+1).
Если нет → INSERT.
Это позволяет «сохранить» без проверки существования.
Returns:
{id: int, version: int} или None при ошибке."""
conn = get_conn()
if not conn:
return None
@@ -98,7 +140,15 @@ def create_definition(client_id, stand, name, steps, updated_by=""):
def update_definition(def_id, client_id, stand, name, steps, version, updated_by=""):
"""Возвращает dict {ok, version} или {conflict: True}."""
"""Обновить определение с ОПТИМИСТИЧНОЙ БЛОКИРОВКОЙ.
WHERE version = %s — если версия изменилась (другой пользователь/вкладка),
UPDATE затронет 0 строк → возвращаем {conflict: true}.
Returns:
{ok: True, version: int} — успех
{conflict: True} — версия устарела (409 Conflict)
{error: str} — ошибка БД"""
conn = get_conn()
if not conn:
return {"error": "no db"}
@@ -116,6 +166,7 @@ def update_definition(def_id, client_id, stand, name, steps, version, updated_by
cur.close()
if row:
return {"ok": True, "version": row[0]}
# 0 строк — версия не совпала (кто-то уже изменил)
return {"conflict": True}
except Exception as e:
print(f"[DEFS] update error: {e}", flush=True)
@@ -126,7 +177,13 @@ def update_definition(def_id, client_id, stand, name, steps, version, updated_by
def delete_definition(def_id, client_id, stand):
"""Мягкое удаление."""
"""Мягкое удаление: is_active = FALSE.
Только для пользовательских сценариев (client_id не пустой).
Seed-сценарии не могут быть удалены через этот метод.
Returns:
bool — True если удалён, False если не найден."""
conn = get_conn()
if not conn:
return False
@@ -150,10 +207,17 @@ def delete_definition(def_id, client_id, stand):
def lock_check(client_id, stand):
"""Проверить нет ли активного RUNNING запуска. True = можно запускать."""
"""Проверить что нет активного RUNNING-запуска сценария.
Используется перед запуском нового сценария — предотвращает
одновременный запуск двух сценариев одним пользователем.
Returns:
True — можно запускать (нет RUNNING)
False — нельзя (уже есть RUNNING) → 409 Conflict"""
conn = get_conn()
if not conn:
return True # без БД — разрешаем
return True # без БД — разрешаем (fallback)
try:
cur = conn.cursor()
cur.execute("""
@@ -163,7 +227,7 @@ def lock_check(client_id, stand):
""", (client_id, stand))
row = cur.fetchone()
cur.close()
return row is None
return row is None # None = нет RUNNING = можно запускать
except Exception as e:
print(f"[DEFS] lock_check error: {e}", flush=True)
return True