Spaces:
Paused
Paused
Download app/main.py from Ethscriptions/coinpush: direct link, hf CLI and curl.
- Browser
- Download file 6.53 kB
-
https://huggingface.co/spaces/Ethscriptions/coinpush/resolve/main/app/main.py
- Command line
-
hf download hf://spaces/Ethscriptions/coinpush/app/main.py
-
curl -L -o main.py https://huggingface.co/spaces/Ethscriptions/coinpush/resolve/main/app/main.py
6.53 kB
| import os | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI, HTTPException, Request | |
| from fastapi.responses import HTMLResponse, JSONResponse, Response | |
| from app.core.dashboard import render_dashboard | |
| from app.core.platform import MonitoringPlatform | |
| platform = MonitoringPlatform() | |
| async def lifespan(app): | |
| if os.getenv("DISABLE_WORKER", "false").lower() not in ("1", "true", "yes", "on"): | |
| platform.start() | |
| yield | |
| platform.stop() | |
| app = FastAPI(title="监控平台", version="1.0.0", lifespan=lifespan) | |
| def dashboard(): | |
| return render_dashboard() | |
| def manifest(): | |
| return JSONResponse({ | |
| "name": "监控平台", | |
| "short_name": "监控", | |
| "description": "价格、财务指标和脚本健康统一监控", | |
| "start_url": "/", | |
| "scope": "/", | |
| "display": "standalone", | |
| "background_color": "#f3f5f8", | |
| "theme_color": "#111827", | |
| "icons": [ | |
| { | |
| "src": "/icons/icon.svg", | |
| "sizes": "any", | |
| "type": "image/svg+xml", | |
| "purpose": "any maskable", | |
| } | |
| ], | |
| }, media_type="application/manifest+json") | |
| def service_worker(): | |
| script = """ | |
| const CACHE_NAME = "coinpush-pwa-v1"; | |
| const APP_SHELL = ["/", "/manifest.webmanifest", "/icons/icon.svg"]; | |
| self.addEventListener("install", (event) => { | |
| event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL))); | |
| self.skipWaiting(); | |
| }); | |
| self.addEventListener("activate", (event) => { | |
| event.waitUntil( | |
| caches.keys().then((keys) => Promise.all( | |
| keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)) | |
| )) | |
| ); | |
| self.clients.claim(); | |
| }); | |
| self.addEventListener("fetch", (event) => { | |
| const url = new URL(event.request.url); | |
| if (url.pathname.startsWith("/api/") || url.pathname === "/healthz") return; | |
| if (event.request.mode === "navigate") { | |
| event.respondWith( | |
| fetch(event.request) | |
| .then((response) => { | |
| const copy = response.clone(); | |
| caches.open(CACHE_NAME).then((cache) => cache.put("/", copy)); | |
| return response; | |
| }) | |
| .catch(() => caches.match("/") || new Response("Offline", {status: 503})) | |
| ); | |
| return; | |
| } | |
| event.respondWith( | |
| caches.match(event.request).then((cached) => cached || fetch(event.request)) | |
| ); | |
| }); | |
| """.strip() | |
| return Response(script, media_type="application/javascript") | |
| def app_icon(): | |
| svg = """ | |
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> | |
| <rect width="512" height="512" rx="96" fill="#111827"/> | |
| <path d="M118 335h276" stroke="#f9fafb" stroke-width="34" stroke-linecap="round"/> | |
| <path d="M142 302l72-78 58 45 98-118" fill="none" stroke="#2dd4bf" stroke-width="34" stroke-linecap="round" stroke-linejoin="round"/> | |
| <circle cx="142" cy="302" r="22" fill="#f59e0b"/> | |
| <circle cx="214" cy="224" r="22" fill="#f9fafb"/> | |
| <circle cx="272" cy="269" r="22" fill="#f59e0b"/> | |
| <circle cx="370" cy="151" r="22" fill="#f9fafb"/> | |
| </svg> | |
| """.strip() | |
| return Response(svg, media_type="image/svg+xml") | |
| def healthz(): | |
| status = platform.status() | |
| errors = [m for m in status["monitors"] if m["status"] == "error"] | |
| return { | |
| "ok": len(errors) == 0, | |
| "started": status["started"], | |
| "error_monitors": len(errors), | |
| } | |
| def api_status(): | |
| return JSONResponse(platform.status()) | |
| def api_monitors(): | |
| return platform.store.list_monitors() | |
| def api_pause_monitor(monitor_id: str): | |
| try: | |
| return platform.set_monitor_paused(monitor_id, True) | |
| except KeyError as exc: | |
| raise HTTPException(status_code=404, detail=str(exc)) from exc | |
| def api_resume_monitor(monitor_id: str): | |
| try: | |
| return platform.set_monitor_paused(monitor_id, False) | |
| except KeyError as exc: | |
| raise HTTPException(status_code=404, detail=str(exc)) from exc | |
| def api_alerts(limit: int = 50): | |
| return platform.store.recent_alerts(limit) | |
| def api_events(limit: int = 80): | |
| return platform.store.recent_events(limit) | |
| def api_get_config(): | |
| return platform.get_config() | |
| async def api_update_config(request: Request): | |
| try: | |
| cfg = await request.json() | |
| if not isinstance(cfg, dict) or "coins" not in cfg or "global" not in cfg: | |
| raise ValueError("配置必须包含 coins 和 global") | |
| return platform.update_config(cfg) | |
| except Exception as exc: | |
| raise HTTPException(status_code=400, detail=str(exc)) from exc | |
| def api_calibrate_all(): | |
| try: | |
| return platform.calibrate_all() | |
| except Exception as exc: | |
| raise HTTPException(status_code=500, detail=str(exc)) from exc | |
| def api_calibrate_coin(coin: str): | |
| try: | |
| return platform.calibrate_coin(coin) | |
| except KeyError as exc: | |
| raise HTTPException(status_code=404, detail=str(exc)) from exc | |
| except Exception as exc: | |
| raise HTTPException(status_code=500, detail=str(exc)) from exc | |
| def api_crypto_klines(coin: str, interval: str = "1h", limit: int = 120): | |
| try: | |
| return JSONResponse(platform.get_klines(coin, interval, limit)) | |
| except KeyError as exc: | |
| raise HTTPException(status_code=404, detail=str(exc)) from exc | |
| except (TypeError, ValueError) as exc: | |
| raise HTTPException(status_code=400, detail=str(exc)) from exc | |
| except Exception as exc: | |
| raise HTTPException(status_code=502, detail=str(exc)) from exc | |
| def api_get_market_move_config(): | |
| return platform.get_market_move_config() | |
| async def api_update_market_move_config(request: Request): | |
| try: | |
| cfg = await request.json() | |
| if not isinstance(cfg, dict) or "coins" not in cfg: | |
| raise ValueError("配置必须包含 coins") | |
| return platform.update_market_move_config(cfg) | |
| except Exception as exc: | |
| raise HTTPException(status_code=400, detail=str(exc)) from exc | |