123 lines
3.7 KiB
Python
123 lines
3.7 KiB
Python
"""MedGraph API — Knowledge base medica para estudio."""
|
|
|
|
import os
|
|
import time
|
|
from fastapi import FastAPI, Request, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
from slowapi.util import get_remote_address
|
|
from slowapi.errors import RateLimitExceeded
|
|
from dotenv import load_dotenv
|
|
from routers import topics, search, pathology, procedure, activity, admin, comprehensive, ontology_router, unified
|
|
from services import graph
|
|
|
|
load_dotenv()
|
|
|
|
API_KEY = os.getenv("API_KEY", "")
|
|
|
|
# Rate limiter
|
|
limiter = Limiter(key_func=get_remote_address, default_limits=["100/minute"])
|
|
|
|
app = FastAPI(
|
|
title="MedGraph API",
|
|
description="Knowledge base medica con grafo de conocimiento y busqueda semantica",
|
|
version="2.0.0",
|
|
servers=[{"url": os.getenv("API_BASE_URL", "http://localhost:8000")}],
|
|
docs_url="/docs" if os.getenv("ENVIRONMENT") == "development" else None,
|
|
redoc_url=None,
|
|
)
|
|
|
|
app.state.limiter = limiter
|
|
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
|
|
# CORS restrictivo
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=[
|
|
os.getenv("CORS_ORIGIN_1", "http://localhost:3000"),
|
|
# Add your frontend origins here
|
|
|
|
os.getenv("API_BASE_URL", "http://localhost:8000"),
|
|
# Add your frontend origins here
|
|
|
|
],
|
|
allow_methods=["GET", "POST"],
|
|
allow_headers=["Authorization", "Content-Type", "X-API-Key"],
|
|
)
|
|
|
|
|
|
# === AUTH ===
|
|
@app.middleware("http")
|
|
async def auth_middleware(request: Request, call_next):
|
|
# Skip auth for health and schema
|
|
if request.url.path in ("/health", "/chatgpt-schema"):
|
|
return await call_next(request)
|
|
|
|
api_key = (
|
|
request.headers.get("X-API-Key")
|
|
or request.headers.get("Authorization", "").replace("Bearer ", "")
|
|
)
|
|
if api_key != API_KEY:
|
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
|
|
return await call_next(request)
|
|
|
|
|
|
# === LOGGING ===
|
|
@app.middleware("http")
|
|
async def log_middleware(request: Request, call_next):
|
|
start = time.time()
|
|
response = await call_next(request)
|
|
duration = time.time() - start
|
|
print(f"{request.method} {request.url.path} -> {response.status_code} ({duration:.2f}s)")
|
|
return response
|
|
|
|
|
|
# === ROUTERS ===
|
|
app.include_router(topics.router)
|
|
app.include_router(search.router)
|
|
app.include_router(pathology.router)
|
|
app.include_router(procedure.router)
|
|
app.include_router(activity.router)
|
|
app.include_router(admin.router)
|
|
app.include_router(comprehensive.router)
|
|
app.include_router(ontology_router.router)
|
|
app.include_router(unified.router)
|
|
|
|
|
|
# === HEALTH ===
|
|
@app.get("/health")
|
|
async def health():
|
|
try:
|
|
graph.query("RETURN 1")
|
|
return {"status": "ok", "service": "medgraph-api", "db": "connected"}
|
|
except Exception:
|
|
from fastapi.responses import JSONResponse
|
|
return JSONResponse({"status": "degraded", "service": "medgraph-api", "db": "disconnected"}, 503)
|
|
|
|
|
|
@app.get("/chatgpt-schema")
|
|
async def chatgpt_schema():
|
|
"""Schema reducido (5 endpoints) para importar en ChatGPT Actions."""
|
|
from fastapi.responses import JSONResponse
|
|
import json
|
|
for path in [
|
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "chatgpt_action_schema.json"),
|
|
"/app/chatgpt_action_schema.json",
|
|
"chatgpt_action_schema.json",
|
|
]:
|
|
if os.path.exists(path):
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return JSONResponse(content=json.load(f))
|
|
return {"error": "schema not found"}
|
|
|
|
|
|
@app.get("/stats")
|
|
async def stats():
|
|
return graph.get_stats()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run(app, host="0.0.0.0", port=8080)
|