"""Projects API router - full CRUD for Kumiho projects."""
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from app.dependencies import get_kumiho_client
from app.models.project import ProjectCreate, ProjectUpdate, ProjectResponse
from app.models.common import StatusResponse
from app.core.kumiho_http import translate_kumiho_exception
import kumiho
from kumiho.client import ProjectLimitError
router = APIRouter()
[docs]
@router.get("", response_model=List[ProjectResponse])
async def list_projects(
request: Request,
client: Any = Depends(get_kumiho_client)
):
"""List all projects visible to the authenticated user."""
try:
projects = kumiho.get_projects()
author = getattr(request.state, "kumiho_author", None)
username = getattr(request.state, "kumiho_username", None)
return [ProjectResponse.from_domain(p, author=author, username=username) for p in projects]
except Exception as e:
raise translate_kumiho_exception(e)
[docs]
@router.post("", response_model=ProjectResponse, status_code=status.HTTP_201_CREATED)
async def create_project(
request_state: Request,
request: ProjectCreate,
client: Any = Depends(get_kumiho_client)
):
"""Create a new project."""
try:
project = kumiho.create_project(
name=request.name,
description=request.description
)
author = getattr(request_state.state, "kumiho_author", None)
username = getattr(request_state.state, "kumiho_username", None)
return ProjectResponse.from_domain(project, author=author, username=username)
except ProjectLimitError as e:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"Project limit reached: {str(e)}"
)
except HTTPException:
raise
except Exception as e:
raise translate_kumiho_exception(e)
[docs]
@router.get("/{name}", response_model=ProjectResponse)
async def get_project(
request: Request,
name: str,
client: Any = Depends(get_kumiho_client)
):
"""Get a specific project by name."""
try:
project = kumiho.get_project(name)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
author = getattr(request.state, "kumiho_author", None)
username = getattr(request.state, "kumiho_username", None)
return ProjectResponse.from_domain(project, author=author, username=username)
except HTTPException:
raise
except Exception as e:
raise translate_kumiho_exception(e)
[docs]
@router.patch("/{name}", response_model=ProjectResponse)
async def update_project(
request_state: Request,
name: str,
request: ProjectUpdate,
client: Any = Depends(get_kumiho_client)
):
"""Update a project's properties."""
try:
project = kumiho.get_project(name)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
updated = project.update(
description=request.description,
allow_public=request.allow_public
)
author = getattr(request_state.state, "kumiho_author", None)
username = getattr(request_state.state, "kumiho_username", None)
return ProjectResponse.from_domain(updated, author=author, username=username)
except HTTPException:
raise
except Exception as e:
raise translate_kumiho_exception(e)
[docs]
@router.delete("/{name}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_project(
name: str,
force: bool = Query(False, description="Force deletion even if project has content"),
client: Any = Depends(get_kumiho_client)
):
"""Delete a project."""
try:
project = kumiho.get_project(name)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
project.delete(force=force)
except HTTPException:
raise
except Exception as e:
raise translate_kumiho_exception(e)