Skip to content

Commit 9e0ebe9

Browse files
DMontgomery40Faxbot Agent
andauthored
p4(api): Admin marketplace listing endpoint (gated) (#21)
* p4(ui): add PluginMarketplace skeleton component (not wired yet) * p4(api): add admin marketplace listing endpoint (disabled by default) --------- Co-authored-by: Faxbot Agent <[email protected]>
1 parent f3f7ba1 commit 9e0ebe9

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

api/app/main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,14 @@ async def v4_config_flush_cache(scope: Optional[str] = None):
316316
# Non-fatal if health monitoring deps missing
317317
pass
318318

319+
# Admin marketplace (Phase 4; disabled by default)
320+
try:
321+
from .routers import admin_marketplace as _marketplace
322+
app.include_router(_marketplace.router)
323+
except Exception:
324+
# Non-fatal if marketplace router cannot be imported
325+
pass
326+
319327
# Webhook hardening router (DLQ + idempotency)
320328
try:
321329
from .routers import webhooks_v2 as _webhooks_v2
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from typing import Optional
5+
from fastapi import APIRouter, Depends, Header, HTTPException
6+
from api.app.config import settings
7+
from api.app.auth import verify_db_key
8+
9+
10+
def require_admin(x_api_key: Optional[str] = Header(default=None)):
11+
"""Minimal admin auth dependency for marketplace endpoints.
12+
- Accepts env bootstrap API key
13+
- Or a DB-backed key with scope keys:manage
14+
"""
15+
if settings.api_key and x_api_key == settings.api_key:
16+
return {"admin": True, "key_id": "env"}
17+
info = verify_db_key(x_api_key)
18+
if not info or ("keys:manage" not in (info.get("scopes") or [])):
19+
raise HTTPException(401, detail="Admin authentication failed")
20+
return info
21+
22+
23+
router = APIRouter(prefix="/admin/marketplace", tags=["AdminMarketplace"], dependencies=[Depends(require_admin)])
24+
25+
26+
@router.get("/plugins")
27+
def list_plugins():
28+
"""List available plugins in the marketplace (disabled by default).
29+
30+
Gate with ADMIN_MARKETPLACE_ENABLED=false by default to avoid exposing in prod
31+
until features are complete.
32+
"""
33+
enabled = os.getenv("ADMIN_MARKETPLACE_ENABLED", "false").lower() in {"1", "true", "yes"}
34+
if not enabled:
35+
# Hide endpoint when disabled to avoid confusing operators
36+
raise HTTPException(404, detail="Not found")
37+
# Placeholder: will be populated via trait‑aware registry in later PRs
38+
return {"plugins": []}
39+

0 commit comments

Comments
 (0)