-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
129 lines (98 loc) · 3.81 KB
/
main.py
File metadata and controls
129 lines (98 loc) · 3.81 KB
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
123
124
125
126
127
128
129
"""
NeuronLab Backend - FastAPI Application
Based on Deep-ML (https://deep-ml.com)
"""
# Load .env FIRST before any other imports
from dotenv import load_dotenv
load_dotenv()
# Initialize logging
from app.logging_config import setup_logger
setup_logger()
import os
import traceback
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from app.database import create_tables
from app.routes import api_router
from app.logging_config import get_logger
from app.rate_limit import limiter
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Initialize database and container pool on startup."""
create_tables()
# Start container pool (non-fatal — app works without Docker, code execution degrades gracefully)
from app.services.executor import container_pool
try:
await container_pool.start()
except Exception as exc:
logger.warning(
"Docker is unavailable — code execution will be disabled. "
"Start Docker Desktop and restart the server to enable it. Error: %s",
exc,
)
yield
# Shutdown container pool (safe even if it never started)
try:
await container_pool.shutdown()
except Exception as exc:
logger.warning("Error shutting down container pool: %s", exc)
app = FastAPI(
title="NeuronLab API",
description="Backend API for NeuronLab ML practice platform (based on Deep-ML)",
version="1.0.0",
lifespan=lifespan,
)
# Rate limiting
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
# CORS middleware - environment-driven configuration
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:3000,http://localhost:5173").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=CORS_ORIGINS,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Authorization", "Content-Type"],
)
# Global Exception Handlers
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
"""Handle HTTP exceptions with structured JSON response."""
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail, "error": exc.detail, "status_code": exc.status_code},
)
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
"""Handle unhandled exceptions with structured JSON response."""
logger.error(f"Unhandled exception on {request.method} {request.url.path}: {exc}")
logger.debug(traceback.format_exc())
return JSONResponse(
status_code=500,
content={"error": "Internal server error", "status_code": 500},
)
# Include all API routes
app.include_router(api_router, prefix="/api")
@app.get("/")
async def root():
"""Health check endpoint."""
return {"status": "ok", "name": "NeuronLab API", "version": "1.0.0"}
if __name__ == "__main__":
import uvicorn
import os
# For development: single worker with reload
# For production: multiple workers (set WORKERS env var)
workers = int(os.getenv("WORKERS", "1"))
reload_mode = os.getenv("RELOAD", "true").lower() == "true"
if reload_mode:
# Development mode: single worker with hot reload
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
else:
# Production mode: multiple workers for concurrent requests
uvicorn.run("main:app", host="0.0.0.0", port=8000, workers=workers)