109 lines
4.3 KiB
Python
109 lines
4.3 KiB
Python
"""
|
|
🦊 Foxy Dev Team — Backend API v2.0
|
|
|
|
FastAPI application with WebSocket support for real-time dashboard.
|
|
"""
|
|
|
|
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse
|
|
import os
|
|
|
|
from app.config import settings
|
|
from app.database import init_db
|
|
from app.routers import projects, agents, logs, workflows, config
|
|
from app.routers.ws import manager
|
|
|
|
# ─── Logging ───────────────────────────────────────────────────────────────────
|
|
|
|
logging.basicConfig(
|
|
level=getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO),
|
|
format="[%(asctime)s] %(levelname)s %(name)s — %(message)s",
|
|
datefmt="%Y-%m-%dT%H:%M:%SZ",
|
|
)
|
|
log = logging.getLogger("foxy.main")
|
|
|
|
|
|
# ─── Lifespan ──────────────────────────────────────────────────────────────────
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
log.info("🦊 Foxy Dev Team API v2.0 — Starting...")
|
|
await init_db()
|
|
log.info("✅ Database initialized")
|
|
yield
|
|
log.info("🛑 Foxy Dev Team API — Shutting down")
|
|
|
|
|
|
# ─── App ───────────────────────────────────────────────────────────────────────
|
|
|
|
app = FastAPI(
|
|
title="🦊 Foxy Dev Team API",
|
|
description="Backend API for the Foxy Dev Team multi-agent orchestration system",
|
|
version="2.0.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# CORS
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.cors_origins_list,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# ─── Routers ───────────────────────────────────────────────────────────────────
|
|
|
|
app.include_router(projects.router)
|
|
app.include_router(agents.router)
|
|
app.include_router(logs.router)
|
|
app.include_router(workflows.router)
|
|
app.include_router(config.router)
|
|
|
|
# ─── Static Files ─────────────────────────────────────────────────────────────
|
|
|
|
if os.path.exists("static"):
|
|
app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")
|
|
|
|
@app.get("/{full_path:path}")
|
|
async def serve_spa(full_path: str):
|
|
# API requests should already be handled by routers
|
|
# If not an API request, serve index.html
|
|
if full_path.startswith("api/") or full_path.startswith("ws/"):
|
|
return None # Should not happen due to router order
|
|
|
|
index_path = os.path.join("static", "index.html")
|
|
if os.path.exists(index_path):
|
|
return FileResponse(index_path)
|
|
return {"detail": "Frontend not found"}
|
|
|
|
|
|
# ─── WebSocket ─────────────────────────────────────────────────────────────────
|
|
|
|
@app.websocket("/ws/live")
|
|
async def websocket_endpoint(websocket: WebSocket):
|
|
await manager.connect(websocket)
|
|
try:
|
|
while True:
|
|
# Keep connection alive; client-initiated messages can be handled here
|
|
data = await websocket.receive_text()
|
|
# Echo ping/pong or handle subscriptions
|
|
if data == "ping":
|
|
await websocket.send_text('{"type":"pong"}')
|
|
except WebSocketDisconnect:
|
|
await manager.disconnect(websocket)
|
|
except Exception:
|
|
await manager.disconnect(websocket)
|
|
|
|
|
|
# ─── Health ────────────────────────────────────────────────────────────────────
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
return {"status": "ok", "version": "2.0.0", "service": "foxy-dev-team"}
|