переделал систему подключения агента к веб-сокету
This commit is contained in:
156
app.py
156
app.py
@@ -16,11 +16,8 @@ import json
|
||||
from pathlib import Path
|
||||
import io
|
||||
import asyncio
|
||||
import contextlib
|
||||
import traceback
|
||||
import time
|
||||
from urllib.parse import quote
|
||||
from db import get_connection
|
||||
|
||||
from repositories.match_formation_repository import (
|
||||
get_match_formations,
|
||||
@@ -29,9 +26,6 @@ from repositories.match_formation_repository import (
|
||||
)
|
||||
|
||||
from parsers.parser_game import run_parser_game
|
||||
from parsers.parser_players import run_parser_players
|
||||
from parsers.parser_schedule import run_parser_schedule
|
||||
from parsers.parser_standings import run_parser_standings
|
||||
from repositories.match_session_repository import (
|
||||
create_match_session,
|
||||
get_match_session_by_token,
|
||||
@@ -91,7 +85,6 @@ from repositories.match_event_repository import (
|
||||
from repositories.auth_repository import get_user_by_username
|
||||
from services.auth_service import (
|
||||
create_auth_session,
|
||||
hash_password,
|
||||
verify_password,
|
||||
get_current_user_from_request,
|
||||
build_not_authenticated_response,
|
||||
@@ -128,6 +121,7 @@ class PublishVmixCommandPayload(BaseModel):
|
||||
commands: List[str] = Field(default_factory=list)
|
||||
target_client_id: str | None = None
|
||||
target_group: str | None = None
|
||||
target_login: str | None = None
|
||||
meta: dict | None = None
|
||||
|
||||
|
||||
@@ -242,6 +236,7 @@ class VmixConnectionManager:
|
||||
message: dict,
|
||||
target_group: str | None = None,
|
||||
session_token: str | None = None,
|
||||
target_login: str | None = None,
|
||||
):
|
||||
async with self.lock:
|
||||
items = list(self.connections.values())
|
||||
@@ -252,6 +247,8 @@ class VmixConnectionManager:
|
||||
continue
|
||||
if target_group and conn.get("group_name") != target_group:
|
||||
continue
|
||||
if target_login and conn.get("operator_name") != target_login:
|
||||
continue
|
||||
if session_token and conn.get("session_token") != session_token:
|
||||
continue
|
||||
matched.append(conn["client_id"])
|
||||
@@ -578,13 +575,20 @@ def download_vmix_project_page(request: Request, session_token: str):
|
||||
|
||||
|
||||
@app.get("/admin/session/{session_token}/download-vmix")
|
||||
def download_vmix_project(session_token: str):
|
||||
def download_vmix_project(request: Request, session_token: str):
|
||||
session_row = get_match_session_by_token(session_token)
|
||||
if not session_row:
|
||||
return JSONResponse({"error": "session_not_found"}, status_code=404)
|
||||
|
||||
current_user = getattr(request.state, "current_user", None) or {}
|
||||
operator_login = current_user.get("username") or None
|
||||
|
||||
try:
|
||||
vmix_bytes = build_vmix_project_bytes(session_token=session_token, match_id=session_row[1])
|
||||
vmix_bytes = build_vmix_project_bytes(
|
||||
session_token=session_token,
|
||||
match_id=session_row[1],
|
||||
operator_login=operator_login,
|
||||
)
|
||||
filename = build_vmix_filename(session_row)
|
||||
|
||||
return StreamingResponse(
|
||||
@@ -732,143 +736,17 @@ def close_session(session_token: str):
|
||||
return RedirectResponse(url="/admin/matches", status_code=303)
|
||||
|
||||
|
||||
def render_admin_db_index(
|
||||
request: Request,
|
||||
parser_output: str | None = None,
|
||||
parser_name: str | None = None,
|
||||
parser_success: bool | None = None,
|
||||
account_output: str | None = None,
|
||||
account_success: bool | None = None,
|
||||
):
|
||||
@app.get("/admin/db", response_class=HTMLResponse)
|
||||
def admin_db_index(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
name="admin_db_index.html",
|
||||
request=request,
|
||||
context={
|
||||
"current_user": getattr(request.state, "current_user", None),
|
||||
"parser_output": parser_output,
|
||||
"parser_name": parser_name,
|
||||
"parser_success": parser_success,
|
||||
"account_output": account_output,
|
||||
"account_success": account_success,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/admin/db", response_class=HTMLResponse)
|
||||
def admin_db_index(request: Request):
|
||||
return render_admin_db_index(request)
|
||||
|
||||
|
||||
@app.post("/admin/db/run-parser", response_class=HTMLResponse)
|
||||
def admin_db_run_parser(request: Request, parser_name: str = Form(...)):
|
||||
current_user = getattr(request.state, "current_user", None)
|
||||
if not current_user or current_user.get("role") != "admin":
|
||||
return RedirectResponse(url="/login", status_code=303)
|
||||
|
||||
parser_map = {
|
||||
"players": ("Игроки", run_parser_players),
|
||||
"schedule": ("Расписание", run_parser_schedule),
|
||||
"standings": ("Турнирка", run_parser_standings),
|
||||
}
|
||||
|
||||
parser_meta = parser_map.get(parser_name)
|
||||
if not parser_meta:
|
||||
return render_admin_db_index(
|
||||
request,
|
||||
parser_output="Неизвестный парсер.",
|
||||
parser_name=parser_name,
|
||||
parser_success=False,
|
||||
)
|
||||
|
||||
title, parser_func = parser_meta
|
||||
buffer = io.StringIO()
|
||||
success = True
|
||||
|
||||
with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(buffer):
|
||||
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Запуск парсера: {title}")
|
||||
try:
|
||||
parser_func()
|
||||
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Парсер завершён успешно")
|
||||
except Exception:
|
||||
success = False
|
||||
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Ошибка при выполнении парсера")
|
||||
print(traceback.format_exc())
|
||||
|
||||
output = buffer.getvalue().strip() or "Парсер не вернул сообщений."
|
||||
return render_admin_db_index(
|
||||
request,
|
||||
parser_output=output,
|
||||
parser_name=title,
|
||||
parser_success=success,
|
||||
)
|
||||
|
||||
|
||||
@app.post("/admin/db/create-account", response_class=HTMLResponse)
|
||||
def admin_db_create_account(
|
||||
request: Request,
|
||||
username: str = Form(...),
|
||||
password: str = Form(...),
|
||||
role: str = Form(default="operator"),
|
||||
):
|
||||
current_user = getattr(request.state, "current_user", None)
|
||||
if not current_user or current_user.get("role") != "admin":
|
||||
return RedirectResponse(url="/login", status_code=303)
|
||||
|
||||
username = username.strip()
|
||||
role = (role or "operator").strip().lower()
|
||||
|
||||
if not username:
|
||||
return render_admin_db_index(request, account_output="Логин обязателен.", account_success=False)
|
||||
|
||||
if not password:
|
||||
return render_admin_db_index(request, account_output="Пароль обязателен.", account_success=False)
|
||||
|
||||
if len(password) < 6:
|
||||
return render_admin_db_index(request, account_output="Пароль должен быть не короче 6 символов.", account_success=False)
|
||||
|
||||
if role not in {"admin", "operator"}:
|
||||
return render_admin_db_index(request, account_output="Роль должна быть admin или operator.", account_success=False)
|
||||
|
||||
password_hash = hash_password(password)
|
||||
conn = get_connection()
|
||||
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO admin_users (username, password_hash, role, is_active)
|
||||
VALUES (%s, %s, %s, TRUE)
|
||||
ON CONFLICT (username) DO NOTHING
|
||||
RETURNING id;
|
||||
""",
|
||||
(username, password_hash, role),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
conn.commit()
|
||||
|
||||
if row:
|
||||
return render_admin_db_index(
|
||||
request,
|
||||
account_output=f"Пользователь создан: {username} (id={row[0]})\nРоль: {role}",
|
||||
account_success=True,
|
||||
)
|
||||
|
||||
return render_admin_db_index(
|
||||
request,
|
||||
account_output=f"Пользователь '{username}' уже существует.",
|
||||
account_success=False,
|
||||
)
|
||||
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
return render_admin_db_index(
|
||||
request,
|
||||
account_output="Ошибка при создании аккаунта:\n" + traceback.format_exc(),
|
||||
account_success=False,
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@app.get("/admin/db/players", response_class=HTMLResponse)
|
||||
def admin_db_players(request: Request, q: str = Query(default="")):
|
||||
# denied = require_role(request, {"admin"})
|
||||
@@ -1700,6 +1578,7 @@ async def ws_vmix_client(
|
||||
"client_id": client_id,
|
||||
"match_id": match_id,
|
||||
"session_token": session_token,
|
||||
"operator_name": operator_name,
|
||||
})
|
||||
|
||||
while True:
|
||||
@@ -1748,6 +1627,7 @@ async def publish_vmix_command(payload: PublishVmixCommandPayload, request: Requ
|
||||
"match_id": payload.match_id,
|
||||
"session_token": payload.session_token,
|
||||
"commands": commands,
|
||||
"target_login": payload.target_login,
|
||||
"meta": payload.meta or {},
|
||||
"sent_at": time.time(),
|
||||
}
|
||||
@@ -1761,6 +1641,7 @@ async def publish_vmix_command(payload: PublishVmixCommandPayload, request: Requ
|
||||
message=message,
|
||||
target_group=payload.target_group,
|
||||
session_token=payload.session_token,
|
||||
target_login=payload.target_login,
|
||||
)
|
||||
|
||||
log_action(
|
||||
@@ -1773,6 +1654,7 @@ async def publish_vmix_command(payload: PublishVmixCommandPayload, request: Requ
|
||||
details={
|
||||
"target_client_id": payload.target_client_id,
|
||||
"target_group": payload.target_group,
|
||||
"target_login": payload.target_login,
|
||||
"commands_count": len(commands),
|
||||
"results": results,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user