90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
"""HTTP middleware helpers (Flask request hooks)."""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
|
|
from flask import Flask, g, request
|
|
|
|
from . import settings
|
|
from .metrics import observe_request
|
|
from .utils import generate_request_id
|
|
|
|
|
|
def register_request_hooks(app: Flask) -> None:
|
|
"""Attach before/after request handlers for logging and correlation."""
|
|
|
|
@app.before_request
|
|
def attach_request_id_and_log(): # type: ignore[unused-ignore]
|
|
rid = request.headers.get("X-Request-Id")
|
|
if not rid:
|
|
rid = generate_request_id()
|
|
request.environ["HTTP_X_REQUEST_ID"] = rid
|
|
request.request_id = rid # type: ignore[attr-defined]
|
|
|
|
if settings.ENABLE_REQUEST_LOGS:
|
|
try:
|
|
logging.info(
|
|
"request.start",
|
|
extra={
|
|
"request_id": rid,
|
|
"method": request.method,
|
|
"path": request.path,
|
|
"remote_addr": request.remote_addr,
|
|
},
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
g._start_time = time.time()
|
|
except Exception:
|
|
g._start_time = None # type: ignore[attr-defined]
|
|
|
|
@app.after_request
|
|
def add_request_id_header(response): # type: ignore[unused-ignore]
|
|
try:
|
|
rid = getattr(request, "request_id", None) or request.environ.get(
|
|
"HTTP_X_REQUEST_ID")
|
|
if rid:
|
|
response.headers["X-Request-Id"] = rid
|
|
|
|
if settings.ENABLE_REQUEST_LOGS:
|
|
try:
|
|
logging.info(
|
|
"request.end",
|
|
extra={
|
|
"request_id": rid,
|
|
"status": response.status_code,
|
|
"path": request.path,
|
|
},
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
start_time = getattr(g, "_start_time", None)
|
|
observe_request(request.method, request.path,
|
|
start_time, response.status_code)
|
|
except Exception:
|
|
pass
|
|
return response
|
|
|
|
@app.after_request
|
|
def add_cors_headers(response): # type: ignore[unused-ignore]
|
|
# Add CORS headers for embedded forms
|
|
if request.path.startswith("/api/"):
|
|
response.headers["Access-Control-Allow-Origin"] = "*"
|
|
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
|
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
|
|
|
|
return response
|
|
|
|
@app.before_request
|
|
def handle_options(): # type: ignore[unused-ignore]
|
|
if request.method == "OPTIONS":
|
|
response = app.response_class()
|
|
response.headers["Access-Control-Allow-Origin"] = "*"
|
|
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
|
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
|
|
return response
|