fix: add debug logging to admin API key validation and re-enable all routers

- Add debug print statements to _validate_api_key and require_admin_key for troubleshooting
- Add /admin/debug-settings and /admin/test-key endpoints for API key validation testing
- Bypass require_admin_key dependency in /admin/stats endpoint for direct validation
- Fix database warmup to properly handle session generator lifecycle
- Re-enable all previously disabled routers in main.py
- Add custom OpenAPI security scheme
This commit is contained in:
oib
2026-03-05 13:44:37 +01:00
parent b44aeaad97
commit 83b5152b40
12 changed files with 556 additions and 65 deletions

View File

@@ -12,9 +12,13 @@ from .storage import SessionDep
def _validate_api_key(allowed_keys: list[str], api_key: str | None) -> str:
# Temporarily more permissive for debugging
print(f"DEBUG: _validate_api_key called with api_key='{api_key}', allowed_keys={allowed_keys}")
allowed = {key.strip() for key in allowed_keys if key}
if not api_key or api_key not in allowed:
print(f"DEBUG: API key validation failed - api_key not in allowed_keys")
raise HTTPException(status_code=401, detail="invalid api key")
print(f"DEBUG: API key validation successful")
return api_key
@@ -51,7 +55,11 @@ def require_admin_key() -> Callable[[str | None], str]:
"""Dependency for admin API key authentication (reads live settings)."""
def validator(api_key: str | None = Header(default=None, alias="X-Api-Key")) -> str:
return _validate_api_key(settings.admin_api_keys, api_key)
print(f"DEBUG: Received API key: {api_key}")
print(f"DEBUG: Allowed admin keys: {settings.admin_api_keys}")
result = _validate_api_key(settings.admin_api_keys, api_key)
print(f"DEBUG: Validation result: {result}")
return result
return validator

View File

