diff --git a/services/api-gateway/app/main.py b/services/api-gateway/app/main.py index e3cf2da..e04f8d5 100644 --- a/services/api-gateway/app/main.py +++ b/services/api-gateway/app/main.py @@ -2,7 +2,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.core.config import get_settings from app.core.database import engine, Base -from app.routers import auth, whatsapp, flows, supervisor +from app.routers import auth, whatsapp, flows, queues, supervisor settings = get_settings() @@ -29,6 +29,7 @@ app.add_middleware( app.include_router(auth.router) app.include_router(whatsapp.router) app.include_router(flows.router) +app.include_router(queues.router) app.include_router(supervisor.router) diff --git a/services/api-gateway/app/routers/queues.py b/services/api-gateway/app/routers/queues.py new file mode 100644 index 0000000..9ecaedb --- /dev/null +++ b/services/api-gateway/app/routers/queues.py @@ -0,0 +1,243 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import List +from uuid import UUID +from app.core.database import get_db +from app.core.security import get_current_user +from app.models.user import User, UserRole +from app.models.queue import Queue, QueueAgent +from app.models.quick_reply import QuickReply +from app.schemas.queue import ( + QueueCreate, QueueUpdate, QueueResponse, QueueDetailResponse, + QueueAgentAdd, QueueAgentResponse, + QuickReplyCreate, QuickReplyUpdate, QuickReplyResponse +) + +router = APIRouter(prefix="/api/queues", tags=["queues"]) + + +def require_admin(current_user: User = Depends(get_current_user)) -> User: + if current_user.role != UserRole.ADMIN: + raise HTTPException(status_code=403, detail="Admin required") + return current_user + + +def require_supervisor(current_user: User = Depends(get_current_user)) -> User: + if current_user.role not in [UserRole.ADMIN, UserRole.SUPERVISOR]: + raise HTTPException(status_code=403, detail="Supervisor or Admin required") + return current_user + + +@router.post("", response_model=QueueResponse) +def create_queue( + request: QueueCreate, + db: Session = Depends(get_db), + current_user: User = Depends(require_admin), +) -> Queue: + queue = Queue(**request.model_dump()) + db.add(queue) + db.commit() + db.refresh(queue) + return queue + + +@router.get("", response_model=List[QueueResponse]) +def list_queues( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> List[QueueResponse]: + queues = db.query(Queue).filter(Queue.is_active == True).all() + result = [] + for q in queues: + q_dict = QueueResponse.model_validate(q).model_dump() + q_dict["agent_count"] = len(q.agents) + result.append(QueueResponse(**q_dict)) + return result + + +@router.get("/{queue_id}", response_model=QueueDetailResponse) +def get_queue( + queue_id: UUID, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> QueueDetailResponse: + queue = db.query(Queue).filter(Queue.id == queue_id).first() + if not queue: + raise HTTPException(status_code=404, detail="Queue not found") + + agents_data = [] + for qa in queue.agents: + agents_data.append(QueueAgentResponse( + id=qa.id, + user_id=qa.user_id, + user_name=qa.user.name if qa.user else None, + user_email=qa.user.email if qa.user else None, + is_supervisor=qa.is_supervisor, + skills=qa.skills or [], + created_at=qa.created_at, + )) + + return QueueDetailResponse( + **QueueResponse.model_validate(queue).model_dump(), + agents=agents_data, + agent_count=len(agents_data), + ) + + +@router.put("/{queue_id}", response_model=QueueResponse) +def update_queue( + queue_id: UUID, + request: QueueUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(require_admin), +) -> Queue: + queue = db.query(Queue).filter(Queue.id == queue_id).first() + if not queue: + raise HTTPException(status_code=404, detail="Queue not found") + + for key, value in request.model_dump(exclude_unset=True).items(): + setattr(queue, key, value) + + db.commit() + db.refresh(queue) + return queue + + +@router.delete("/{queue_id}") +def delete_queue( + queue_id: UUID, + db: Session = Depends(get_db), + current_user: User = Depends(require_admin), +) -> dict: + queue = db.query(Queue).filter(Queue.id == queue_id).first() + if not queue: + raise HTTPException(status_code=404, detail="Queue not found") + + queue.is_active = False + db.commit() + return {"success": True} + + +@router.post("/{queue_id}/agents", response_model=QueueAgentResponse) +def add_agent_to_queue( + queue_id: UUID, + request: QueueAgentAdd, + db: Session = Depends(get_db), + current_user: User = Depends(require_admin), +) -> QueueAgentResponse: + queue = db.query(Queue).filter(Queue.id == queue_id).first() + if not queue: + raise HTTPException(status_code=404, detail="Queue not found") + + existing = db.query(QueueAgent).filter( + QueueAgent.queue_id == queue_id, + QueueAgent.user_id == request.user_id + ).first() + if existing: + raise HTTPException(status_code=400, detail="Agent already in queue") + + qa = QueueAgent( + queue_id=queue_id, + user_id=request.user_id, + is_supervisor=request.is_supervisor, + skills=request.skills, + ) + db.add(qa) + db.commit() + db.refresh(qa) + + return QueueAgentResponse( + id=qa.id, + user_id=qa.user_id, + user_name=qa.user.name if qa.user else None, + user_email=qa.user.email if qa.user else None, + is_supervisor=qa.is_supervisor, + skills=qa.skills or [], + created_at=qa.created_at, + ) + + +@router.delete("/{queue_id}/agents/{user_id}") +def remove_agent_from_queue( + queue_id: UUID, + user_id: UUID, + db: Session = Depends(get_db), + current_user: User = Depends(require_admin), +) -> dict: + qa = db.query(QueueAgent).filter( + QueueAgent.queue_id == queue_id, + QueueAgent.user_id == user_id + ).first() + if not qa: + raise HTTPException(status_code=404, detail="Agent not in queue") + + db.delete(qa) + db.commit() + return {"success": True} + + +@router.get("/quick-replies", response_model=List[QuickReplyResponse]) +def list_quick_replies( + queue_id: UUID = None, + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +) -> List[QuickReply]: + query = db.query(QuickReply) + if queue_id: + query = query.filter( + (QuickReply.queue_id == queue_id) | (QuickReply.queue_id == None) + ) + else: + query = query.filter(QuickReply.queue_id == None) + + return query.all() + + +@router.post("/quick-replies", response_model=QuickReplyResponse) +def create_quick_reply( + request: QuickReplyCreate, + db: Session = Depends(get_db), + current_user: User = Depends(require_supervisor), +) -> QuickReply: + qr = QuickReply( + **request.model_dump(), + created_by=current_user.id, + ) + db.add(qr) + db.commit() + db.refresh(qr) + return qr + + +@router.put("/quick-replies/{reply_id}", response_model=QuickReplyResponse) +def update_quick_reply( + reply_id: UUID, + request: QuickReplyUpdate, + db: Session = Depends(get_db), + current_user: User = Depends(require_supervisor), +) -> QuickReply: + qr = db.query(QuickReply).filter(QuickReply.id == reply_id).first() + if not qr: + raise HTTPException(status_code=404, detail="Quick reply not found") + + for key, value in request.model_dump(exclude_unset=True).items(): + setattr(qr, key, value) + + db.commit() + db.refresh(qr) + return qr + + +@router.delete("/quick-replies/{reply_id}") +def delete_quick_reply( + reply_id: UUID, + db: Session = Depends(get_db), + current_user: User = Depends(require_supervisor), +) -> dict: + qr = db.query(QuickReply).filter(QuickReply.id == reply_id).first() + if not qr: + raise HTTPException(status_code=404, detail="Quick reply not found") + + db.delete(qr) + db.commit() + return {"success": True}