Files
aiform_dev/backend/app/main.py
AI Assistant 3621ae6021 feat: Session persistence with Redis + Draft management fixes
- Implement session management API (/api/v1/session/create, verify, logout)
- Add session restoration from localStorage on page reload
- Fix session_id priority when loading drafts (use current, not old from DB)
- Add unified_id and claim_id to wizard payload sent to n8n
- Add Docker volume for frontend HMR (Hot Module Replacement)
- Add comprehensive session logging for debugging

Components updated:
- backend/app/api/session.py (NEW) - Session management endpoints
- backend/app/main.py - Include session router
- frontend/src/components/form/Step1Phone.tsx v2.0 - Create session after SMS
- frontend/src/pages/ClaimForm.tsx v3.8 - Session restoration & priority fix
- frontend/src/components/form/StepWizardPlan.tsx v1.4 - Add unified_id/claim_id
- docker-compose.yml - Add frontend volume for live reload

Session flow:
1. User verifies phone -> session created in Redis (24h TTL)
2. session_token saved to localStorage
3. Page reload -> session restored automatically
4. Draft selected -> current session_id used (not old from DB)
5. Wizard submit -> unified_id, claim_id, session_id sent to n8n
6. Logout -> session removed from Redis & localStorage

Fixes:
- Session token not persisting after page reload
- unified_id missing in n8n webhook payload
- Old session_id from draft overwriting current session
- Frontend changes requiring container rebuild
2025-11-20 18:31:42 +03:00

231 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Ticket Form Intake Platform - FastAPI Backend
"""
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging
from .config import settings
from .services.database import db
from .services.redis_service import redis_service
from .services.rabbitmq_service import rabbitmq_service
from .services.policy_service import policy_service
from .services.s3_service import s3_service
from .api import sms, claims, policy, upload, draft, events, n8n_proxy, session
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Lifecycle events: startup and shutdown
"""
# STARTUP
logger.info("🚀 Starting Ticket Form Intake Platform...")
try:
# Подключаем PostgreSQL
await db.connect()
except Exception as e:
logger.warning(f"⚠️ PostgreSQL not available: {e}")
try:
# Подключаем Redis
await redis_service.connect()
# Инициализируем session API с Redis connection
session.init_redis(redis_service.client)
except Exception as e:
logger.warning(f"⚠️ Redis not available: {e}")
try:
# Подключаем RabbitMQ
await rabbitmq_service.connect()
except Exception as e:
logger.warning(f"⚠️ RabbitMQ not available: {e}")
try:
# Подключаем MySQL (для проверки полисов)
await policy_service.connect()
except Exception as e:
logger.warning(f"⚠️ MySQL Policy DB not available: {e}")
try:
# Подключаем S3 (для загрузки файлов)
s3_service.connect()
except Exception as e:
logger.warning(f"⚠️ S3 storage not available: {e}")
logger.info("✅ Ticket Form Intake Platform started successfully!")
yield
# SHUTDOWN
logger.info("🛑 Shutting down Ticket Form Intake Platform...")
await db.disconnect()
await redis_service.disconnect()
await rabbitmq_service.disconnect()
await policy_service.close()
logger.info("👋 Ticket Form Intake Platform stopped")
# Создаём FastAPI приложение
app = FastAPI(
title="Ticket Form Intake API",
description="API для обработки обращений Ticket Form",
version="1.0.0",
lifespan=lifespan
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# API Routes
app.include_router(sms.router)
app.include_router(claims.router)
app.include_router(policy.router)
app.include_router(upload.router)
app.include_router(draft.router)
app.include_router(events.router)
app.include_router(n8n_proxy.router) # 🔒 Безопасный proxy к n8n webhooks
app.include_router(session.router) # 🔑 Session management через Redis
@app.get("/")
async def root():
"""Главная страница API"""
return {
"message": "🚀 Ticket Form Intake API",
"version": "1.0.0",
"status": "running",
"docs": f"{settings.backend_url}/docs"
}
@app.get("/health")
async def health():
"""Health check - проверка всех сервисов"""
health_status = {
"status": "ok",
"message": "API работает!",
"services": {}
}
# Проверка PostgreSQL
try:
pg_healthy = await db.health_check()
health_status["services"]["postgresql"] = {
"status": "✅ healthy" if pg_healthy else "❌ unhealthy",
"connected": pg_healthy
}
except:
health_status["services"]["postgresql"] = {
"status": "❌ unavailable",
"connected": False
}
# Проверка Redis
try:
redis_healthy = await redis_service.health_check()
health_status["services"]["redis"] = {
"status": "✅ healthy" if redis_healthy else "❌ unhealthy",
"connected": redis_healthy
}
except:
health_status["services"]["redis"] = {
"status": "❌ unavailable",
"connected": False
}
# Проверка RabbitMQ
try:
rabbitmq_healthy = await rabbitmq_service.health_check()
health_status["services"]["rabbitmq"] = {
"status": "✅ healthy" if rabbitmq_healthy else "❌ unhealthy",
"connected": rabbitmq_healthy
}
except:
health_status["services"]["rabbitmq"] = {
"status": "❌ unavailable",
"connected": False
}
# Общий статус
all_healthy = all(
service.get("connected", False)
for service in health_status["services"].values()
)
if not all_healthy:
health_status["status"] = "degraded"
health_status["message"] = "⚠️ Некоторые сервисы недоступны"
return health_status
@app.get("/api/v1/test")
async def test():
"""Тестовый endpoint"""
return {
"success": True,
"message": "✅ Backend API работает!",
"services": {
"redis": f"{settings.redis_host}:{settings.redis_port}",
"postgres": f"{settings.postgres_host}:{settings.postgres_port}",
"ocr": settings.ocr_api_url,
"rabbitmq": f"{settings.rabbitmq_host}:{settings.rabbitmq_port}"
}
}
@app.get("/api/v1/utils/client-ip")
async def get_client_ip(request: Request):
"""Возвращает IP-адрес клиента по HTTP-запросу"""
client_host = request.client.host if request.client else None
return {
"ip": client_host
}
@app.get("/api/v1/info")
async def info():
"""Информация о платформе"""
return {
"platform": settings.app_name,
"version": "1.0.0",
"tech_stack": {
"backend": "Python FastAPI",
"frontend": "React TypeScript",
"database": "PostgreSQL + MySQL",
"cache": "Redis",
"queue": "RabbitMQ",
"storage": "S3 Timeweb"
},
"features": [
"OCR документов",
"AI автозаполнение",
"Проверка статуса выплат",
"СБП выплаты",
"Интеграция с CRM Vtiger"
]
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8200)