This commit is contained in:
2025-12-18 07:27:37 -05:00
parent 6240888ac5
commit 041de95698
8 changed files with 1151 additions and 745 deletions

69
base.py
View File

@@ -4,6 +4,7 @@ import sys
sys.path.insert(0, ".")
import logging
import asyncio
from contextlib import asynccontextmanager
from typing import Any
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
@@ -13,20 +14,61 @@ from lyric_search.sources import redis_cache
logging.basicConfig(level=logging.INFO)
logging.getLogger("aiosqlite").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("python_multipart.multipart").setLevel(logging.WARNING)
logging.getLogger("streamrip").setLevel(logging.WARNING)
logging.getLogger("utils.sr_wrapper").setLevel(logging.WARNING)
logger = logging.getLogger()
loop = asyncio.get_event_loop()
# Pre-import endpoint modules so we can wire up lifespan
constants = importlib.import_module("constants").Constants()
# Will be set after app creation
_routes: dict = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifespan context manager for startup/shutdown events."""
# Startup
uvicorn_access_logger = logging.getLogger("uvicorn.access")
uvicorn_access_logger.disabled = True
# Start Radio playlists
if "radio" in _routes and hasattr(_routes["radio"], "on_start"):
await _routes["radio"].on_start()
# Start endpoint background tasks
if "trip" in _routes and hasattr(_routes["trip"], "startup"):
await _routes["trip"].startup()
if "lighting" in _routes and hasattr(_routes["lighting"], "startup"):
await _routes["lighting"].startup()
logger.info("Application startup complete")
yield
# Shutdown
if "lighting" in _routes and hasattr(_routes["lighting"], "shutdown"):
await _routes["lighting"].shutdown()
if "trip" in _routes and hasattr(_routes["trip"], "shutdown"):
await _routes["trip"].shutdown()
logger.info("Application shutdown complete")
app = FastAPI(
title="codey.lol API",
version="1.0",
contact={"name": "codey"},
redirect_slashes=False,
loop=loop,
docs_url="/docs", # Swagger UI (default)
redoc_url="/redoc", # ReDoc UI (default, but explicitly set)
docs_url=None, # Disabled - using Scalar at /docs instead
redoc_url="/redoc",
lifespan=lifespan,
)
constants = importlib.import_module("constants").Constants()
util = importlib.import_module("util").Utilities(app, constants)
origins = [
@@ -48,8 +90,8 @@ app.add_middleware(
) # type: ignore
# Add Scalar API documentation endpoint (before blacklist routes)
@app.get("/scalar", include_in_schema=False)
# Scalar API documentation at /docs (replaces default Swagger UI)
@app.get("/docs", include_in_schema=False)
def scalar_docs():
return get_scalar_api_reference(openapi_url="/openapi.json", title="codey.lol API")
@@ -72,7 +114,7 @@ def base_head():
@app.get("/{path}", include_in_schema=False)
def disallow_get_any(request: Request, var: Any = None):
path = request.path_params["path"]
allowed_paths = ["widget", "misc/no", "docs", "redoc", "scalar", "openapi.json"]
allowed_paths = ["widget", "misc/no", "docs", "redoc", "openapi.json"]
logging.info(
f"Checking path: {path}, allowed: {path in allowed_paths or path.split('/', maxsplit=1)[0] in allowed_paths}"
)
@@ -99,7 +141,7 @@ End Blacklisted Routes
Actionable Routes
"""
routes: dict = {
_routes.update({
"randmsg": importlib.import_module("endpoints.rand_msg").RandMsg(
app, util, constants
),
@@ -116,12 +158,12 @@ routes: dict = {
"lighting": importlib.import_module("endpoints.lighting").Lighting(
app, util, constants
),
}
})
# Misc endpoint depends on radio endpoint instance
radio_endpoint = routes.get("radio")
radio_endpoint = _routes.get("radio")
if radio_endpoint:
routes["misc"] = importlib.import_module("endpoints.misc").Misc(
_routes["misc"] = importlib.import_module("endpoints.misc").Misc(
app, util, constants, radio_endpoint
)
@@ -133,12 +175,5 @@ End Actionable Routes
Startup
"""
async def on_start():
uvicorn_access_logger = logging.getLogger("uvicorn.access")
uvicorn_access_logger.disabled = True
app.add_event_handler("startup", on_start)
redis = redis_cache.RedisCache()
loop.create_task(redis.create_index())