v1.7.0: refactor max_bot to flat structure, add VCF+UserModel+NLP history context, portal pages and proxy fixes
- Refactored max_bot from nested packages to flat module structure - Q2: Extended BotUser model (patronymic, email, org, address, vcf_raw, contact_hash, phone_verified, email_verified, last_interaction, total_conversations, total_tickets) - Q2: VCF parser (FN, N, TEL, EMAIL, ORG, ADR), upsert on re-contact, NLP history context (_get_user_history_context -> YandexGPT) - Q1: Broadcast preview modal with 10s confirmation timer - Q3: CSS var(--white)->var(--bg-card), var(--text)->var(--text-primary) - Q4: bot_settings showNotification(), editable max_bot_id - Q5: Webhook secret passthrough via X-Max-Bot-Api-Secret - Masking sensitive keys, dialog_cleared handler, migrate via _add_column_if_not_exists() - Rate limit (asyncio.sleep 0.5 per 10), dead code removed, conv.intent context in contact.py - Portal pages: bot_consent, bot_kb (edit), bot_settings, bot_test, bot_tickets, portal_settings - Tests: 21/21 passing, added test_yandex_gpt.py, test_email_sender.py - Deploy: deploy_full.sh, schema.sql, seed_knowledge_base.sql
This commit is contained in:
@@ -6,6 +6,7 @@ from fastapi.responses import RedirectResponse, JSONResponse, HTMLResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, delete, or_, and_, Integer, text
|
||||
from sqlalchemy.orm import selectinload, joinedload
|
||||
import httpx
|
||||
from app.database import get_db
|
||||
from app.dependencies import get_current_user, require_role
|
||||
from app.auth import audit_log
|
||||
@@ -1135,45 +1136,6 @@ async def cases_delete(request: Request, db: AsyncSession = Depends(get_db), use
|
||||
return RedirectResponse(url="/service/cases", status_code=302)
|
||||
|
||||
|
||||
# ===================== BOT SETTINGS =====================
|
||||
|
||||
@router.get("/service/bot-settings")
|
||||
async def bot_settings(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_settings.html", ctx(request, user=user, active_page="bot-settings", title="Настройки бота"))
|
||||
|
||||
@router.get("/service/bot-settings/responses")
|
||||
async def bot_responses(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_responses.html", ctx(request, user=user, active_page="bot-responses", title="Шаблоны ответов бота"))
|
||||
|
||||
@router.get("/service/bot-settings/features")
|
||||
async def bot_features(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_features.html", ctx(request, user=user, active_page="bot-features", title="Функции бота"))
|
||||
|
||||
@router.get("/service/bot-settings/categories")
|
||||
async def bot_categories(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_categories.html", ctx(request, user=user, active_page="bot-categories", title="Категории обращений"))
|
||||
|
||||
@router.get("/service/bot-settings/kb")
|
||||
async def bot_kb(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_kb.html", ctx(request, user=user, active_page="bot-kb", title="База знаний бота"))
|
||||
|
||||
@router.get("/service/bot-settings/conversations")
|
||||
async def bot_conversations(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_conversations.html", ctx(request, user=user, active_page="bot-conversations", title="Диалоги бота"))
|
||||
|
||||
@router.get("/service/bot-settings/users")
|
||||
async def bot_users(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_users.html", ctx(request, user=user, active_page="bot-users", title="Пользователи бота"))
|
||||
|
||||
@router.get("/service/bot-settings/test-runner")
|
||||
async def bot_test_runner(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_test_runner.html", ctx(request, user=user, active_page="bot-test-runner", title="Тестовый прогон бота"))
|
||||
|
||||
@router.get("/service/bot-settings/analytics")
|
||||
async def bot_analytics(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_analytics.html", ctx(request, user=user, active_page="bot-analytics", title="Аналитика бота"))
|
||||
|
||||
|
||||
@router.get("/service/portal-settings")
|
||||
async def portal_settings_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/portal_settings.html", ctx(request, user=user, active_page="portal-settings", title="Настройка портала"))
|
||||
@@ -1184,6 +1146,40 @@ async def role_settings_page(request: Request, db: AsyncSession = Depends(get_db
|
||||
return templates.TemplateResponse("pages/role_settings.html", ctx(request, user=user, active_page="role-settings", title="Настройка Ролей"))
|
||||
|
||||
|
||||
# ===================== CHAT-BOT MAX PAGES =====================
|
||||
|
||||
@router.get("/service/bot-settings")
|
||||
async def bot_settings_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)):
|
||||
return templates.TemplateResponse("pages/bot_settings.html", ctx(request, user=user, active_page="bot-settings", title="Настройки чат-бота MAX"))
|
||||
|
||||
|
||||
@router.get("/service/bot-kb")
|
||||
async def bot_kb_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)):
|
||||
return templates.TemplateResponse("pages/bot_kb.html", ctx(request, user=user, active_page="bot-kb", title="База знаний чат-бота"))
|
||||
|
||||
|
||||
@router.get("/service/bot-test")
|
||||
async def bot_test_page_redirect():
|
||||
return RedirectResponse(url="/service/bot-settings", status_code=302)
|
||||
|
||||
|
||||
@router.get("/service/bot-test-content")
|
||||
async def bot_test_content(request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return HTMLResponse("Not authenticated")
|
||||
return templates.TemplateResponse("pages/bot_test.html", {"request": request})
|
||||
|
||||
|
||||
@router.get("/service/bot-consent")
|
||||
async def bot_consent_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)):
|
||||
return templates.TemplateResponse("pages/bot_consent.html", ctx(request, user=user, active_page="bot-consent", title="Согласия ФЗ-152 и рассылка"))
|
||||
|
||||
|
||||
@router.get("/service/bot-tickets")
|
||||
async def bot_tickets_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)):
|
||||
return templates.TemplateResponse("pages/bot_tickets.html", ctx(request, user=user, active_page="bot-tickets", title="Заявки от чат-бота"))
|
||||
|
||||
|
||||
# ---- Role permissions API ----
|
||||
@router.get("/service/api/role-permissions")
|
||||
async def api_role_permissions_get(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
@@ -1242,332 +1238,111 @@ async def api_role_reset(request: Request, user: dict = Depends(get_current_user
|
||||
return JSONResponse({"ok": True, "role": request.session["user"]["role"]})
|
||||
|
||||
|
||||
# ===================== BOT API (proxied from max_bot) =====================
|
||||
# ===================== BOT API PROXY =====================
|
||||
BOT_SERVICE_URL = "http://127.0.0.1:8002"
|
||||
|
||||
import aiohttp
|
||||
|
||||
MAX_BOT_URL = "http://127.0.0.1:8002"
|
||||
|
||||
async def _proxy(method, path, json_body=None):
|
||||
try:
|
||||
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30)) as session:
|
||||
async with session.request(method, f"{MAX_BOT_URL}{path}", json=json_body) as resp:
|
||||
return JSONResponse(await resp.json())
|
||||
except aiohttp.ClientError as e:
|
||||
return JSONResponse({"error": f"Bot service unavailable: {e}", "ok": False}, status_code=503)
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e), "ok": False}, status_code=500)
|
||||
async def _proxy(method: str, path: str, body=None):
|
||||
async with httpx.AsyncClient() as client:
|
||||
if body is not None:
|
||||
resp = await client.request(method, f"{BOT_SERVICE_URL}{path}", json=body)
|
||||
else:
|
||||
resp = await client.request(method, f"{BOT_SERVICE_URL}{path}")
|
||||
return JSONResponse(resp.json(), status_code=resp.status_code)
|
||||
|
||||
|
||||
@router.get("/service/api/bot/settings")
|
||||
async def api_bot_settings(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
async def proxy_bot_settings_get(user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
return await _proxy("GET", "/api/bot/settings")
|
||||
|
||||
|
||||
@router.post("/service/api/bot/settings/save")
|
||||
async def api_bot_settings_save(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
async def proxy_bot_settings_save(request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/settings/save", body)
|
||||
|
||||
@router.get("/service/api/bot/templates")
|
||||
async def api_bot_templates(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/templates")
|
||||
|
||||
@router.post("/service/api/bot/templates/save")
|
||||
async def api_bot_templates_save(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/templates/save", body)
|
||||
|
||||
@router.get("/service/api/bot/features")
|
||||
async def api_bot_features(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/features")
|
||||
|
||||
@router.post("/service/api/bot/features/toggle")
|
||||
async def api_bot_features_toggle(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/features/toggle", body)
|
||||
|
||||
@router.get("/service/api/bot/categories")
|
||||
async def api_bot_categories(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/categories")
|
||||
|
||||
@router.post("/service/api/bot/categories/create")
|
||||
async def api_bot_categories_create(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/categories/create", body)
|
||||
|
||||
@router.post("/service/api/bot/categories/edit")
|
||||
async def api_bot_categories_edit(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/categories/edit", body)
|
||||
|
||||
@router.post("/service/api/bot/categories/delete")
|
||||
async def api_bot_categories_delete(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/categories/delete", body)
|
||||
|
||||
@router.get("/service/api/bot/kb")
|
||||
async def api_bot_kb(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
@router.get("/api/bot/kb")
|
||||
async def proxy_bot_kb_list(user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
return await _proxy("GET", "/api/bot/kb")
|
||||
|
||||
@router.post("/service/api/bot/kb/create")
|
||||
async def api_bot_kb_create(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
|
||||
@router.post("/api/bot/kb")
|
||||
async def proxy_bot_kb_create(request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/kb/create", body)
|
||||
return await _proxy("POST", "/api/bot/kb", body)
|
||||
|
||||
@router.post("/service/api/bot/kb/edit")
|
||||
async def api_bot_kb_edit(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
|
||||
@router.delete("/api/bot/kb/{card_id}")
|
||||
async def proxy_bot_kb_delete(card_id: int, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
return await _proxy("DELETE", f"/api/bot/kb/{card_id}")
|
||||
|
||||
|
||||
@router.get("/api/bot/tickets")
|
||||
async def proxy_bot_tickets_list(user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
return await _proxy("GET", "/api/bot/tickets")
|
||||
|
||||
|
||||
@router.patch("/api/bot/tickets/{ticket_id}")
|
||||
async def proxy_bot_tickets_update(ticket_id: int, request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/kb/edit", body)
|
||||
return await _proxy("PATCH", f"/api/bot/tickets/{ticket_id}", body)
|
||||
|
||||
@router.post("/service/api/bot/kb/delete")
|
||||
async def api_bot_kb_delete(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
|
||||
@router.put("/api/bot/kb/{card_id}")
|
||||
async def proxy_bot_kb_update(card_id: int, request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/kb/delete", body)
|
||||
return await _proxy("PUT", f"/api/bot/kb/{card_id}", body)
|
||||
|
||||
@router.get("/service/api/bot/conversations")
|
||||
async def api_bot_conversations(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/conversations")
|
||||
|
||||
@router.get("/service/api/bot/users")
|
||||
async def api_bot_users(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner")),
|
||||
page: int = Query(1), limit: int = Query(20),
|
||||
sort_by: str = Query("created_at"), sort_dir: str = Query("desc"),
|
||||
consent: str = Query(None)):
|
||||
params = f"?page={page}&limit={limit}&sort_by={sort_by}&sort_dir={sort_dir}"
|
||||
if consent:
|
||||
params += f"&consent={consent}"
|
||||
return await _proxy("GET", f"/api/bot/users{params}")
|
||||
@router.get("/api/bot/users/consented")
|
||||
async def proxy_bot_users_consented(user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
return await _proxy("GET", "/api/bot/users/consented")
|
||||
|
||||
@router.get("/service/api/bot/analytics")
|
||||
async def api_bot_analytics(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/analytics")
|
||||
|
||||
@router.post("/service/api/bot/test-run")
|
||||
async def api_bot_test_run(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/test-run", body)
|
||||
|
||||
@router.get("/service/api/bot/broadcast/recipients")
|
||||
async def api_bot_broadcast_recipients(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner")),
|
||||
q: str = Query(None), page: int = Query(1), limit: int = Query(200)):
|
||||
params = f"?page={page}&limit={limit}"
|
||||
if q:
|
||||
params += f"&q={q}"
|
||||
return await _proxy("GET", f"/api/bot/broadcast/recipients{params}")
|
||||
|
||||
@router.get("/service/api/bot/broadcast/history")
|
||||
async def api_bot_broadcast_history(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner")),
|
||||
page: int = Query(1), limit: int = Query(20)):
|
||||
return await _proxy("GET", f"/api/bot/broadcast/history?page={page}&limit={limit}")
|
||||
|
||||
@router.post("/service/api/bot/broadcast")
|
||||
async def api_bot_broadcast(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
@router.post("/api/bot/broadcast")
|
||||
async def proxy_bot_broadcast(request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/broadcast", body)
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/handoffs")
|
||||
async def bot_handoffs_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_handoffs.html", ctx(request, user=user, active_page="bot-handoffs", title="Handoff-диалоги"))
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/handoffs/poll")
|
||||
async def bot_handoffs_poll(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/handoffs")
|
||||
|
||||
|
||||
@router.post("/service/bot-settings/handoffs/{handoff_id}/take")
|
||||
async def bot_handoff_take(handoff_id: int, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("POST", f"/api/bot/handoffs/{handoff_id}/take")
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/notifications")
|
||||
async def bot_notifications_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_notifications.html", ctx(request, user=user, active_page="bot-notifications", title="Уведомления бота"))
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/notifications/data")
|
||||
async def bot_notifications_data(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/notifications")
|
||||
|
||||
|
||||
@router.post("/service/bot-settings/notifications/{notif_id}/read")
|
||||
async def bot_notification_read(notif_id: int, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("POST", f"/api/bot/notifications/{notif_id}/read")
|
||||
|
||||
|
||||
# ===================== BOT RISK QUESTIONS =====================
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/risk-questions")
|
||||
async def bot_risk_questions_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_risk_questions.html", ctx(request, user=user, active_page="bot-risk-questions", title="Вопросы риск-инжиниринга"))
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/storage")
|
||||
async def bot_storage_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_storage.html", ctx(request, user=user, active_page="bot-storage", title="Яндекс.Диск"))
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/broadcasts")
|
||||
async def bot_broadcasts_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_broadcasts.html", ctx(request, user=user, active_page="bot-broadcasts", title="Рассылки"))
|
||||
|
||||
|
||||
@router.get("/service/api/bot/risk-questions")
|
||||
async def api_bot_risk_questions(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/risk-questions")
|
||||
|
||||
|
||||
@router.post("/service/api/bot/risk-questions/create")
|
||||
async def api_bot_risk_questions_create(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
@router.post("/api/bot/broadcast/preview")
|
||||
async def proxy_bot_broadcast_preview(request: Request, user: dict = Depends(get_current_user)):
|
||||
if not user:
|
||||
return JSONResponse({"ok": False, "error": "Not authenticated"}, status_code=401)
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/risk-questions/create", body)
|
||||
return await _proxy("POST", "/api/bot/broadcast/preview", body)
|
||||
|
||||
|
||||
@router.post("/service/api/bot/risk-questions/edit")
|
||||
async def api_bot_risk_questions_edit(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
@router.post("/api/bot/webhook")
|
||||
async def proxy_bot_webhook(request: Request):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/risk-questions/edit", body)
|
||||
headers = {"Content-Type": "application/json"}
|
||||
secret = request.headers.get("X-Max-Bot-Api-Secret")
|
||||
if secret:
|
||||
headers["X-Max-Bot-Api-Secret"] = secret
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.post(f"{BOT_SERVICE_URL}/webhook", json=body, headers=headers)
|
||||
return JSONResponse(resp.json(), status_code=resp.status_code)
|
||||
|
||||
|
||||
@router.post("/service/api/bot/risk-questions/toggle")
|
||||
async def api_bot_risk_questions_toggle(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/risk-questions/toggle", body)
|
||||
|
||||
|
||||
@router.post("/service/api/bot/risk-questions/delete")
|
||||
async def api_bot_risk_questions_delete(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/risk-questions/delete", body)
|
||||
|
||||
|
||||
# ===================== BOT TICKETS =====================
|
||||
|
||||
|
||||
@router.get("/service/bot-settings/tickets")
|
||||
async def bot_tickets_page(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return templates.TemplateResponse("pages/bot_tickets.html", ctx(request, user=user, active_page="bot-tickets", title="Заявки"))
|
||||
|
||||
|
||||
@router.get("/service/api/bot/tickets")
|
||||
async def api_bot_tickets(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner")),
|
||||
status: str = Query(None), priority: str = Query(None)):
|
||||
params = ""
|
||||
if status: params += f"status={status}&"
|
||||
if priority: params += f"priority={priority}"
|
||||
return await _proxy("GET", f"/api/bot/tickets?{params}")
|
||||
|
||||
|
||||
@router.get("/service/api/bot/tickets/{ticket_id}")
|
||||
async def api_bot_ticket_get(ticket_id: int, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", f"/api/bot/tickets/{ticket_id}")
|
||||
|
||||
|
||||
@router.post("/service/api/bot/tickets/create")
|
||||
async def api_bot_tickets_create(request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", "/api/bot/tickets/create", body)
|
||||
|
||||
|
||||
@router.post("/service/api/bot/tickets/{ticket_id}/message")
|
||||
async def api_bot_tickets_message(ticket_id: int, request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", f"/api/bot/tickets/{ticket_id}/message", body)
|
||||
|
||||
|
||||
@router.post("/service/api/bot/tickets/{ticket_id}/status")
|
||||
async def api_bot_tickets_status(ticket_id: int, request: Request, db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
body = await request.json()
|
||||
return await _proxy("POST", f"/api/bot/tickets/{ticket_id}/status", body)
|
||||
|
||||
|
||||
@router.get("/service/api/bot/engineers")
|
||||
async def api_bot_engineers(db: AsyncSession = Depends(get_db), user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("GET", "/api/bot/engineers")
|
||||
|
||||
|
||||
# ===================== BOT STORAGE (Yandex Disk) =====================
|
||||
|
||||
async def _get_yandex_client(db: AsyncSession):
|
||||
from app.yandex_disk import YandexDiskClient
|
||||
result = await db.execute(text("SELECT value FROM bot_settings WHERE key = 'yandex_disk_token'"))
|
||||
row = result.scalar_one_or_none()
|
||||
if not row:
|
||||
return None
|
||||
return YandexDiskClient(row)
|
||||
|
||||
|
||||
@router.get("/service/api/bot/storage/list")
|
||||
async def api_bot_storage_list(path: str = Query(""), db: AsyncSession = Depends(get_db),
|
||||
user: dict = Depends(require_role("owner"))):
|
||||
client = await _get_yandex_client(db)
|
||||
if not client:
|
||||
return JSONResponse({"error": "Yandex Disk not configured"}, status_code=400)
|
||||
try:
|
||||
items = await client.list_folder(path)
|
||||
return JSONResponse(items)
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
@router.get("/service/api/bot/storage/search")
|
||||
async def api_bot_storage_search(query: str = Query(""), db: AsyncSession = Depends(get_db),
|
||||
user: dict = Depends(require_role("owner"))):
|
||||
client = await _get_yandex_client(db)
|
||||
if not client:
|
||||
return JSONResponse({"error": "Yandex Disk not configured"}, status_code=400)
|
||||
try:
|
||||
items = await client.search(query)
|
||||
return JSONResponse(items)
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
@router.get("/service/api/bot/storage/public-link")
|
||||
async def api_bot_storage_public_link(path: str = Query(""), expiry: int = Query(30),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: dict = Depends(require_role("owner"))):
|
||||
client = await _get_yandex_client(db)
|
||||
if not client:
|
||||
return JSONResponse({"error": "Yandex Disk not configured"}, status_code=400)
|
||||
try:
|
||||
url = await client.get_public_url(path, expiry)
|
||||
return JSONResponse({"url": url})
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
@router.get("/service/api/bot/storage/download")
|
||||
async def api_bot_storage_download(path: str = Query(""), db: AsyncSession = Depends(get_db),
|
||||
user: dict = Depends(require_role("owner"))):
|
||||
client = await _get_yandex_client(db)
|
||||
if not client:
|
||||
return JSONResponse({"error": "Yandex Disk not configured"}, status_code=400)
|
||||
try:
|
||||
info = await client.get_file_info(path)
|
||||
dl_url = info.get("public_url", "")
|
||||
if not dl_url:
|
||||
dl_info = await client._request("GET", "/resources/download", params={"path": path})
|
||||
dl_url = dl_info.get("href", "")
|
||||
return JSONResponse({"url": dl_url})
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
|
||||
# ===================== HANDOFF CLOSE =====================
|
||||
|
||||
|
||||
@router.post("/service/api/bot/handoffs/{handoff_id}/close")
|
||||
async def api_bot_handoff_close(handoff_id: int, db: AsyncSession = Depends(get_db),
|
||||
user: dict = Depends(require_role("owner"))):
|
||||
return await _proxy("POST", f"/api/bot/handoffs/{handoff_id}/close")
|
||||
Reference in New Issue
Block a user