Primakov Alexandr Alexandrovich 6ae2d0d8ec Add organization and task queue features
- Introduced new models for `Organization` and `ReviewTask` to manage organizations and review tasks.
- Implemented API endpoints for CRUD operations on organizations and tasks, including scanning organizations for repositories and PRs.
- Developed a background worker for sequential processing of review tasks with priority handling and automatic retries.
- Created frontend components for managing organizations and monitoring task queues, including real-time updates and filtering options.
- Added comprehensive documentation for organization features and quick start guides.
- Fixed UI issues and improved navigation for better user experience.
2025-10-13 00:10:04 +03:00

154 lines
4.3 KiB
Python

"""Main FastAPI application"""
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from contextlib import asynccontextmanager
from typing import List
from pathlib import Path
import json
from app.config import settings
from app.database import init_db
from app.api import api_router
class ConnectionManager:
"""WebSocket connection manager"""
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def broadcast(self, message: dict):
"""Broadcast message to all connected clients"""
for connection in self.active_connections:
try:
await connection.send_json(message)
except Exception:
pass
# Create connection manager
manager = ConnectionManager()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifespan events"""
# Startup
await init_db()
# Start task worker
from app.workers.task_worker import start_worker
await start_worker()
yield
# Shutdown
from app.workers.task_worker import stop_worker
await stop_worker()
# Create FastAPI app
app = FastAPI(
title="AI Code Review Agent",
description="AI агент для автоматического ревью Pull Request",
version="0.1.0",
lifespan=lifespan
)
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API routes
app.include_router(api_router, prefix="/api")
# Serve static files (frontend build)
public_dir = Path(__file__).parent.parent / "public"
if public_dir.exists():
# Serve assets (JS, CSS, images)
app.mount("/assets", StaticFiles(directory=str(public_dir / "assets")), name="assets")
@app.get("/", response_class=FileResponse)
async def serve_frontend_root():
"""Serve frontend index.html"""
return FileResponse(str(public_dir / "index.html"))
@app.get("/{full_path:path}", response_class=FileResponse)
async def serve_frontend_routes(full_path: str):
"""Serve frontend for all routes (SPA support)"""
# Skip API and WebSocket routes
if full_path.startswith(("api/", "ws/", "docs", "redoc", "openapi.json")):
return None
file_path = public_dir / full_path
if file_path.exists() and file_path.is_file():
return FileResponse(str(file_path))
# Fallback to index.html for SPA routing
return FileResponse(str(public_dir / "index.html"))
else:
@app.get("/")
async def root():
"""Root endpoint (when frontend not built)"""
return {
"message": "AI Code Review Agent API",
"version": "0.1.0",
"docs": "/docs",
"note": "Frontend not built. Run 'npm run build' in frontend directory."
}
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy"}
@app.websocket("/ws/reviews")
async def websocket_endpoint(websocket: WebSocket):
"""WebSocket endpoint for real-time review updates"""
await manager.connect(websocket)
try:
while True:
# Keep connection alive
data = await websocket.receive_text()
# Echo back or handle client messages if needed
await websocket.send_json({"type": "pong", "message": "connected"})
except WebSocketDisconnect:
manager.disconnect(websocket)
async def broadcast_review_update(review_id: int, event_type: str, data: dict = None):
"""Broadcast review update to all connected clients"""
message = {
"type": event_type,
"review_id": review_id,
"data": data or {}
}
await manager.broadcast(message)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host=settings.host,
port=settings.port,
reload=settings.debug
)