amazon-transcreation/backend/app/api/v1/clients.py
DJP 98fa16bfc3 feat: complete Phase 1-2 scaffold — backend, frontend, pipeline skeleton
Full-stack Amazon AI Transcreation Platform with:
- FastAPI backend (async, PostgreSQL, Redis, Celery) with 11 DB tables
- JWT auth (SSO-ready abstract provider pattern)
- 6-agent pipeline orchestrator with deterministic modules
- Next.js 14 frontend with Amazon branding (Ember fonts, orange/dark theme)
- Job wizard, monitoring HUD, output review, admin screens
- 154 TM/reference files imported, 12 locales configured
- Docker Compose for all services

Agents 2-5 (TM retrieval, ranker, transcreator, compliance) are stubs
pending Phase 3 LLM integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 12:31:43 -04:00

105 lines
3.5 KiB
Python

from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, require_role
from app.models.client import Client
from app.schemas.client import ClientCreate, ClientResponse, ClientUpdate
from app.schemas.common import PaginatedResponse
router = APIRouter(prefix="/clients", tags=["clients"])
@router.post(
"",
response_model=ClientResponse,
status_code=status.HTTP_201_CREATED,
)
async def create_client(
body: ClientCreate,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_role(["admin"])),
) -> ClientResponse:
"""Create a new client (admin only)."""
client = Client(name=body.name, settings=body.settings)
db.add(client)
await db.flush()
return ClientResponse.model_validate(client)
@router.get("", response_model=PaginatedResponse[ClientResponse])
async def list_clients(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_role(["admin"])),
) -> PaginatedResponse[ClientResponse]:
"""List all clients (admin only)."""
count_result = await db.execute(select(func.count(Client.id)))
total = count_result.scalar() or 0
result = await db.execute(
select(Client)
.order_by(Client.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
)
clients = [ClientResponse.model_validate(c) for c in result.scalars().all()]
pages = (total + page_size - 1) // page_size if total > 0 else 1
return PaginatedResponse(
items=clients, total=total, page=page, page_size=page_size, pages=pages
)
@router.get("/{client_id}", response_model=ClientResponse)
async def get_client(
client_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_role(["admin"])),
) -> ClientResponse:
"""Get a client by ID (admin only)."""
result = await db.execute(select(Client).where(Client.id == client_id))
client = result.scalar_one_or_none()
if client is None:
raise HTTPException(status_code=404, detail="Client not found")
return ClientResponse.model_validate(client)
@router.put("/{client_id}", response_model=ClientResponse)
async def update_client(
client_id: UUID,
body: ClientUpdate,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_role(["admin"])),
) -> ClientResponse:
"""Update a client (admin only)."""
result = await db.execute(select(Client).where(Client.id == client_id))
client = result.scalar_one_or_none()
if client is None:
raise HTTPException(status_code=404, detail="Client not found")
update_data = body.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(client, field, value)
await db.flush()
return ClientResponse.model_validate(client)
@router.delete("/{client_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_client(
client_id: UUID,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_role(["admin"])),
) -> None:
"""Delete a client (admin only)."""
result = await db.execute(select(Client).where(Client.id == client_id))
client = result.scalar_one_or_none()
if client is None:
raise HTTPException(status_code=404, detail="Client not found")
await db.delete(client)
await db.flush()