fix: disable global API key middleware and add test miner creation endpoint
All checks were successful
API Endpoint Tests / test-api-endpoints (push) Successful in 47s
Documentation Validation / validate-docs (push) Successful in 17s
Integration Tests / test-service-integration (push) Successful in 2m11s
Python Tests / test-python (push) Successful in 5m49s
Security Scanning / security-scan (push) Successful in 4m1s
Systemd Sync / sync-systemd (push) Successful in 14s
All checks were successful
API Endpoint Tests / test-api-endpoints (push) Successful in 47s
Documentation Validation / validate-docs (push) Successful in 17s
Integration Tests / test-service-integration (push) Successful in 2m11s
Python Tests / test-python (push) Successful in 5m49s
Security Scanning / security-scan (push) Successful in 4m1s
Systemd Sync / sync-systemd (push) Successful in 14s
API Key Middleware Changes:
- Disabled global API key middleware in favor of dependency injection
- Added comment explaining the change
- Preserves existing middleware code for reference
Admin Router Enhancements:
✅ NEW ENDPOINT: POST /debug/create-test-miner for debugging marketplace sync
- Creates test miner with id "debug-test-miner"
- Updates existing miner to ONLINE status if already exists
- Returns miner_id and session_token for testing
- Requires
This commit is contained in:
@@ -248,21 +248,21 @@ def create_app() -> FastAPI:
|
||||
]
|
||||
)
|
||||
|
||||
# API Key middleware (if configured)
|
||||
required_key = os.getenv("COORDINATOR_API_KEY")
|
||||
if required_key:
|
||||
@app.middleware("http")
|
||||
async def api_key_middleware(request: Request, call_next):
|
||||
# Health endpoints are exempt
|
||||
if request.url.path in ("/health", "/v1/health", "/health/live", "/health/ready", "/metrics", "/rate-limit-metrics"):
|
||||
return await call_next(request)
|
||||
provided = request.headers.get("X-Api-Key")
|
||||
if provided != required_key:
|
||||
return JSONResponse(
|
||||
status_code=401,
|
||||
content={"detail": "Invalid or missing API key"}
|
||||
)
|
||||
return await call_next(request)
|
||||
# API Key middleware (if configured) - DISABLED in favor of dependency injection
|
||||
# required_key = os.getenv("COORDINATOR_API_KEY")
|
||||
# if required_key:
|
||||
# @app.middleware("http")
|
||||
# async def api_key_middleware(request: Request, call_next):
|
||||
# # Health endpoints are exempt
|
||||
# if request.url.path in ("/health", "/v1/health", "/health/live", "/health/ready", "/metrics", "/rate-limit-metrics"):
|
||||
# return await call_next(request)
|
||||
# provided = request.headers.get("X-Api-Key")
|
||||
# if provided != required_key:
|
||||
# return JSONResponse(
|
||||
# status_code=401,
|
||||
# content={"detail": "Invalid or missing API key"}
|
||||
# )
|
||||
# return await call_next(request)
|
||||
|
||||
app.state.limiter = limiter
|
||||
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
||||
|
||||
@@ -29,6 +29,68 @@ async def debug_settings() -> dict: # type: ignore[arg-type]
|
||||
}
|
||||
|
||||
|
||||
@router.post("/debug/create-test-miner", summary="Create a test miner for debugging")
|
||||
async def create_test_miner(
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
admin_key: str = Depends(require_admin_key())
|
||||
) -> dict[str, str]: # type: ignore[arg-type]
|
||||
"""Create a test miner for debugging marketplace sync"""
|
||||
try:
|
||||
from ..domain import Miner
|
||||
from uuid import uuid4
|
||||
|
||||
miner_id = "debug-test-miner"
|
||||
session_token = uuid4().hex
|
||||
|
||||
# Check if miner already exists
|
||||
existing_miner = session.get(Miner, miner_id)
|
||||
if existing_miner:
|
||||
# Update existing miner to ONLINE
|
||||
existing_miner.status = "ONLINE"
|
||||
existing_miner.last_heartbeat = datetime.utcnow()
|
||||
existing_miner.session_token = session_token
|
||||
session.add(existing_miner)
|
||||
session.commit()
|
||||
return {"status": "updated", "miner_id": miner_id, "message": "Existing miner updated to ONLINE"}
|
||||
|
||||
# Create new test miner
|
||||
miner = Miner(
|
||||
id=miner_id,
|
||||
capabilities={
|
||||
"gpu_memory": 8192,
|
||||
"models": ["qwen3:8b"],
|
||||
"pricing_per_hour": 0.50,
|
||||
"gpu": "RTX 4090",
|
||||
"gpu_memory_gb": 8192,
|
||||
"gpu_count": 1,
|
||||
"cuda_version": "12.0",
|
||||
"supported_models": ["qwen3:8b"]
|
||||
},
|
||||
concurrency=1,
|
||||
region="test-region",
|
||||
session_token=session_token,
|
||||
status="ONLINE",
|
||||
inflight=0,
|
||||
last_heartbeat=datetime.utcnow()
|
||||
)
|
||||
|
||||
session.add(miner)
|
||||
session.commit()
|
||||
session.refresh(miner)
|
||||
|
||||
logger.info(f"Created test miner: {miner_id}")
|
||||
return {
|
||||
"status": "created",
|
||||
"miner_id": miner_id,
|
||||
"session_token": session_token,
|
||||
"message": "Test miner created successfully"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create test miner: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/test-key", summary="Test API key validation")
|
||||
async def test_key(
|
||||
api_key: str = Header(default=None, alias="X-Api-Key")
|
||||
@@ -102,23 +164,26 @@ async def list_jobs(session: Annotated[Session, Depends(get_session)], admin_key
|
||||
|
||||
@router.get("/miners", summary="List miners")
|
||||
async def list_miners(session: Annotated[Session, Depends(get_session)], admin_key: str = Depends(require_admin_key())) -> dict[str, list[dict]]: # type: ignore[arg-type]
|
||||
miner_service = MinerService(session)
|
||||
miners = [
|
||||
from sqlmodel import select
|
||||
from ..domain import Miner
|
||||
|
||||
miners = session.execute(select(Miner)).scalars().all()
|
||||
miner_list = [
|
||||
{
|
||||
"miner_id": record.id,
|
||||
"status": record.status,
|
||||
"inflight": record.inflight,
|
||||
"concurrency": record.concurrency,
|
||||
"region": record.region,
|
||||
"last_heartbeat": record.last_heartbeat.isoformat(),
|
||||
"average_job_duration_ms": record.average_job_duration_ms,
|
||||
"jobs_completed": record.jobs_completed,
|
||||
"jobs_failed": record.jobs_failed,
|
||||
"last_receipt_id": record.last_receipt_id,
|
||||
"miner_id": miner.id,
|
||||
"status": miner.status,
|
||||
"inflight": miner.inflight,
|
||||
"concurrency": miner.concurrency,
|
||||
"region": miner.region,
|
||||
"last_heartbeat": miner.last_heartbeat.isoformat(),
|
||||
"average_job_duration_ms": miner.average_job_duration_ms,
|
||||
"jobs_completed": miner.jobs_completed,
|
||||
"jobs_failed": miner.jobs_failed,
|
||||
"last_receipt_id": miner.last_receipt_id,
|
||||
}
|
||||
for record in miner_service.list_records()
|
||||
for miner in miners
|
||||
]
|
||||
return {"items": miners}
|
||||
return {"items": miner_list}
|
||||
|
||||
|
||||
@router.get("/status", summary="Get system status", response_model=None)
|
||||
|
||||
@@ -24,9 +24,10 @@ async def sync_offers(
|
||||
"""Create marketplace offers from all registered miners"""
|
||||
|
||||
# Get all registered miners
|
||||
miners = session.execute(select(Miner).where(Miner.status == "ONLINE")).all()
|
||||
miners = session.execute(select(Miner).where(Miner.status == "ONLINE")).scalars().all()
|
||||
|
||||
created_offers = []
|
||||
offer_objects = []
|
||||
|
||||
for miner in miners:
|
||||
# Check if offer already exists
|
||||
@@ -54,10 +55,14 @@ async def sync_offers(
|
||||
)
|
||||
|
||||
session.add(offer)
|
||||
created_offers.append(offer.id)
|
||||
offer_objects.append(offer)
|
||||
|
||||
session.commit()
|
||||
|
||||
# Collect offer IDs after commit (when IDs are generated)
|
||||
for offer in offer_objects:
|
||||
created_offers.append(offer.id)
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"created_offers": len(created_offers),
|
||||
|
||||
@@ -55,7 +55,8 @@ async def heartbeat(
|
||||
async def poll(
|
||||
req: PollRequest,
|
||||
session: Annotated[Session, Depends(get_session)],
|
||||
miner_id: str = Depends(require_miner_key()),
|
||||
api_key: str = Depends(require_miner_key()),
|
||||
miner_id: str = Depends(get_miner_id()),
|
||||
) -> AssignedJob | Response: # type: ignore[arg-type]
|
||||
job = MinerService(session).poll(miner_id, req.max_wait_seconds)
|
||||
if job is None:
|
||||
|
||||
Reference in New Issue
Block a user