from flask import Flask, render_template_string, jsonify, Response import requests import os app = Flask(__name__) lightModeStyle = """ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Inter", "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif; background: #f0f2f5; color: #333; padding: 20px; min-height: 100vh; } .container { max-width: 1400px; margin: 0 auto; animation: fadeIn 0.5s ease; padding: 0 20px; } .overview { background: #fff; border-radius: 15px; padding: 25px; margin-bottom: 30px; border: 1px solid #dfe1e6; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .overview-title { font-size: 20px; display: flex; align-items: center; gap: 10px; margin-bottom: 20px; color: #2e86de; font-weight: 600; } .overview-title i { margin-right: 8px; } #summary { display: grid; grid-template-columns: repeat(5, 1fr); gap: 15px; } #summary div { background: #f9fafb; padding: 15px; border-radius: 8px; border: 1px solid #dfe1e6; transition: background-color 0.2s ease; } #summary div:hover { background-color: #f0f2f5; } #summary div { font-size: 14px; color: #555; } #summary span { display: block; font-size: 24px; font-weight: bold; margin-top: 5px; color: #333; } .stats-container { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin-top: 20px; } .server-card { background: #fff; border-radius: 10px; padding: 20px; border: 1px solid #dfe1e6; transition: transform 0.2s ease, box-shadow 0.2s ease; height: auto; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .server-card:hover { transform: translateY(-3px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); } .server-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; font-size: 16px; color: #444; } .server-name { display: flex; align-items: center; gap: 10px; } .server-flag { width: 20px; height: 20px; border-radius: 4px; } .metric-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-top: 15px; } .metric-item { background: #f9fafb; padding: 12px; border-radius: 8px; border: 1px solid #dfe1e6; transition: background-color 0.2s ease; } .metric-item:hover { background-color: #f0f2f5; } .metric-label { color: #777; font-size: 13px; margin-bottom: 5px; } .metric-value { font-size: 16px; font-weight: 500; color: #333; } .status-dot { display: inline-block; border-radius: 50%; animation: pulse 2s infinite; width: 12px; height: 12px; } .status-online { background-color: #2ecc71; color: #2ecc71; box-shadow: 0 0 5px rgba(46, 204, 113, 0.4); } .status-offline { background-color: #e74c3c; color: #e74c3c; box-shadow: 0 0 5px rgba(231, 76, 60, 0.4); } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(46, 204, 113, 0); } 100% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0); } } @media (max-width: 768px) { #summary { grid-template-columns: 1fr; } .stats-container { grid-template-columns: 1fr; } .metric-grid { grid-template-columns: 1fr; } } .progress-bar-container { width: 100%; background-color: #ddd; border-radius: 4px; margin-top: 4px; } .progress-bar { height: 8px; border-radius: 4px; background-color: #2e86de; width: 0%; } .cpu-progress-bar { height: 8px; border-radius: 4px; background-color: #2ecc71; width: 0%; } .memory-progress-bar{ height: 8px; border-radius: 4px; background-color: #e67e22; width: 0%; } """ htmlTemplate = f""" HF Space Monitor
系统概览
总实例数: 0
在线实例: 0
离线实例: 0
总上传: 0 B/s
总下载: 0 B/s
""" USERNAME = os.environ.get("USERNAME", "yangtb24") def fetch_instances(username): try: response = requests.get(f"https://huggingface.co./api/spaces?author={username}") response.raise_for_status() user_instances = response.json() return [{"id": instance["id"].split('/')[1], "owner": username} for instance in user_instances] except requests.exceptions.RequestException as e: print(f"Error fetching instances: {e}") return [] @app.route('/') def index(): return render_template_string(htmlTemplate, username=USERNAME) @app.route('/instances') def get_instances(): instances = fetch_instances(USERNAME) return jsonify(instances) @app.route('/metrics//') def stream_metrics(username, instance_id): url = f"https://api.hf.space/v1/{username}/{instance_id}/live-metrics/sse" def generate(): try: response = requests.get(url, stream=True, headers={"Accept": "text/event-stream"}, timeout=15) response.raise_for_status() buffer = "" for chunk in response.iter_content(chunk_size=1024, decode_unicode=True): if chunk: buffer += chunk while "\n\n" in buffer: event_data, buffer = buffer.split("\n\n", 1) lines = event_data.split("\n") event_type = "message" data_lines = [] for line in lines: if line.startswith("event:"): event_type = line.split(":", 1)[1].strip() elif line.startswith("data:"): data_lines.append(line.split(":", 1)[1].strip()) if event_type == "metric": yield f"event: {event_type}\ndata: {''.join(data_lines)}\n\n" except requests.exceptions.RequestException as e: print(f"Request Exception: {e}") yield f"event: error\ndata: Connection error: {e}\\n\\n" except Exception as e: print(f"An error occurred: {e}") yield f"event: error\ndata: An error occurred: {e}\\n\\n" return Response(generate(), mimetype='text/event-stream') if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=7860)