v1.2.22: 3rd audit fixes — UniqueViolation→409, lock_check no-db→False, escName add & escape
This commit is contained in:
+1
-1
@@ -32,7 +32,7 @@ from routes.api_scenario_defs import bp_defs as api_scenario_defs_bp
|
|||||||
|
|
||||||
# Версия — показывается в топбаре UI. Меняется при КАЖДОМ изменении кода.
|
# Версия — показывается в топбаре UI. Меняется при КАЖДОМ изменении кода.
|
||||||
# Нужна для фильтрации истории (пользователь видит только записи своей версии).
|
# Нужна для фильтрации истории (пользователь видит только записи своей версии).
|
||||||
VERSION = "1.2.21"
|
VERSION = "1.2.22"
|
||||||
|
|
||||||
# Flask-приложение с Jinja2-шаблонами из папки templates/
|
# Flask-приложение с Jinja2-шаблонами из папки templates/
|
||||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ def lock_check(client_id, stand):
|
|||||||
False — нельзя (уже есть RUNNING) → 409 Conflict"""
|
False — нельзя (уже есть RUNNING) → 409 Conflict"""
|
||||||
conn = get_conn()
|
conn = get_conn()
|
||||||
if not conn:
|
if not conn:
|
||||||
return True # без БД — разрешаем (partial unique index недоступен)
|
return False # без БД — не разрешаем (partial unique index недоступен)
|
||||||
try:
|
try:
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ def api_scenario_run():
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
cur.close()
|
cur.close()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Unique violation (23505) → другая параллельная вставка уже создала RUNNING
|
||||||
|
if hasattr(e, 'pgcode') and e.pgcode == '23505':
|
||||||
|
conn.rollback()
|
||||||
|
return jsonify({"error": "Another scenario is already running"}), 409
|
||||||
print(f"[API] create scenario_run error: {e}", flush=True)
|
print(f"[API] create scenario_run error: {e}", flush=True)
|
||||||
conn.rollback()
|
conn.rollback()
|
||||||
return jsonify({"error": "Failed to create scenario_run"}), 500
|
return jsonify({"error": "Failed to create scenario_run"}), 500
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ async function toggleScenarioSteps(defId) {
|
|||||||
// \ → \\ (backslash)
|
// \ → \\ (backslash)
|
||||||
// ' → \' (terminate JS string literal)
|
// ' → \' (terminate JS string literal)
|
||||||
// HTML-escape уже не нужен — внутри JS-строки в атрибуте HTML-теги не парсятся.
|
// HTML-escape уже не нужен — внутри JS-строки в атрибуте HTML-теги не парсятся.
|
||||||
const escName = def.name.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"');
|
const escName = def.name.replace(/&/g,'&').replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"');
|
||||||
html += `<button class="btn btn-sm" style="margin-top:4px;font-size:11px;background:var(--brand-primary);color:#fff;" onclick="event.stopPropagation();runScenario(${defId})">▶ Запустить</button>`;
|
html += `<button class="btn btn-sm" style="margin-top:4px;font-size:11px;background:var(--brand-primary);color:#fff;" onclick="event.stopPropagation();runScenario(${defId})">▶ Запустить</button>`;
|
||||||
html += `<button class="btn btn-sm" style="margin-top:4px;margin-left:4px;font-size:11px;" onclick="event.stopPropagation();editScenario(${defId})">✏ Редактировать</button>`;
|
html += `<button class="btn btn-sm" style="margin-top:4px;margin-left:4px;font-size:11px;" onclick="event.stopPropagation();editScenario(${defId})">✏ Редактировать</button>`;
|
||||||
html += `<button class="btn btn-sm" style="margin-top:4px;margin-left:4px;font-size:11px;" onclick="event.stopPropagation();cloneScenario(${defId},'${escName}')">📋 Копировать</button>`;
|
html += `<button class="btn btn-sm" style="margin-top:4px;margin-left:4px;font-size:11px;" onclick="event.stopPropagation();cloneScenario(${defId},'${escName}')">📋 Копировать</button>`;
|
||||||
|
|||||||
Reference in New Issue
Block a user