33import logging
44from typing import Dict
55
6- from fastapi import FastAPI
6+ from fastapi import FastAPI , Response , Request
7+ from fastapi .exceptions import RequestValidationError
8+ from fastapi .responses import JSONResponse
79from starlette .middleware .cors import CORSMiddleware
810
911from api .config import Settings
1315from api .routes_quote import router as quote_router
1416from api .routes_cnc import router as cnc_router
1517from api .routes_sync import router as sync_router
18+ from prometheus_client import CONTENT_TYPE_LATEST , generate_latest
1619
1720# Configure logging
1821logging .basicConfig (
3033 version = "0.1.0" ,
3134)
3235
36+ from api .db import Base , engine
37+ from api .config import Settings as _Settings
38+
39+ # Ensure tables exist in SQLite (dev/test); migrations still handle Postgres
40+ if _Settings ().database_url .startswith ("sqlite" ):
41+ @app .on_event ("startup" )
42+ async def _ensure_sqlite_tables () -> None :
43+ Base .metadata .create_all (bind = engine )
44+
3345# Add CORS middleware
3446app .add_middleware (
3547 CORSMiddleware ,
4860app .include_router (sync_router )
4961
5062
63+ @app .exception_handler (RequestValidationError )
64+ async def handle_validation_error (request : Request , exc : RequestValidationError ) -> JSONResponse :
65+ # Unified error envelope for malformed JSON / validation errors
66+ return JSONResponse (
67+ status_code = 422 ,
68+ content = {
69+ "ok" : False ,
70+ "error" : {
71+ "code" : "BAD_REQUEST" ,
72+ "message" : "invalid request" ,
73+ "details" : exc .errors (),
74+ },
75+ },
76+ )
77+
78+
5179@app .get ("/" )
5280async def root () -> Dict [str , str ]:
5381 """Root endpoint of the API.
@@ -62,8 +90,7 @@ async def root() -> Dict[str, str]:
6290
6391@app .get ("/healthcheck" )
6492async def healthcheck () -> Dict [str , str ]:
65- """Health check endpoint.
66-
93+ """
6794 This endpoint can be used to verify that the API is running and responsive.
6895
6996 Returns
@@ -72,3 +99,9 @@ async def healthcheck() -> Dict[str, str]:
7299 A dictionary indicating the health status of the API.
73100 """
74101 return {"status" : "healthy" }
102+
103+
104+ @app .get ("/metrics" )
105+ async def metrics () -> Response :
106+ # Expose Prometheus metrics including default process/python collectors
107+ return Response (generate_latest (), media_type = CONTENT_TYPE_LATEST )
0 commit comments