from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from pydantic import BaseModel from backend.database import get_db, Integration from backend.auth import get_current_user, User from backend.composio_client import get_composio router = APIRouter() class IntegrationResponse(BaseModel): id: str name: str description: str logo_url: str = "" category: str = "Other" auth_type: str = "api_key" oauth_scopes: Optional[List[str]] = None tool_count: int = 0 trigger_count: int = 0 is_active: bool = True @router.get("", response_model=List[IntegrationResponse]) async def list_integrations( category: Optional[str] = None, search: Optional[str] = None, db: Session = Depends(get_db) ): composio = get_composio() integrations = [] if composio: try: raw = composio.toolkits.get() for t in raw: t_d = t if hasattr(t, "model_dump"): t_d = t.model_dump(mode="json") elif hasattr(t, "__dict__"): t_d = {k: v for k, v in t.__dict__.items() if not k.startswith("_")} meta = t_d.get("meta", {}) or {} cats = (meta.get("categories") or []) if isinstance(meta, dict) else [] cat = cats[0].get("name", "Other") if cats else "Other" if category and cat.lower() != category.lower(): continue name = t_d.get("name", t_d.get("slug", "")) if search and search.lower() not in name.lower(): continue auth_schemes = t_d.get("auth_schemes") or [] auth_type = "api_key" if any("oauth" in str(s).lower() for s in auth_schemes): auth_type = "oauth2" integrations.append(IntegrationResponse( id=t_d.get("slug", t_d.get("id", "")), name=name, description=t_d.get("description", ""), logo_url="", category=cat, auth_type=auth_type, tool_count=int(meta.get("tools_count", 0)) if isinstance(meta, dict) else 0, )) except Exception as e: print(f"[Apps] SDK list failed: {e}") if not integrations: query = db.query(Integration).filter(Integration.is_active == True) if category: query = query.filter(Integration.category == category) if search: query = query.filter( (Integration.name.ilike(f"%{search}%")) | (Integration.description.ilike(f"%{search}%")) ) for i in query.all(): integrations.append(IntegrationResponse( id=i.id, name=i.name, description=i.description, logo_url=i.logo_url, category=i.category, auth_type=i.auth_type, tool_count=i.tool_count, trigger_count=i.trigger_count, is_active=i.is_active )) return integrations @router.get("/categories") async def list_categories(db: Session = Depends(get_db)): categories = db.query(Integration.category).distinct().all() return {"categories": list(set(c[0] for c in categories if c[0]))} @router.get("/{integration_id}", response_model=IntegrationResponse) async def get_integration(integration_id: str, db: Session = Depends(get_db)): integration = db.query(Integration).filter(Integration.id == integration_id).first() if not integration: raise HTTPException(status_code=404, detail="Integration not found") return IntegrationResponse( id=integration.id, name=integration.name, description=integration.description, logo_url=integration.logo_url, category=integration.category, auth_type=integration.auth_type, tool_count=integration.tool_count, trigger_count=integration.trigger_count, is_active=integration.is_active ) @router.post("/{integration_id}/connect") async def connect_integration( integration_id: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): integration = db.query(Integration).filter(Integration.id == integration_id).first() if not integration: raise HTTPException(status_code=404, detail="Integration not found") return { "auth_url": f"/api/connections/oauth/begin?integration_id={integration_id}", "message": "Use /api/connections/oauth/begin for OAuth flow" } @router.post("/sync") async def sync_integrations( force: bool = Query(False), current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): from integrations.registry import sync_from_composio result = await sync_from_composio(force=force) return {"message": "Integrations synced", "source": result["source"], "toolkits": result["toolkits"], "tools": result["tools"]}