116 lines
3.3 KiB
Python
116 lines
3.3 KiB
Python
|
|
"""
|
||
|
|
Main FastAPI application for GSPro Remote backend.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import logging
|
||
|
|
from contextlib import asynccontextmanager
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from fastapi import FastAPI
|
||
|
|
from fastapi.middleware.cors import CORSMiddleware
|
||
|
|
from fastapi.staticfiles import StaticFiles
|
||
|
|
|
||
|
|
from .api import actions, config, system, vision
|
||
|
|
from .core.config import AppConfig, get_config
|
||
|
|
from .core.mdns import MDNSService
|
||
|
|
|
||
|
|
# Configure logging
|
||
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
@asynccontextmanager
|
||
|
|
async def lifespan(app: FastAPI):
|
||
|
|
"""
|
||
|
|
Application lifespan manager for startup/shutdown tasks.
|
||
|
|
"""
|
||
|
|
# Startup
|
||
|
|
logger.info("Starting GSPro Remote backend v0.1.0")
|
||
|
|
|
||
|
|
# Load configuration
|
||
|
|
config = get_config()
|
||
|
|
logger.info(f"Configuration loaded from {config.config_path}")
|
||
|
|
|
||
|
|
# Start mDNS service if enabled
|
||
|
|
mdns_service = None
|
||
|
|
if config.server.mdns_enabled:
|
||
|
|
try:
|
||
|
|
mdns_service = MDNSService(name="gsproapp", port=config.server.port, service_type="_http._tcp.local.")
|
||
|
|
mdns_service.start()
|
||
|
|
logger.info(f"mDNS service started: gsproapp.local:{config.server.port}")
|
||
|
|
except Exception as e:
|
||
|
|
logger.warning(f"Failed to start mDNS service: {e}")
|
||
|
|
|
||
|
|
yield
|
||
|
|
|
||
|
|
# Shutdown
|
||
|
|
logger.info("Shutting down GSPro Remote backend")
|
||
|
|
|
||
|
|
# Stop mDNS service
|
||
|
|
if mdns_service:
|
||
|
|
mdns_service.stop()
|
||
|
|
logger.info("mDNS service stopped")
|
||
|
|
|
||
|
|
# Save configuration
|
||
|
|
config.save()
|
||
|
|
logger.info("Configuration saved")
|
||
|
|
|
||
|
|
|
||
|
|
def create_app() -> FastAPI:
|
||
|
|
"""
|
||
|
|
Create and configure the FastAPI application.
|
||
|
|
"""
|
||
|
|
app = FastAPI(
|
||
|
|
title="GSPro Remote",
|
||
|
|
version="0.1.0",
|
||
|
|
description="Remote control API for GSPro golf simulator",
|
||
|
|
docs_url="/api/docs",
|
||
|
|
redoc_url="/api/redoc",
|
||
|
|
openapi_url="/api/openapi.json",
|
||
|
|
lifespan=lifespan,
|
||
|
|
)
|
||
|
|
|
||
|
|
# Configure CORS
|
||
|
|
app.add_middleware(
|
||
|
|
CORSMiddleware,
|
||
|
|
allow_origins=["*"], # In production, specify actual origins
|
||
|
|
allow_credentials=True,
|
||
|
|
allow_methods=["*"],
|
||
|
|
allow_headers=["*"],
|
||
|
|
)
|
||
|
|
|
||
|
|
# Mount API routers
|
||
|
|
app.include_router(actions.router, prefix="/api/actions", tags=["Actions"])
|
||
|
|
app.include_router(config.router, prefix="/api/config", tags=["Configuration"])
|
||
|
|
app.include_router(vision.router, prefix="/api/vision", tags=["Vision"])
|
||
|
|
app.include_router(system.router, prefix="/api/system", tags=["System"])
|
||
|
|
|
||
|
|
# Serve frontend UI if built
|
||
|
|
ui_path = Path(__file__).parent.parent / "ui"
|
||
|
|
if ui_path.exists():
|
||
|
|
app.mount("/ui", StaticFiles(directory=str(ui_path), html=True), name="ui")
|
||
|
|
logger.info(f"Serving UI from {ui_path}")
|
||
|
|
|
||
|
|
# Root redirect
|
||
|
|
@app.get("/")
|
||
|
|
async def root():
|
||
|
|
return {
|
||
|
|
"name": "GSPro Remote",
|
||
|
|
"version": "0.1.0",
|
||
|
|
"status": "running",
|
||
|
|
"ui": "/ui" if ui_path.exists() else None,
|
||
|
|
"docs": "/api/docs",
|
||
|
|
}
|
||
|
|
|
||
|
|
return app
|
||
|
|
|
||
|
|
|
||
|
|
# Create the application instance
|
||
|
|
app = create_app()
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
import uvicorn
|
||
|
|
|
||
|
|
config = get_config()
|
||
|
|
uvicorn.run("app.main:app", host=config.server.host, port=config.server.port, reload=True, log_level="info")
|