Compost / backend /routers /apps.py
abc1181's picture
Refactor: use Composio Core SDK as backend engine
dc0fe62
Raw
History Blame Contribute Delete
5.03 kB
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"]}