Files
bakery-ia/services/demo_session/app/api/demo_sessions.py
2025-10-06 15:27:01 +02:00

132 lines
4.3 KiB
Python

"""
Demo Sessions API - Atomic CRUD operations on DemoSession model
"""
from fastapi import APIRouter, Depends, HTTPException, Path, Query, Request
from typing import Optional
from uuid import UUID
import structlog
import jwt
from app.api.schemas import DemoSessionCreate, DemoSessionResponse
from app.services import DemoSessionManager
from app.core import get_db, get_redis, RedisClient
from sqlalchemy.ext.asyncio import AsyncSession
from shared.routing import RouteBuilder
router = APIRouter(tags=["demo-sessions"])
logger = structlog.get_logger()
route_builder = RouteBuilder('demo')
@router.post(
route_builder.build_base_route("sessions", include_tenant_prefix=False),
response_model=DemoSessionResponse,
status_code=201
)
async def create_demo_session(
request: DemoSessionCreate,
http_request: Request,
db: AsyncSession = Depends(get_db),
redis: RedisClient = Depends(get_redis)
):
"""Create a new isolated demo session (ATOMIC)"""
logger.info("Creating demo session", demo_account_type=request.demo_account_type)
try:
ip_address = request.ip_address or http_request.client.host
user_agent = request.user_agent or http_request.headers.get("user-agent", "")
session_manager = DemoSessionManager(db, redis)
session = await session_manager.create_session(
demo_account_type=request.demo_account_type,
user_id=request.user_id,
ip_address=ip_address,
user_agent=user_agent
)
# Trigger async data cloning job
from app.services.k8s_job_cloner import K8sJobCloner
import asyncio
job_cloner = K8sJobCloner()
asyncio.create_task(
job_cloner.clone_tenant_data(
session.session_id,
"",
str(session.virtual_tenant_id),
request.demo_account_type
)
)
await session_manager.mark_data_cloned(session.session_id)
await session_manager.mark_redis_populated(session.session_id)
# Generate session token
session_token = jwt.encode(
{
"session_id": session.session_id,
"virtual_tenant_id": str(session.virtual_tenant_id),
"demo_account_type": request.demo_account_type,
"exp": session.expires_at.timestamp()
},
"demo-secret-key",
algorithm="HS256"
)
return {
"session_id": session.session_id,
"virtual_tenant_id": str(session.virtual_tenant_id),
"demo_account_type": session.demo_account_type,
"status": session.status.value,
"created_at": session.created_at,
"expires_at": session.expires_at,
"demo_config": session.metadata.get("demo_config", {}),
"session_token": session_token
}
except Exception as e:
logger.error("Failed to create demo session", error=str(e))
raise HTTPException(status_code=500, detail=f"Failed to create demo session: {str(e)}")
@router.get(
route_builder.build_resource_detail_route("sessions", "session_id", include_tenant_prefix=False),
response_model=dict
)
async def get_session_info(
session_id: str = Path(...),
db: AsyncSession = Depends(get_db),
redis: RedisClient = Depends(get_redis)
):
"""Get demo session information (ATOMIC READ)"""
session_manager = DemoSessionManager(db, redis)
session = await session_manager.get_session(session_id)
if not session:
raise HTTPException(status_code=404, detail="Session not found")
return session.to_dict()
@router.delete(
route_builder.build_resource_detail_route("sessions", "session_id", include_tenant_prefix=False),
response_model=dict
)
async def destroy_demo_session(
session_id: str = Path(...),
db: AsyncSession = Depends(get_db),
redis: RedisClient = Depends(get_redis)
):
"""Destroy demo session and cleanup resources (ATOMIC DELETE)"""
try:
session_manager = DemoSessionManager(db, redis)
await session_manager.destroy_session(session_id)
return {"message": "Session destroyed successfully", "session_id": session_id}
except Exception as e:
logger.error("Failed to destroy session", error=str(e))
raise HTTPException(status_code=500, detail=str(e))