GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts: - Fix stack trace exposure in error responses - Add SSRF protection with DNS resolution checking - Implement proper URL hostname validation (replaces substring matching) - Add centralized path sanitization to prevent path traversal - Fix ReDoS vulnerability in email validation regex - Improve HTML sanitization in validation utilities - Fix capability wildcard matching in auth utilities - Update glob dependency to address CVE - Add CodeQL suppression comments for verified false positives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
240
apps/control-panel-backend/app/api/v1/analytics.py
Normal file
240
apps/control-panel-backend/app/api/v1/analytics.py
Normal file
@@ -0,0 +1,240 @@
|
||||
"""
|
||||
Analytics and Dremio SQL Federation Endpoints
|
||||
"""
|
||||
from typing import List, Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.services.dremio_service import DremioService
|
||||
from app.core.auth import get_current_user
|
||||
from app.models.user import User
|
||||
|
||||
router = APIRouter(prefix="/api/v1/analytics", tags=["Analytics"])
|
||||
|
||||
|
||||
class TenantDashboardResponse(BaseModel):
|
||||
"""Response model for tenant dashboard data"""
|
||||
tenant: Dict[str, Any]
|
||||
metrics: Dict[str, Any]
|
||||
analytics: Dict[str, Any]
|
||||
alerts: List[Dict[str, Any]]
|
||||
|
||||
|
||||
class CustomQueryRequest(BaseModel):
|
||||
"""Request model for custom analytics queries"""
|
||||
query_type: str
|
||||
start_date: Optional[datetime] = None
|
||||
end_date: Optional[datetime] = None
|
||||
|
||||
|
||||
class DatasetCreationResponse(BaseModel):
|
||||
"""Response model for dataset creation"""
|
||||
tenant_id: int
|
||||
datasets_created: List[str]
|
||||
status: str
|
||||
|
||||
|
||||
@router.get("/dashboard/{tenant_id}", response_model=TenantDashboardResponse)
|
||||
async def get_tenant_dashboard(
|
||||
tenant_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get comprehensive dashboard data for a tenant using Dremio SQL federation"""
|
||||
|
||||
# Check permissions
|
||||
if current_user.user_type != 'super_admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Insufficient permissions to view dashboard"
|
||||
)
|
||||
|
||||
|
||||
service = DremioService(db)
|
||||
|
||||
try:
|
||||
dashboard_data = await service.get_tenant_dashboard_data(tenant_id)
|
||||
return TenantDashboardResponse(**dashboard_data)
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to fetch dashboard data: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/query/{tenant_id}")
|
||||
async def execute_custom_analytics(
|
||||
tenant_id: int,
|
||||
request: CustomQueryRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Execute custom analytics queries for a tenant"""
|
||||
|
||||
# Check permissions (only admins)
|
||||
if current_user.user_type != 'super_admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Insufficient permissions for analytics queries"
|
||||
)
|
||||
|
||||
|
||||
service = DremioService(db)
|
||||
|
||||
try:
|
||||
results = await service.get_custom_analytics(
|
||||
tenant_id=tenant_id,
|
||||
query_type=request.query_type,
|
||||
start_date=request.start_date,
|
||||
end_date=request.end_date
|
||||
)
|
||||
return {
|
||||
"query_type": request.query_type,
|
||||
"results": results,
|
||||
"count": len(results)
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Query execution failed: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/datasets/create/{tenant_id}", response_model=DatasetCreationResponse)
|
||||
async def create_virtual_datasets(
|
||||
tenant_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Create Dremio virtual datasets for tenant analytics"""
|
||||
|
||||
# Check permissions (only GT admin)
|
||||
if current_user.user_type != 'super_admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Only GT admins can create virtual datasets"
|
||||
)
|
||||
|
||||
service = DremioService(db)
|
||||
|
||||
try:
|
||||
result = await service.create_virtual_datasets(tenant_id)
|
||||
return DatasetCreationResponse(**result)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to create datasets: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/metrics/performance/{tenant_id}")
|
||||
async def get_performance_metrics(
|
||||
tenant_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get real-time performance metrics for a tenant"""
|
||||
|
||||
# Check permissions
|
||||
if current_user.user_type != 'super_admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Insufficient permissions to view metrics"
|
||||
)
|
||||
|
||||
if current_user.user_type == 'tenant_admin' and current_user.tenant_id != tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Cannot view metrics for other tenants"
|
||||
)
|
||||
|
||||
service = DremioService(db)
|
||||
|
||||
try:
|
||||
metrics = await service._get_performance_metrics(tenant_id)
|
||||
return metrics
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to fetch metrics: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/alerts/{tenant_id}")
|
||||
async def get_security_alerts(
|
||||
tenant_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get security and operational alerts for a tenant"""
|
||||
|
||||
# Check permissions
|
||||
if current_user.user_type != 'super_admin':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Insufficient permissions to view alerts"
|
||||
)
|
||||
|
||||
if current_user.user_type == 'tenant_admin' and current_user.tenant_id != tenant_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Cannot view alerts for other tenants"
|
||||
)
|
||||
|
||||
service = DremioService(db)
|
||||
|
||||
try:
|
||||
alerts = await service._get_security_alerts(tenant_id)
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"alerts": alerts,
|
||||
"total": len(alerts),
|
||||
"critical": len([a for a in alerts if a.get('severity') == 'critical']),
|
||||
"warning": len([a for a in alerts if a.get('severity') == 'warning']),
|
||||
"info": len([a for a in alerts if a.get('severity') == 'info'])
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to fetch alerts: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/query-types")
|
||||
async def get_available_query_types(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get list of available analytics query types"""
|
||||
|
||||
return {
|
||||
"query_types": [
|
||||
{
|
||||
"id": "user_activity",
|
||||
"name": "User Activity Analysis",
|
||||
"description": "Analyze user activity, token usage, and costs"
|
||||
},
|
||||
{
|
||||
"id": "resource_trends",
|
||||
"name": "Resource Usage Trends",
|
||||
"description": "View resource usage trends over time"
|
||||
},
|
||||
{
|
||||
"id": "cost_optimization",
|
||||
"name": "Cost Optimization Report",
|
||||
"description": "Identify cost optimization opportunities"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user