🚀 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>
This commit is contained in:
@@ -33,7 +33,7 @@ class UserCreate(BaseModel):
|
||||
|
||||
|
||||
class UserResponse(BaseModel):
|
||||
id: int
|
||||
id: str
|
||||
username: str
|
||||
email: str
|
||||
full_name: Optional[str]
|
||||
@@ -63,7 +63,7 @@ class APIKeyCreate(BaseModel):
|
||||
|
||||
|
||||
class APIKeyResponse(BaseModel):
|
||||
id: int
|
||||
id: str
|
||||
name: str
|
||||
key_prefix: str
|
||||
scopes: List[str]
|
||||
@@ -198,7 +198,7 @@ async def refresh_token(
|
||||
detail="Invalid token type"
|
||||
)
|
||||
|
||||
user_id = int(payload.get("sub"))
|
||||
user_id = payload.get("sub")
|
||||
jti = payload.get("jti")
|
||||
|
||||
# Check if refresh token exists and is valid
|
||||
|
||||
@@ -10,13 +10,16 @@ import asyncio
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from ..core.unified_coordinator import UnifiedCoordinator, AgentType as TaskType, TaskPriority
|
||||
from ..core.unified_coordinator_refactored import UnifiedCoordinatorRefactored as UnifiedCoordinator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/distributed", tags=["distributed-workflows"])
|
||||
|
||||
# Use unified coordinator from main application
|
||||
# Dependency function for coordinator injection (will be imported by main)
|
||||
def get_coordinator() -> UnifiedCoordinator:
|
||||
"""This will be overridden by main.py dependency injection"""
|
||||
pass
|
||||
|
||||
class WorkflowRequest(BaseModel):
|
||||
"""Request model for workflow submission"""
|
||||
|
||||
@@ -1,62 +1,53 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Dict, Any, Optional
|
||||
from ..core.auth import get_current_user
|
||||
from ..core.unified_coordinator import UnifiedCoordinator, AgentType, TaskStatus
|
||||
from ..core.auth_deps import get_current_user_context
|
||||
from ..core.unified_coordinator_refactored import UnifiedCoordinatorRefactored as UnifiedCoordinator
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# This will be injected by main.py
|
||||
coordinator: UnifiedCoordinator = None
|
||||
|
||||
def set_coordinator(coord: UnifiedCoordinator):
|
||||
global coordinator
|
||||
coordinator = coord
|
||||
# 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]):
|
||||
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:
|
||||
# Map string type to AgentType enum
|
||||
task_type_str = task_data.get("type")
|
||||
if task_type_str not in [t.value for t in AgentType]:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid task type: {task_type_str}")
|
||||
|
||||
task_type = AgentType(task_type_str)
|
||||
priority = task_data.get("priority", 3)
|
||||
# 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 = coordinator.create_task(task_type, context, priority)
|
||||
task_id = await coordinator.submit_task(task_data)
|
||||
|
||||
return {
|
||||
"id": task.id,
|
||||
"type": task.type.value,
|
||||
"priority": task.priority,
|
||||
"status": task.status.value,
|
||||
"context": task.context,
|
||||
"created_at": task.created_at,
|
||||
"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, current_user: dict = Depends(get_current_user)):
|
||||
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 = coordinator.get_task_status(task_id)
|
||||
task = await coordinator.get_task_status(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
return {
|
||||
"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,
|
||||
}
|
||||
return task
|
||||
|
||||
@router.get("/tasks")
|
||||
async def get_tasks(
|
||||
@@ -64,7 +55,8 @@ async def get_tasks(
|
||||
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"),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
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)"""
|
||||
|
||||
@@ -157,7 +149,10 @@ async def get_tasks(
|
||||
}
|
||||
|
||||
@router.get("/tasks/statistics")
|
||||
async def get_task_statistics(current_user: dict = Depends(get_current_user)):
|
||||
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()
|
||||
@@ -179,11 +174,20 @@ async def get_task_statistics(current_user: dict = Depends(get_current_user)):
|
||||
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, current_user: dict = Depends(get_current_user)):
|
||||
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 in-memory cache if present
|
||||
if task_id in coordinator.tasks:
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user