Files
hive/backend/app/api/tasks.py
anthonyrawlins cd28f94e8f 🚀 Release Hive Platform v1.1 - Complete Authentication & Architecture Overhaul
Major Features:
 JWT Bearer Token authentication system with secure token management
 API key generation and management with scoped permissions
 Complete user management (registration, login, logout, password change)
 Frontend authentication components and context integration

Backend Architecture Improvements:
 CORS configuration via environment variables (CORS_ORIGINS)
 Dependency injection pattern for unified coordinator
 Database schema fixes with UUID support and SQLAlchemy compliance
 Task persistence replaced in-memory storage with database-backed system
 Service separation following Single Responsibility Principle
 Fixed SQLAlchemy metadata column naming conflicts

Infrastructure & Testing:
 Comprehensive Jest unit testing and Playwright e2e testing infrastructure
 GitHub Actions CI/CD pipeline integration
 Enhanced API clients matching PROJECT_PLAN.md specifications
 Docker Swarm deployment with proper networking and service connectivity

Database & Security:
 UUID-based user models with proper validation
 Unified database schema with authentication tables
 Token blacklisting and refresh token management
 Secure password hashing with bcrypt
 API key scoping and permissions system

API Enhancements:
 Authentication endpoints (/api/auth/*)
 Task management with database persistence
 Enhanced monitoring and health check endpoints
 Comprehensive error handling and validation

Deployment:
 Successfully deployed to Docker Swarm at https://hive.home.deepblack.cloud
 All services operational with proper networking
 Environment-based configuration support

🛠️ Technical Debt Resolved:
- Fixed global coordinator instances with proper dependency injection
- Replaced hardcoded CORS origins with environment variables
- Unified User model schema conflicts across authentication system
- Implemented database persistence for critical task storage
- Created comprehensive testing infrastructure

This release transforms Hive from a development prototype into a production-ready
distributed AI orchestration platform with enterprise-grade authentication,
proper architectural patterns, and robust deployment infrastructure.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 22:00:42 +10:00

205 lines
7.5 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Dict, Any, Optional
from ..core.auth_deps import get_current_user_context
from ..core.unified_coordinator_refactored import UnifiedCoordinatorRefactored as UnifiedCoordinator
router = APIRouter()
# Dependency function for coordinator injection (will be overridden by main.py)
def get_coordinator() -> UnifiedCoordinator:
"""This will be overridden by main.py dependency injection"""
pass
@router.post("/tasks")
async def create_task(
task_data: Dict[str, Any],
coordinator: UnifiedCoordinator = Depends(get_coordinator),
current_user: Dict[str, Any] = Depends(get_current_user_context)
):
"""Create a new development task"""
try:
# Extract task details
task_type_str = task_data.get("type", "python")
priority = task_data.get("priority", 5)
context = task_data.get("context", {})
# Create task using coordinator
task_id = await coordinator.submit_task(task_data)
return {
"id": task_id,
"type": task_type_str,
"priority": priority,
"status": "pending",
"context": context,
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/tasks/{task_id}")
async def get_task(
task_id: str,
coordinator: UnifiedCoordinator = Depends(get_coordinator),
current_user: Dict[str, Any] = Depends(get_current_user_context)
):
"""Get details of a specific task"""
task = await coordinator.get_task_status(task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task
@router.get("/tasks")
async def get_tasks(
status: Optional[str] = Query(None, description="Filter by task status"),
agent: Optional[str] = Query(None, description="Filter by assigned agent"),
workflow_id: Optional[str] = Query(None, description="Filter by workflow ID"),
limit: int = Query(50, description="Maximum number of tasks to return"),
coordinator: UnifiedCoordinator = Depends(get_coordinator),
current_user: Dict[str, Any] = Depends(get_current_user_context)
):
"""Get list of tasks with optional filtering (includes database tasks)"""
try:
# Get tasks from database (more comprehensive than in-memory only)
db_tasks = coordinator.task_service.get_tasks(
status=status,
agent_id=agent,
workflow_id=workflow_id,
limit=limit
)
# Convert ORM tasks to coordinator tasks for consistent response format
tasks = []
for orm_task in db_tasks:
coordinator_task = coordinator.task_service.coordinator_task_from_orm(orm_task)
tasks.append({
"id": coordinator_task.id,
"type": coordinator_task.type.value,
"priority": coordinator_task.priority,
"status": coordinator_task.status.value,
"context": coordinator_task.context,
"assigned_agent": coordinator_task.assigned_agent,
"result": coordinator_task.result,
"created_at": coordinator_task.created_at,
"completed_at": coordinator_task.completed_at,
"workflow_id": coordinator_task.workflow_id,
})
# Get total count for the response
total_count = len(db_tasks)
return {
"tasks": tasks,
"total": total_count,
"source": "database",
"filters_applied": {
"status": status,
"agent": agent,
"workflow_id": workflow_id
}
}
except Exception as e:
# Fallback to in-memory tasks if database fails
all_tasks = list(coordinator.tasks.values())
# Apply filters
filtered_tasks = all_tasks
if status:
try:
status_enum = TaskStatus(status)
filtered_tasks = [t for t in filtered_tasks if t.status == status_enum]
except ValueError:
raise HTTPException(status_code=400, detail=f"Invalid status: {status}")
if agent:
filtered_tasks = [t for t in filtered_tasks if t.assigned_agent == agent]
if workflow_id:
filtered_tasks = [t for t in filtered_tasks if t.workflow_id == workflow_id]
# Sort by creation time (newest first) and limit
filtered_tasks.sort(key=lambda t: t.created_at or 0, reverse=True)
filtered_tasks = filtered_tasks[:limit]
# Format response
tasks = []
for task in filtered_tasks:
tasks.append({
"id": task.id,
"type": task.type.value,
"priority": task.priority,
"status": task.status.value,
"context": task.context,
"assigned_agent": task.assigned_agent,
"result": task.result,
"created_at": task.created_at,
"completed_at": task.completed_at,
"workflow_id": task.workflow_id,
})
return {
"tasks": tasks,
"total": len(tasks),
"source": "memory_fallback",
"database_error": str(e),
"filtered": len(all_tasks) != len(tasks),
}
@router.get("/tasks/statistics")
async def get_task_statistics(
coordinator: UnifiedCoordinator = Depends(get_coordinator),
current_user: Dict[str, Any] = Depends(get_current_user_context)
):
"""Get comprehensive task statistics"""
try:
db_stats = coordinator.task_service.get_task_statistics()
# Get in-memory statistics
memory_stats = {
"in_memory_active": len([t for t in coordinator.tasks.values() if t.status == TaskStatus.IN_PROGRESS]),
"in_memory_pending": len(coordinator.task_queue),
"in_memory_total": len(coordinator.tasks)
}
return {
"database_statistics": db_stats,
"memory_statistics": memory_stats,
"coordinator_status": "operational" if coordinator.is_initialized else "initializing"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get task statistics: {str(e)}")
@router.delete("/tasks/{task_id}")
async def delete_task(
task_id: str,
coordinator: UnifiedCoordinator = Depends(get_coordinator),
current_user: Dict[str, Any] = Depends(get_current_user_context)
):
"""Delete a specific task"""
try:
# Remove from database
success = coordinator.task_service.delete_task(task_id)
if not success:
raise HTTPException(status_code=404, detail="Task not found")
# Remove from in-memory cache if present
if hasattr(coordinator, 'tasks') and task_id in coordinator.tasks:
del coordinator.tasks[task_id]
# Remove from task queue if present
coordinator.task_queue = [t for t in coordinator.task_queue if t.id != task_id]
# Delete from database
success = coordinator.task_service.delete_task(task_id)
if success:
return {"message": f"Task {task_id} deleted successfully"}
else:
raise HTTPException(status_code=404, detail="Task not found")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete task: {str(e)}")