from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.auth.dependencies import get_current_active_user from app.db import get_db_session from app.models.config import Config from app.models.project import Project from app.models.tool_instance import ToolInstance from app.models.user import User from app.schemas.config import ConfigCreate, ConfigRead, ConfigUpdate router = APIRouter(tags=["configs"]) async def _verify_config_ownership( config_obj: Config, user: User, session: AsyncSession ) -> None: if config_obj.scope_type == "user": if config_obj.scope_id != user.id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied") elif config_obj.scope_type == "project": project = await session.get(Project, config_obj.scope_id) if not project or project.owner_id != user.id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied") elif config_obj.scope_type == "tool_instance": ti = await session.get(ToolInstance, config_obj.scope_id) if not ti: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied") project = await session.get(Project, ti.project_id) if not project or project.owner_id != user.id: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Access denied") elif config_obj.scope_type == "global": pass else: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid scope_type") @router.post("/configs", response_model=ConfigRead, status_code=status.HTTP_201_CREATED) async def create_config( config_in: ConfigCreate, current_user: User = Depends(get_current_active_user), session: AsyncSession = Depends(get_db_session), ) -> Config: cfg = Config(**config_in.model_dump()) await _verify_config_ownership(cfg, current_user, session) session.add(cfg) await session.commit() await session.refresh(cfg) return cfg @router.get("/configs", response_model=list[ConfigRead]) async def list_configs( scope_type: str | None = None, scope_id: UUID | None = None, current_user: User = Depends(get_current_active_user), session: AsyncSession = Depends(get_db_session), ) -> list[Config]: stmt = select(Config) if scope_type: stmt = stmt.where(Config.scope_type == scope_type) if scope_id: stmt = stmt.where(Config.scope_id == scope_id) result = await session.execute(stmt) configs = list(result.scalars().all()) allowed = [] for cfg in configs: try: await _verify_config_ownership(cfg, current_user, session) allowed.append(cfg) except HTTPException: pass return allowed @router.get("/configs/{config_id}", response_model=ConfigRead) async def get_config( config_id: UUID, current_user: User = Depends(get_current_active_user), session: AsyncSession = Depends(get_db_session), ) -> Config: cfg = await session.get(Config, config_id) if not cfg: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Config not found") await _verify_config_ownership(cfg, current_user, session) return cfg @router.put("/configs/{config_id}", response_model=ConfigRead) async def update_config( config_id: UUID, config_in: ConfigUpdate, current_user: User = Depends(get_current_active_user), session: AsyncSession = Depends(get_db_session), ) -> Config: cfg = await session.get(Config, config_id) if not cfg: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Config not found") await _verify_config_ownership(cfg, current_user, session) update_data = config_in.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(cfg, field, value) await session.commit() await session.refresh(cfg) return cfg @router.delete("/configs/{config_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_config( config_id: UUID, current_user: User = Depends(get_current_active_user), session: AsyncSession = Depends(get_db_session), ) -> None: cfg = await session.get(Config, config_id) if not cfg: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Config not found") await _verify_config_ownership(cfg, current_user, session) await session.delete(cfg) await session.commit()