Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, Request, HTTPException | |
| from fastapi.responses import StreamingResponse, HTMLResponse | |
| import subprocess | |
| import httpx | |
| app = FastAPI(title="Market-Data Observatory") | |
| # Async HTTP client for proxying to ClickHouse's internal HTTP interface | |
| ch_client = httpx.AsyncClient(base_url="http://127.0.0.1:8123", timeout=60.0) | |
| # ββ Refresh endpoint (git pull) ββ | |
| def refresh(): | |
| try: | |
| result = subprocess.check_output( | |
| "cd /app/data/ohlc_data && git pull", | |
| shell=True, stderr=subprocess.STDOUT | |
| ) | |
| return {"status": "updated", "details": result.decode().strip()} | |
| except subprocess.CalledProcessError as e: | |
| msg = e.output.decode() if e.output else str(e) | |
| raise HTTPException(status_code=500, detail={"error": "Refresh failed", "message": msg}) | |
| # ββ Health check ββ | |
| async def health(): | |
| try: | |
| r = await ch_client.get("/ping") | |
| return {"clickhouse": r.text.strip(), "status": "ok"} | |
| except Exception as e: | |
| raise HTTPException(status_code=503, detail={"status": "unhealthy", "error": str(e)}) | |
| # ββ Catch-all reverse proxy to ClickHouse ββ | |
| # This gives you /play, /dashboard, native HTTP API, everything. | |
| async def proxy_to_clickhouse(request: Request, path: str): | |
| url = httpx.URL(path=f"/{path}", query=request.url.query.encode("utf-8")) | |
| # Forward headers, strip hop-by-hop | |
| headers = dict(request.headers) | |
| for h in ("host", "content-length", "transfer-encoding"): | |
| headers.pop(h, None) | |
| try: | |
| body = await request.body() | |
| req = ch_client.build_request( | |
| method=request.method, | |
| url=url, | |
| headers=headers, | |
| content=body, | |
| ) | |
| r = await ch_client.send(req, stream=True) | |
| # Pass through ClickHouse response headers | |
| resp_headers = dict(r.headers) | |
| for h in ("content-length", "content-encoding", "transfer-encoding"): | |
| resp_headers.pop(h, None) | |
| return StreamingResponse( | |
| r.aiter_raw(), | |
| status_code=r.status_code, | |
| headers=resp_headers, | |
| ) | |
| except httpx.RequestError as exc: | |
| raise HTTPException(status_code=502, detail=f"ClickHouse proxy error: {exc}") | |