Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 22 additions & 32 deletions app/core/request_id_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import json
import os
import time
from contextvars import ContextVar

from fastapi import Request
Expand All @@ -13,19 +12,12 @@
from app.utils.utils import get_uuid


def _load_openclaw_gateway_token() -> str:
config_path = os.environ.get("OPENCLAW_CONFIG_PATH")
if not config_path:
return ""
CREATE_INSTANCE_APIKEY_ENV = "CREATE_INSTANCE_APIKEY"

try:
with open(config_path, "r", encoding="utf-8") as f:
data = json.load(f)
token = ((data.get("gateway") or {}).get("auth") or {}).get("token")
return token if isinstance(token, str) and token else ""
except Exception as e:
logger.warning(f"OPENCLAW_CONFIG_PATH load failed: {config_path}: {e}")
return ""

def _get_create_instance_apikey() -> str:
apikey = os.environ.get(CREATE_INSTANCE_APIKEY_ENV)
return apikey if isinstance(apikey, str) and apikey else ""

REQUEST_ID_HEADER = "X-Request-ID"

Expand Down Expand Up @@ -55,35 +47,33 @@ def truncate_long_strings(obj, max_length: int = 50):
class RequestIDMiddleware(BaseHTTPMiddleware):
def __init__(self, app):
super().__init__(app)
self._gateway_token = ""
self._gateway_token_loaded_at = 0.0

def _refresh_gateway_token_if_needed(self) -> None:
if not os.environ.get("OPENCLAW_CONFIG_PATH"):
self._gateway_token = ""
self._gateway_token_loaded_at = 0.0
return

now = time.time()
if self._gateway_token_loaded_at and (now - self._gateway_token_loaded_at) < 60:
return
self._create_instance_apikey = ""

self._gateway_token = _load_openclaw_gateway_token()
self._gateway_token_loaded_at = now
def _refresh_create_instance_apikey_if_needed(self) -> None:
# Keep a per-process cached copy to avoid repeated os.environ lookups.
self._create_instance_apikey = _get_create_instance_apikey()

async def dispatch(self, request: Request, call_next):
request_id = get_uuid(remove_hyphen=True)
request.state.request_id = request_id
request.state.upstream_request_id = ""
token = request_id_ctx.set(request_id)

self._refresh_gateway_token_if_needed()
if self._gateway_token and request.client and request.client.host not in {"127.0.0.1", "::1"}:
auth = request.headers.get("Authorization")
expected = f"Bearer {self._gateway_token}"
self._refresh_create_instance_apikey_if_needed()
if self._create_instance_apikey and request.client and request.client.host not in {"127.0.0.1", "::1"}:
auth = request.headers.get("Authorization") or ""
expected = f"Bearer {self._create_instance_apikey}"
if auth != expected:
request_id_ctx.reset(token)
return JSONResponse(status_code=401, content={"detail": "Unauthorized"})
return JSONResponse(status_code=401, content={
"error": {
"err_code": -10001,
"message": "Missing 302 Apikey",
"message_cn": "缺少 302 API 密钥",
"message_jp": "302 APIキーがありません",
"type": "api_error"
}
})

# Streaming endpoints: don't read body.
is_streaming = False
Expand Down
Loading