""" Repository management API endpoints """ from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks from sqlalchemy.orm import Session from typing import List, Dict, Any, Optional from datetime import datetime from ..core.database import get_db from ..models.project import Project from ..services.repository_service import repository_service from ..auth.auth import get_current_user router = APIRouter() @router.get("/repositories", response_model=List[Dict[str, Any]]) async def list_repositories( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """List all repositories with bzzz integration enabled""" try: projects = db.query(Project).filter( Project.bzzz_enabled == True ).all() repositories = [] for project in projects: repo_data = { "id": project.id, "name": project.name, "description": project.description, "provider": project.provider or "github", "provider_base_url": project.provider_base_url, "owner": project.git_owner, "repository": project.git_repository, "branch": project.git_branch, "status": project.status, "bzzz_enabled": project.bzzz_enabled, "ready_to_claim": project.ready_to_claim, "auto_assignment": getattr(project, "auto_assignment", True), "created_at": project.created_at.isoformat() if project.created_at else None } repositories.append(repo_data) return repositories except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to list repositories: {str(e)}") @router.post("/repositories/sync") async def sync_repositories( background_tasks: BackgroundTasks, repository_ids: Optional[List[int]] = None, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Sync tasks from repositories""" try: if repository_ids: # Sync specific repositories projects = db.query(Project).filter( Project.id.in_(repository_ids), Project.bzzz_enabled == True ).all() if not projects: raise HTTPException(status_code=404, detail="No matching repositories found") results = {"synced_projects": 0, "new_tasks": 0, "assigned_tasks": 0, "errors": []} for project in projects: try: sync_result = await repository_service.sync_project_tasks(db, project) results["synced_projects"] += 1 results["new_tasks"] += sync_result.get("new_tasks", 0) results["assigned_tasks"] += sync_result.get("assigned_tasks", 0) except Exception as e: results["errors"].append(f"Project {project.name}: {str(e)}") return results else: # Sync all repositories in background background_tasks.add_task(repository_service.sync_all_repositories, db) return {"message": "Repository sync started in background", "status": "initiated"} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to sync repositories: {str(e)}") @router.get("/repositories/{repository_id}/stats") async def get_repository_stats( repository_id: int, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Get task statistics for a specific repository""" try: stats = await repository_service.get_project_task_stats(db, repository_id) return stats except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get repository stats: {str(e)}") @router.post("/repositories/{repository_id}/sync") async def sync_repository( repository_id: int, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Sync tasks from a specific repository""" try: project = db.query(Project).filter( Project.id == repository_id, Project.bzzz_enabled == True ).first() if not project: raise HTTPException(status_code=404, detail="Repository not found or bzzz integration not enabled") result = await repository_service.sync_project_tasks(db, project) return result except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to sync repository: {str(e)}") @router.put("/repositories/{repository_id}/config") async def update_repository_config( repository_id: int, config_data: Dict[str, Any], db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Update repository configuration""" try: project = db.query(Project).filter(Project.id == repository_id).first() if not project: raise HTTPException(status_code=404, detail="Repository not found") # Update allowed configuration fields if "auto_assignment" in config_data: setattr(project, "auto_assignment", config_data["auto_assignment"]) if "bzzz_enabled" in config_data: project.bzzz_enabled = config_data["bzzz_enabled"] if "ready_to_claim" in config_data: project.ready_to_claim = config_data["ready_to_claim"] if "status" in config_data and config_data["status"] in ["active", "inactive", "arcwhooshd"]: project.status = config_data["status"] db.commit() return {"message": "Repository configuration updated", "repository_id": repository_id} except HTTPException: raise except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to update repository config: {str(e)}") @router.get("/repositories/{repository_id}/tasks") async def get_repository_tasks( repository_id: int, limit: int = 50, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Get available tasks from a repository""" try: project = db.query(Project).filter( Project.id == repository_id, Project.bzzz_enabled == True ).first() if not project: raise HTTPException(status_code=404, detail="Repository not found or bzzz integration not enabled") # Get repository client and fetch tasks repo_client = await repository_service._get_repository_client(project) if not repo_client: raise HTTPException(status_code=500, detail="Failed to create repository client") tasks = await repo_client.list_available_tasks() # Limit results if len(tasks) > limit: tasks = tasks[:limit] return { "repository_id": repository_id, "repository_name": project.name, "provider": project.provider or "github", "tasks": tasks, "total_tasks": len(tasks) } except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get repository tasks: {str(e)}") @router.post("/repositories/discover") async def discover_repositories( provider: str = "gitea", base_url: str = "http://192.168.1.113:3000", db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """Discover repositories from a provider (placeholder for future implementation)""" try: # This would implement repository discovery functionality # For now, return the manually configured repositories existing_repos = db.query(Project).filter( Project.provider == provider, Project.provider_base_url == base_url ).all() discovered = [] for repo in existing_repos: discovered.append({ "name": repo.name, "owner": repo.git_owner, "repository": repo.git_repository, "description": repo.description, "already_configured": True }) return { "provider": provider, "base_url": base_url, "discovered_repositories": discovered } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to discover repositories: {str(e)}") @router.post("/webhook/{repository_id}") async def handle_repository_webhook( repository_id: int, payload: Dict[str, Any], db: Session = Depends(get_db) ): """Handle webhook events from repositories""" try: project = db.query(Project).filter(Project.id == repository_id).first() if not project: raise HTTPException(status_code=404, detail="Repository not found") # Log the webhook event (would be stored in webhook_events table) event_type = payload.get("action", "unknown") # For now, just trigger a sync if it's an issue event if "issue" in payload and event_type in ["opened", "labeled", "unlabeled"]: # Check if it's a bzzz-task issue = payload.get("issue", {}) labels = [label["name"] for label in issue.get("labels", [])] if "bzzz-task" in labels: # Trigger task sync for this project await repository_service.sync_project_tasks(db, project) return { "message": "Webhook processed, task sync triggered", "event_type": event_type, "issue_number": issue.get("number") } return {"message": "Webhook received", "event_type": event_type} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to process webhook: {str(e)}") @router.delete("/repositories/cache") async def clear_task_cache( current_user: dict = Depends(get_current_user) ): """Clear the task cache""" try: await repository_service.cleanup_old_cache(max_age_hours=0) # Clear all return {"message": "Task cache cleared"} except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to clear cache: {str(e)}")