File size: 4,005 Bytes
7ac6163
 
 
 
 
 
56fa34f
7ac6163
56fa34f
7ac6163
 
 
 
 
 
 
 
3203a8c
a76c4ac
c1fe190
e49c006
 
7ac6163
 
 
 
 
 
 
 
cbff5e0
7ac6163
dd0b28c
 
 
 
7ac6163
 
dd0b28c
 
 
 
7ac6163
 
 
 
56fa34f
 
 
 
 
 
 
 
 
 
 
 
 
 
7ac6163
 
2e3db11
 
 
 
 
 
 
 
 
 
 
 
7ac6163
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cbff5e0
7ac6163
 
cbff5e0
 
e993aad
8de7693
26c0314
7ac6163
 
 
 
 
3203a8c
54df631
c1fe190
e49c006
 
7ac6163
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
CrownCode backend entrypoint with enhanced error handling.
"""

from __future__ import annotations

import asyncio
import os
from contextlib import asynccontextmanager

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from .routes.health import router as health_router
from .routes.youtube import router as youtube_router
from .routes.data_processing import router as data_processing_router
from .routes.analyze import router as analyze_router
from .routes.commend import router as commend_router
from .routes.reservations import router as reservations_router
from .routes.dreams import router as dreams_router
from .routes.remix import router as remix_router
from .services.logging_config import setup_logging, get_logger


setup_logging(level=os.getenv("LOG_LEVEL", "INFO"))
logger = get_logger(__name__)


def _load_origins() -> list[str]:
    raw = os.getenv("CROWNCODE_CORS_ORIGINS") or os.getenv("CORS_ORIGIN") or "http://localhost:3000"
    if raw.strip() == "*":
        logger.warning(
            "CORS wildcard '*' is not recommended for production. "
            "Set CROWNCODE_CORS_ORIGINS to an explicit origin list."
        )
        return ["*"]
    origins = [origin.strip() for origin in raw.split(",") if origin.strip()]
    if not origins:
        # No origins configured — default to localhost for development
        origins = ["http://localhost:3000"]
        logger.warning("No CORS origins configured, defaulting to localhost")
    logger.info(f"CORS configured for origins: {origins}")
    return origins


@asynccontextmanager
async def lifespan(_app: FastAPI):
    # On the Space (AURIS_WARMUP=1 in the Dockerfile) load the models and
    # compile librosa's numba code before the first visitor; off in tests.
    warm_task = None
    if os.getenv("AURIS_WARMUP") == "1":
        from .routes.analyze import warm_up
        warm_task = asyncio.create_task(warm_up())
    yield
    if warm_task is not None and not warm_task.done():
        warm_task.cancel()


app = FastAPI(title="CrownCode Backend API", version="0.1.0", lifespan=lifespan)


@app.get("/")
async def root():
    """Root endpoint for health check and API info."""
    return {
        "name": "CrownCode Backend API",
        "version": "0.1.0",
        "status": "running",
        "docs": "/docs",
        "health": "/api/health"
    }


@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError) -> JSONResponse:
    logger.warning(f"Validation error: {exc}")
    return JSONResponse(
        status_code=400,
        content={"detail": str(exc), "type": "validation_error"}
    )


@app.exception_handler(FileNotFoundError)
async def file_not_found_handler(request: Request, exc: FileNotFoundError) -> JSONResponse:
    logger.error(f"File not found: {exc}")
    return JSONResponse(
        status_code=404,
        content={"detail": "Resource not found", "type": "not_found"}
    )


@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    logger.error(f"Unhandled exception: {type(exc).__name__}: {exc}", exc_info=True)
    return JSONResponse(
        status_code=500,
        content={"detail": "Internal server error", "type": "server_error"}
    )


_origins = _load_origins()
app.add_middleware(
    CORSMiddleware,
    allow_origins=_origins,
    allow_credentials="*" not in _origins,
    allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
    allow_headers=["Content-Type", "Accept"],
    expose_headers=["X-Remix-Analysis", "X-Multitrack-Analysis"],
)

app.include_router(health_router)
app.include_router(youtube_router)
app.include_router(data_processing_router)
app.include_router(analyze_router)
app.include_router(commend_router)
app.include_router(reservations_router)
app.include_router(dreams_router)
app.include_router(remix_router)

logger.info("CrownCode backend API initialized")