Files
aiform_dev/backend/app/api/n8n_proxy.py
AI Assistant 2d08043b4d feat: Добавлено логирование response для policy/check
Проблема:
- Backend не логировал что именно n8n возвращает для /api/n8n/policy/check
- Не видно откуда брать project_id в response

Исправление:
 Добавлено логирование response.text[:500] для policy/check
 Добавлена обработка ошибок парсинга JSON

Теперь в логах видно полный ответ от n8n!
2025-11-02 10:47:56 +03:00

248 lines
10 KiB
Python

"""
N8N Webhook Proxy Router
Безопасное проксирование запросов к n8n webhooks.
Frontend не знает прямых URL webhooks!
"""
import httpx
import logging
from fastapi import APIRouter, HTTPException, File, UploadFile, Form, Request
from fastapi.responses import JSONResponse
from typing import Optional
from ..config import settings
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/n8n", tags=["n8n-proxy"])
# URL webhooks из .env (будут добавлены)
N8N_POLICY_CHECK_WEBHOOK = getattr(settings, 'n8n_policy_check_webhook', None)
N8N_FILE_UPLOAD_WEBHOOK = getattr(settings, 'n8n_file_upload_webhook', None)
N8N_CREATE_CONTACT_WEBHOOK = getattr(settings, 'n8n_create_contact_webhook', 'https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27')
N8N_CREATE_CLAIM_WEBHOOK = getattr(settings, 'n8n_create_claim_webhook', 'https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d')
@router.post("/policy/check")
async def proxy_policy_check(request: Request):
"""
Проксирует проверку полиса к n8n webhook
Frontend отправляет: POST /api/n8n/policy/check
Backend проксирует к: https://n8n.clientright.pro/webhook/{uuid}
"""
if not N8N_POLICY_CHECK_WEBHOOK:
raise HTTPException(status_code=500, detail="N8N webhook не настроен")
try:
# Получаем JSON body от фронтенда
body = await request.json()
logger.info(f"🔄 Proxy policy check: {body.get('policy_number', 'unknown')}")
# Проксируем запрос к n8n
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
N8N_POLICY_CHECK_WEBHOOK,
json=body,
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
response_text = response.text
logger.info(f"✅ Policy check success. Response: {response_text[:500]}")
try:
return response.json()
except Exception as e:
logger.error(f"❌ Failed to parse JSON: {e}. Response: {response_text[:500]}")
raise HTTPException(status_code=500, detail=f"Ошибка парсинга ответа n8n: {str(e)}")
else:
logger.error(f"❌ N8N returned {response.status_code}: {response.text}")
raise HTTPException(
status_code=response.status_code,
detail=f"N8N error: {response.text}"
)
except httpx.TimeoutException:
logger.error("⏱️ N8N webhook timeout")
raise HTTPException(status_code=504, detail="Таймаут подключения к n8n")
except Exception as e:
logger.error(f"❌ Error proxying to n8n: {e}")
raise HTTPException(status_code=500, detail=f"Ошибка проверки полиса: {str(e)}")
@router.post("/contact/create")
async def proxy_create_contact(request: Request):
"""
Проксирует создание контакта к n8n webhook
Frontend отправляет: POST /api/n8n/contact/create
Backend проксирует к: https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27
"""
if not N8N_CREATE_CONTACT_WEBHOOK:
raise HTTPException(status_code=500, detail="N8N contact webhook не настроен")
try:
body = await request.json()
logger.info(f"🔄 Proxy create contact: phone={body.get('phone', 'unknown')}, session_id={body.get('session_id', 'unknown')}")
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
N8N_CREATE_CONTACT_WEBHOOK,
json=body,
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
response_text = response.text
logger.info(f"✅ Contact created successfully. Response: {response_text[:500]}")
if not response_text or response_text.strip() == '':
logger.error(f"❌ N8N returned empty response")
raise HTTPException(status_code=500, detail="N8N вернул пустой ответ")
try:
return response.json()
except Exception as e:
logger.error(f"❌ Failed to parse JSON: {e}. Response: {response_text[:500]}")
raise HTTPException(status_code=500, detail=f"Ошибка парсинга ответа n8n: {str(e)}")
else:
logger.error(f"❌ N8N returned {response.status_code}: {response.text}")
raise HTTPException(
status_code=response.status_code,
detail=f"N8N error: {response.text}"
)
except httpx.TimeoutException:
logger.error("⏱️ N8N webhook timeout")
raise HTTPException(status_code=504, detail="Таймаут подключения к n8n")
except Exception as e:
logger.error(f"❌ Error proxying to n8n: {e}")
raise HTTPException(status_code=500, detail=f"Ошибка создания контакта: {str(e)}")
@router.post("/upload/file")
async def proxy_file_upload(
file: UploadFile = File(...),
claim_id: Optional[str] = Form(None),
voucher: Optional[str] = Form(None),
session_id: Optional[str] = Form(None),
file_type: Optional[str] = Form(None),
filename: Optional[str] = Form(None),
upload_timestamp: Optional[str] = Form(None)
):
"""
Проксирует загрузку файла к n8n webhook
Frontend отправляет: POST /api/n8n/upload/file (multipart/form-data)
Backend проксирует к: https://n8n.clientright.pro/webhook/{uuid}
"""
if not N8N_FILE_UPLOAD_WEBHOOK:
raise HTTPException(status_code=500, detail="N8N upload webhook не настроен")
try:
logger.info(f"🔄 Proxy file upload: {file.filename} for claim {claim_id}")
# Читаем файл
file_content = await file.read()
# Формируем multipart/form-data для n8n
files = {
'file': (file.filename, file_content, file.content_type)
}
data = {}
if claim_id:
data['claim_id'] = claim_id
if voucher:
data['voucher'] = voucher
if session_id:
data['session_id'] = session_id
if file_type:
data['file_type'] = file_type
if filename:
data['filename'] = filename
if upload_timestamp:
data['upload_timestamp'] = upload_timestamp
# Проксируем запрос к n8n
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(
N8N_FILE_UPLOAD_WEBHOOK,
files=files,
data=data
)
if response.status_code == 200:
logger.info(f"✅ File upload success")
return response.json()
else:
logger.error(f"❌ N8N returned {response.status_code}: {response.text}")
raise HTTPException(
status_code=response.status_code,
detail=f"N8N error: {response.text}"
)
except httpx.TimeoutException:
logger.error("⏱️ N8N webhook timeout")
raise HTTPException(status_code=504, detail="Таймаут загрузки файла")
except Exception as e:
logger.error(f"❌ Error proxying file to n8n: {e}")
raise HTTPException(status_code=500, detail=f"Ошибка загрузки файла: {str(e)}")
@router.post("/claim/create")
async def proxy_create_claim(request: Request):
"""
Проксирует создание черновика заявки к n8n webhook
Frontend отправляет: POST /api/n8n/claim/create
Backend проксирует к: https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d
"""
if not N8N_CREATE_CLAIM_WEBHOOK:
raise HTTPException(status_code=500, detail="N8N claim webhook не настроен")
try:
# Получаем JSON body от фронтенда
body = await request.json()
logger.info(f"🔄 Proxy create claim: event_type={body.get('event_type', 'unknown')}, claim_id={body.get('claim_id', 'unknown')}")
# Проксируем запрос к n8n
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
N8N_CREATE_CLAIM_WEBHOOK,
json=body,
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
response_text = response.text
logger.info(f"✅ Claim created successfully. Response: {response_text[:200]}")
# Проверяем что ответ не пустой
if not response_text or response_text.strip() == '':
logger.error(f"❌ N8N returned empty response")
raise HTTPException(status_code=500, detail="N8N вернул пустой ответ")
try:
return response.json()
except Exception as e:
logger.error(f"❌ Failed to parse JSON: {e}. Response: {response_text[:500]}")
raise HTTPException(status_code=500, detail=f"Ошибка парсинга ответа n8n: {str(e)}")
else:
logger.error(f"❌ N8N returned {response.status_code}: {response.text}")
raise HTTPException(
status_code=response.status_code,
detail=f"N8N error: {response.text}"
)
except httpx.TimeoutException:
logger.error("⏱️ N8N webhook timeout")
raise HTTPException(status_code=504, detail="Таймаут подключения к n8n")
except Exception as e:
logger.error(f"❌ Error proxying to n8n: {e}")
raise HTTPException(status_code=500, detail=f"Ошибка создания заявки: {str(e)}")