@@ -33,7 +33,8 @@ from .routers import (
cross_chain_integration,
global_marketplace_integration,
developer_platform,
governance_enhanced
governance_enhanced,
blockchain
)
# Skip optional routers with missing dependencies
try:
@@ -84,13 +85,17 @@ async def lifespan(app: FastAPI):
try:
# Test database connectivity
from sqlmodel import select
from ..domain import Job
from ..storage import get_session
from .domain import Job
from .storage import get_session
# Simple connectivity test using dependency injection
with get_session() as session:
session_gen = get_session()
session = next(session_gen)
try:
test_query = select(Job).limit(1)
session.exec(test_query).first()
session.execute(test_query).first()
finally:
session.close()
logger.info("Database warmup completed successfully")
except Exception as e:
logger.warning(f"Database warmup failed: {e}")
@@ -191,12 +196,21 @@ def create_app() -> FastAPI:
app = FastAPI(
title="AITBC Coordinator API",
version="0.1.0",
description="Stage 1 coordinator service handling job orchestration between clients and miners.",
description="API for coordinating AI training jobs and blockchain operations",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
lifespan=lifespan,
# Custom OpenAPI config to handle SessionDep issues
openapi_components={
"securitySchemes": {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "X-Api-Key"
}
}
},
openapi_tags=[
{"name": "health", "description": "Health check endpoints"},
{"name": "client", "description": "Client operations"},
@@ -223,37 +237,37 @@ def create_app() -> FastAPI:
allow_headers=["*"] # Allow all headers for API keys and content types
)
# Temporarily disable some routers to isolate the Pydantic issue
# app.include_router(client, prefix="/v1")
# app.include_router(miner, prefix="/v1")
# app.include_router(admin, prefix="/v1")
# app.include_router(marketplace, prefix="/v1")
# app.include_router(marketplace_gpu, prefix="/v1")
# app.include_router(explorer, prefix="/v1")
# app.include_router(services, prefix="/v1")
# app.include_router(users, prefix="/v1")
# app.include_router(exchange, prefix="/v1")
# app.include_router(marketplace_offers, prefix="/v1")
# app.include_router(payments, prefix="/v1")
# app.include_router(web_vitals, prefix="/v1")
# app.include_router(edge_gpu)
# if ml_zk_proofs:
# app.include_router(ml_zk_proofs)
# app.include_router(marketplace_enhanced, prefix="/v1")
# app.include_router(openclaw_enhanced, prefix="/v1")
# app.include_router(monitoring_dashboard, prefix="/v1")
# app.include_router(agent_router.router, prefix="/v1/agents")
# app.include_router(agent_identity, prefix="/v1")
# app.include_router(global_marketplace, prefix="/v1")
# app.include_router(cross_chain_integration, prefix="/v1")
# app.include_router(global_marketplace_integration, prefix="/v1")
# app.include_router(developer_platform, prefix="/v1")
# app.include_router(governance_enhanced, prefix="/v1")
# Enable all routers with OpenAPI disabled
app.include_router(client, prefix="/v1")
app.include_router(miner, prefix="/v1")
app.include_router(admin, prefix="/v1")
app.include_router(marketplace, prefix="/v1")
app.include_router(marketplace_gpu, prefix="/v1")
app.include_router(explorer, prefix="/v1")
app.include_router(services, prefix="/v1")
app.include_router(users, prefix="/v1")
app.include_router(exchange, prefix="/v1")
app.include_router(marketplace_offers, prefix="/v1")
app.include_router(payments, prefix="/v1")
app.include_router(web_vitals, prefix="/v1")
app.include_router(edge_gpu)
if ml_zk_proofs:
app.include_router(ml_zk_proofs)
app.include_router(marketplace_enhanced, prefix="/v1")
app.include_router(openclaw_enhanced, prefix="/v1")
app.include_router(monitoring_dashboard, prefix="/v1")
app.include_router(agent_router.router, prefix="/v1/agents")
app.include_router(agent_identity, prefix="/v1")
app.include_router(global_marketplace, prefix="/v1")
app.include_router(cross_chain_integration, prefix="/v1")
app.include_router(global_marketplace_integration, prefix="/v1")
app.include_router(developer_platform, prefix="/v1")
app.include_router(governance_enhanced, prefix="/v1")
# Only include blockchain for testing
# Add blockchain router for CLI compatibility
print(f"Adding blockchain router: {blockchain}")
app.include_router(blockchain, prefix="/v1")
# from .routers import blockchain as blockchain_router
# app.include_router(blockchain_router, prefix="/v1")
print("Blockchain router added successfully")
# Add Prometheus metrics endpoint
metrics_app = make_asgi_app()

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status, Request
from fastapi import APIRouter, Depends, HTTPException, status, Request, Header
from sqlmodel import select
from slowapi import Limiter
from slowapi.util import get_remote_address
@@ -16,14 +16,48 @@ limiter = Limiter(key_func=get_remote_address)
router = APIRouter(prefix="/admin", tags=["admin"])
@router.get("/debug-settings", summary="Debug settings")
async def debug_settings() -> dict: # type: ignore[arg-type]
return {
"admin_api_keys": settings.admin_api_keys,
"client_api_keys": settings.client_api_keys,
"miner_api_keys": settings.miner_api_keys,
"app_env": settings.app_env
}
@router.get("/test-key", summary="Test API key validation")
async def test_key(
api_key: str = Header(default=None, alias="X-Api-Key")
) -> dict[str, str]: # type: ignore[arg-type]
print(f"DEBUG: Received API key: {api_key}")
print(f"DEBUG: Allowed admin keys: {settings.admin_api_keys}")
if not api_key or api_key not in settings.admin_api_keys:
print(f"DEBUG: API key validation failed!")
raise HTTPException(status_code=401, detail="invalid api key")
print(f"DEBUG: API key validation successful!")
return {"message": "API key is valid", "key": api_key}
@router.get("/stats", summary="Get coordinator stats")
@limiter.limit(lambda: settings.rate_limit_admin_stats)
@cached(**get_cache_config("job_list")) # Cache admin stats for 1 minute
async def get_stats(
request: Request,
session: SessionDep,
admin_key: str = Depends(require_admin_key())
api_key: str = Header(default=None, alias="X-Api-Key")
) -> dict[str, int]: # type: ignore[arg-type]
# Temporary debug: bypass dependency and validate directly
print(f"DEBUG: Received API key: {api_key}")
print(f"DEBUG: Allowed admin keys: {settings.admin_api_keys}")
if not api_key or api_key not in settings.admin_api_keys:
raise HTTPException(status_code=401, detail="invalid api key")
print(f"DEBUG: API key validation successful!")
service = JobService(session)
from sqlmodel import func, select
from ..domain import Job

View File

@@ -89,7 +89,10 @@ def session_scope() -> Generator[Session, None, None]:
# Dependency for FastAPI
from fastapi import Depends
from typing import Annotated
from typing import Annotated, TYPE_CHECKING
if TYPE_CHECKING:
from sqlalchemy.orm import Session
def get_session():
"""Get a database session."""
@@ -97,8 +100,8 @@ def get_session():
with Session(engine) as session:
yield session
# Create SessionDep as Annotated type - this should work with proper imports
SessionDep = Annotated[Session, Depends(get_session)]
# Use string annotation to avoid ForwardRef issues
SessionDep = Annotated["Session", Depends(get_session)]
# Async support for future use