v1.8.3: добавлены docstrings и комментарии по AGENTS.md ко всем ключевым файлам
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
Главный модуль max_bot FastAPI-приложения.
|
||||
|
||||
Эндпоинты:
|
||||
- /webhook: входящие webhook-и от MAX Platform
|
||||
- /health: healthcheck
|
||||
- /api/bot/*: CRUD для настроек, KB, заявок, диалогов, пользователей
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -54,6 +62,7 @@ app.add_middleware(
|
||||
|
||||
|
||||
async def _apply_migrations():
|
||||
"""Автоматические миграции: добавление колонок/таблиц при старте."""
|
||||
async with engine.begin() as conn:
|
||||
try:
|
||||
await conn.execute(text("""
|
||||
@@ -101,6 +110,7 @@ async def _apply_migrations():
|
||||
|
||||
|
||||
async def _add_column_if_not_exists(conn, table: str, column: str, col_type: str):
|
||||
"""Безопасное добавление колонки (idempotent)."""
|
||||
from sqlalchemy import text as sa_text
|
||||
_validate_sql_name(table)
|
||||
_validate_sql_name(column)
|
||||
@@ -121,6 +131,7 @@ async def _add_column_if_not_exists(conn, table: str, column: str, col_type: str
|
||||
|
||||
|
||||
async def _create_table_if_not_exists(conn, table: str, create_sql: str):
|
||||
"""Безопасное создание таблицы (idempotent)."""
|
||||
from sqlalchemy import text as sa_text
|
||||
try:
|
||||
await conn.execute(sa_text(f"""
|
||||
@@ -138,12 +149,14 @@ async def _create_table_if_not_exists(conn, table: str, create_sql: str):
|
||||
|
||||
|
||||
def _validate_sql_name(name: str) -> None:
|
||||
"""Валидация SQL-идентификатора (только буквы, цифры, подчёркивания)."""
|
||||
import re
|
||||
if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_ ]*$', name):
|
||||
raise ValueError(f"Invalid SQL identifier: {name}")
|
||||
|
||||
|
||||
def _validate_sql_type(col_type: str) -> None:
|
||||
"""Валидация SQL-типа колонки (разрешённые типы)."""
|
||||
import re
|
||||
allowed = r'^(VARCHAR\(\d+\)|TEXT|INTEGER( DEFAULT \d+)?|BOOLEAN( DEFAULT (TRUE|FALSE))?|TIMESTAMP)$'
|
||||
if not re.match(allowed, col_type.strip()):
|
||||
@@ -151,6 +164,7 @@ def _validate_sql_type(col_type: str) -> None:
|
||||
|
||||
|
||||
async def _log_processing(user_id: int, step: str, status: str = "ok", message: str = None, detail: str = None, conversation_id: int = None):
|
||||
"""Запись лога обработки в bot_processing_logs."""
|
||||
try:
|
||||
async with async_session() as db:
|
||||
db.add(BotProcessingLog(
|
||||
@@ -167,6 +181,7 @@ async def _log_processing(user_id: int, step: str, status: str = "ok", message:
|
||||
|
||||
|
||||
async def _seed_default_settings():
|
||||
"""Заполнение настроек по умолчанию при первом запуске."""
|
||||
defaults = {
|
||||
"assistant_name": "София",
|
||||
"assistant_role": "Представитель компании AegisOne Engineering, помогающий клиентам с запросами по безопасности",
|
||||
@@ -185,6 +200,7 @@ async def _seed_default_settings():
|
||||
|
||||
|
||||
async def _register_webhook():
|
||||
"""Регистрация webhook-а в MAX Platform."""
|
||||
try:
|
||||
subs = await max_api.get_subscriptions()
|
||||
logger.info(f"Current subscriptions: {subs}")
|
||||
@@ -199,6 +215,7 @@ async def _register_webhook():
|
||||
|
||||
|
||||
def _verify_secret(request: Request) -> bool:
|
||||
"""Проверка webhook-секрета (пустой заголовок = OK для обратной совместимости)."""
|
||||
secret = request.headers.get("X-Max-Bot-Api-Secret", "")
|
||||
if not secret:
|
||||
return True
|
||||
@@ -206,6 +223,7 @@ def _verify_secret(request: Request) -> bool:
|
||||
|
||||
|
||||
async def _get_active_conv_id(user_id: int) -> int | None:
|
||||
"""Получение ID активного диалога пользователя."""
|
||||
try:
|
||||
async with async_session() as db:
|
||||
result = await db.execute(
|
||||
@@ -798,6 +816,7 @@ async def api_delete_conversation(conv_id: int):
|
||||
|
||||
@app.delete("/api/bot/users/{user_id}")
|
||||
async def api_delete_bot_user(user_id: int):
|
||||
"""Каскадное удаление пользователя: BotUser + BotConversation + BotMessage + BotProcessingLog. Заявки остаются."""
|
||||
from app.models import BotMessage, BotProcessingLog
|
||||
from sqlalchemy import delete as sa_delete
|
||||
async with async_session() as db:
|
||||
|
||||
Reference in New Issue
Block a user