- FastMCP server with 12 tools (pullzone, cache, statistics, shield) - Dockerfile with multi-stage build (python:3.11-slim + uv) - K8s manifests (Deployment + Service) - Gitea Actions CI/CD pipeline (build → push → deploy)
67 lines
2.1 KiB
Python
67 lines
2.1 KiB
Python
"""BunnyCDN REST API client using httpx."""
|
|
|
|
import httpx
|
|
|
|
from .config import BUNNY_API_BASE, BUNNY_API_KEY, REQUEST_TIMEOUT, logger
|
|
|
|
|
|
class BunnyClient:
|
|
"""Async HTTP client for BunnyCDN API."""
|
|
|
|
def __init__(self) -> None:
|
|
self._base = BUNNY_API_BASE
|
|
self._headers = {
|
|
"AccessKey": BUNNY_API_KEY,
|
|
"Accept": "application/json",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
async def get(self, path: str, params: dict | None = None) -> dict | list:
|
|
async with httpx.AsyncClient(timeout=REQUEST_TIMEOUT) as c:
|
|
resp = await c.get(
|
|
f"{self._base}{path}",
|
|
headers=self._headers,
|
|
params=params,
|
|
)
|
|
return self._handle(resp)
|
|
|
|
async def post(self, path: str, json: dict | None = None) -> dict | list | str:
|
|
async with httpx.AsyncClient(timeout=REQUEST_TIMEOUT) as c:
|
|
resp = await c.post(
|
|
f"{self._base}{path}",
|
|
headers=self._headers,
|
|
json=json,
|
|
)
|
|
return self._handle(resp)
|
|
|
|
async def delete(self, path: str) -> dict | str:
|
|
async with httpx.AsyncClient(timeout=REQUEST_TIMEOUT) as c:
|
|
resp = await c.delete(
|
|
f"{self._base}{path}",
|
|
headers=self._headers,
|
|
)
|
|
return self._handle(resp)
|
|
|
|
def _handle(self, resp: httpx.Response) -> dict | list | str:
|
|
if resp.status_code == 401:
|
|
raise BunnyAPIError("Authentication failed: invalid API key")
|
|
if resp.status_code == 404:
|
|
raise BunnyAPIError(f"Not found: {resp.url}")
|
|
if resp.status_code >= 500:
|
|
raise BunnyAPIError(f"Server error {resp.status_code}: {resp.text}")
|
|
if resp.status_code >= 400:
|
|
raise BunnyAPIError(f"Client error {resp.status_code}: {resp.text}")
|
|
if not resp.content:
|
|
return ""
|
|
try:
|
|
return resp.json()
|
|
except Exception:
|
|
return resp.text
|
|
|
|
|
|
class BunnyAPIError(Exception):
|
|
pass
|
|
|
|
|
|
client = BunnyClient()
|