Primakov Alexandr Alexandrovich b297bcbba9 Add deployment documentation and enhance project setup scripts
- Introduced `DEPLOYMENT.md` for comprehensive deployment instructions.
- Added `start.bat` and `start.sh` scripts for unified project startup on Windows and Linux/Mac.
- Updated `.gitignore` to exclude additional files and directories.
- Enhanced `main.py` to serve static frontend files and handle SPA routing.
- Configured Vite to output the frontend build to the backend public directory.
- Minor adjustments in `ReviewDetail.tsx` for improved data handling.
2025-10-12 23:27:41 +03:00

147 lines
4.1 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()
yield
# Shutdown
pass
# 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
)