from contextlib import asynccontextmanager from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from sqlalchemy import select from app.core.config import settings from app.core.database import init_db, AsyncSessionLocal from app.core.security import get_password_hash from app.models.models import User from app.api.auth import router as auth_router from app.api.clients import router as clients_router from app.api.jobs import router as jobs_router from app.api.upload import router as upload_router from app.api.backups import router as backups_router from app.api.events import router as events_router from app.api.stats import router as stats_router from app.ws.manager import ws_manager @asynccontextmanager async def lifespan(app: FastAPI): # Initialize database tables await init_db() # Seed default administrator if not present async with AsyncSessionLocal() as session: result = await session.execute(select(User)) admin = result.scalar_one_or_none() if not admin: admin_user = User( email="admin@oneverdrive.local", hashed_password=get_password_hash("Admin1234!"), full_name="System Administrator", role="ADMIN", is_active=True ) session.add(admin_user) await session.commit() print(">> [OnEver Drive] Default admin created: admin@oneverdrive.local / Admin1234!") yield app = FastAPI( title=settings.PROJECT_NAME, version=settings.VERSION, description="Centralized Backup & Sync Platform for Windows on Proxmox VE", lifespan=lifespan ) # CORS Middleware to allow Web UI connections app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register API Routers app.include_router(auth_router, prefix=settings.API_V1_PREFIX) app.include_router(clients_router, prefix=settings.API_V1_PREFIX) app.include_router(jobs_router, prefix=settings.API_V1_PREFIX) app.include_router(upload_router, prefix=settings.API_V1_PREFIX) app.include_router(backups_router, prefix=settings.API_V1_PREFIX) app.include_router(events_router, prefix=settings.API_V1_PREFIX) app.include_router(stats_router, prefix=settings.API_V1_PREFIX) @app.websocket("/ws/telemetry") async def websocket_telemetry(websocket: WebSocket): """WebSocket endpoint for real-time dashboard telemetry and live upload meters.""" await ws_manager.connect(websocket) try: while True: # Keep connection open and accept incoming ping/pong or messages data = await websocket.receive_text() if data == "ping": await websocket.send_text("pong") except WebSocketDisconnect: ws_manager.disconnect(websocket) except Exception: ws_manager.disconnect(websocket) @app.get("/health") async def health(): return { "status": "healthy", "service": settings.PROJECT_NAME, "version": settings.VERSION }