"""Plugin routes — list, install, activate, deactivate, uninstall, manifest schema.""" from __future__ import annotations from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.deps import require_admin from app.plugins.migration_runner import MigrationValidationError from app.services.plugin_service import get_plugin_service router = APIRouter(prefix="/api/v1/plugins", tags=["plugins"]) @router.get("") async def list_plugins( db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_admin), ): """List all plugins with their current status (discovered, installed, active, inactive).""" service = get_plugin_service() plugins = await service.list_plugins(db) return {"plugins": plugins, "total": len(plugins)} @router.get("/manifest") async def get_manifest_schema( current_user: dict = Depends(require_admin), ): """Get the plugin manifest schema documentation.""" service = get_plugin_service() return service.get_manifest_schema() @router.post("/{name}/install") async def install_plugin( name: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_admin), ): """Install a plugin by name. Runs migrations and creates DB record. Idempotent: returns 200 if already installed. """ import uuid as uuid_mod service = get_plugin_service() try: result = await service.install_plugin( db, name, tenant_id=uuid_mod.UUID(current_user["tenant_id"]), user_id=uuid_mod.UUID(current_user["user_id"]), ) return result except ValueError as exc: if "not found" in str(exc).lower(): raise HTTPException( 404, detail={"detail": str(exc), "code": "plugin_not_found"} ) from None raise HTTPException(400, detail={"detail": str(exc), "code": "plugin_error"}) from None except MigrationValidationError as exc: raise HTTPException( 422, detail={"detail": str(exc), "code": "migration_validation_error"} ) from None @router.post("/{name}/activate") async def activate_plugin( name: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_admin), ): """Activate a plugin by name. Registers event listeners and routes. Idempotent: returns 200 if already active. """ import uuid as uuid_mod service = get_plugin_service() try: result = await service.activate_plugin( db, name, tenant_id=uuid_mod.UUID(current_user["tenant_id"]), user_id=uuid_mod.UUID(current_user["user_id"]), ) return result except ValueError as exc: if "not installed" in str(exc).lower(): raise HTTPException( 400, detail={"detail": str(exc), "code": "plugin_not_installed"} ) from None if "not found" in str(exc).lower(): raise HTTPException( 404, detail={"detail": str(exc), "code": "plugin_not_found"} ) from None raise HTTPException(400, detail={"detail": str(exc), "code": "plugin_error"}) from None @router.post("/{name}/deactivate") async def deactivate_plugin( name: str, db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_admin), ): """Deactivate a plugin by name. Unregisters event listeners and routes. Idempotent: returns 200 if already inactive. """ import uuid as uuid_mod service = get_plugin_service() try: result = await service.deactivate_plugin( db, name, tenant_id=uuid_mod.UUID(current_user["tenant_id"]), user_id=uuid_mod.UUID(current_user["user_id"]), ) return result except ValueError as exc: if "not found" in str(exc).lower(): raise HTTPException( 404, detail={"detail": str(exc), "code": "plugin_not_found"} ) from None raise HTTPException(400, detail={"detail": str(exc), "code": "plugin_error"}) from None @router.delete("/{name}") async def uninstall_plugin( name: str, remove_data: bool = Query(False), db: AsyncSession = Depends(get_db), current_user: dict = Depends(require_admin), ): """Uninstall a plugin. Optionally drop plugin-created tables with remove_data=true. Returns 200 with uninstalled status. Idempotent for already-uninstalled plugins. """ import uuid as uuid_mod service = get_plugin_service() try: result = await service.uninstall_plugin( db, name, remove_data=remove_data, tenant_id=uuid_mod.UUID(current_user["tenant_id"]), user_id=uuid_mod.UUID(current_user["user_id"]), ) return result except ValueError as exc: if "not found" in str(exc).lower(): raise HTTPException( 404, detail={"detail": str(exc), "code": "plugin_not_found"} ) from None raise HTTPException(400, detail={"detail": str(exc), "code": "plugin_error"}) from None