| |
| """ |
| Live status dashboard for Alpha MC — serves on port 7860. |
| Reads /tmp/mc_status.json and log files to show real-time info. |
| """ |
|
|
| import http.server |
| import json |
| import os |
| import subprocess |
| import time |
| from datetime import datetime |
|
|
| STATUS_FILE = "/tmp/mc_status.json" |
| MC_LOG = "/data/logs/minecraft.log" |
| PLAYIT_LOG = "/data/playit_gg/playit.log" |
| MONITOR_LOG = "/data/logs/monitor.log" |
|
|
| def read_status(): |
| try: |
| with open(STATUS_FILE) as f: |
| return json.load(f) |
| except Exception: |
| return { |
| "mc_online": False, |
| "playit_online": False, |
| "tunnel_address": "", |
| "started_at": "", |
| "uptime": "unknown" |
| } |
|
|
| def tail_file(path, lines=30): |
| try: |
| result = subprocess.run( |
| ["tail", "-n", str(lines), path], |
| capture_output=True, text=True, timeout=3 |
| ) |
| return result.stdout.strip() or "(no logs yet)" |
| except Exception: |
| return "(log unavailable)" |
|
|
| def get_system_stats(): |
| try: |
| mem = subprocess.run(["free", "-h"], capture_output=True, text=True).stdout |
| mem_line = [l for l in mem.splitlines() if l.startswith("Mem:")][0].split() |
| mem_used, mem_total = mem_line[2], mem_line[1] |
| except Exception: |
| mem_used, mem_total = "?", "?" |
|
|
| try: |
| disk = subprocess.run(["df", "-h", "/data"], capture_output=True, text=True).stdout |
| disk_line = disk.splitlines()[1].split() |
| disk_used, disk_total, disk_pct = disk_line[2], disk_line[1], disk_line[4] |
| except Exception: |
| disk_used, disk_total, disk_pct = "?", "?", "?" |
|
|
| try: |
| load = subprocess.run(["uptime"], capture_output=True, text=True).stdout |
| load = load.split("load average:")[-1].strip() |
| except Exception: |
| load = "?" |
|
|
| return { |
| "mem_used": mem_used, "mem_total": mem_total, |
| "disk_used": disk_used, "disk_total": disk_total, "disk_pct": disk_pct, |
| "load": load |
| } |
|
|
| def get_tunnel_address(): |
| """Try to extract the live tunnel address from playit log.""" |
| try: |
| result = subprocess.run( |
| ["grep", "-oE", r"[a-z0-9-]+\.playit\.gg(:[0-9]+)?", PLAYIT_LOG], |
| capture_output=True, text=True, timeout=3 |
| ) |
| addr = result.stdout.strip().splitlines() |
| return addr[0] if addr else "" |
| except Exception: |
| return "" |
|
|
| def render_html(): |
| status = read_status() |
| stats = get_system_stats() |
| tunnel = get_tunnel_address() or status.get("tunnel_address", "") |
| mc_log = tail_file(MC_LOG, 40) |
| now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|
|
| mc_status_color = "#4ade80" if status.get("mc_online") else "#f87171" |
| mc_status_text = "🟢 Online" if status.get("mc_online") else "🔴 Offline" |
| tunnel_color = "#4ade80" if tunnel else "#facc15" |
| tunnel_text = tunnel if tunnel else "Waiting for tunnel..." |
|
|
| return f"""<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <meta http-equiv="refresh" content="15"> |
| <title>⛏️ Alpha MC — Status</title> |
| <style> |
| * {{ box-sizing: border-box; margin: 0; padding: 0; }} |
| body {{ |
| background: #0f1117; |
| color: #e2e8f0; |
| font-family: 'Segoe UI', system-ui, sans-serif; |
| padding: 24px; |
| min-height: 100vh; |
| }} |
| h1 {{ font-size: 1.8rem; color: #4ade80; margin-bottom: 4px; }} |
| .subtitle {{ color: #64748b; font-size: 0.9rem; margin-bottom: 24px; }} |
| .grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-bottom: 24px; }} |
| .card {{ |
| background: #1e2333; |
| border-radius: 12px; |
| padding: 20px; |
| border: 1px solid #2d3748; |
| }} |
| .card-label {{ color: #64748b; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 6px; }} |
| .card-value {{ font-size: 1.2rem; font-weight: 600; }} |
| .tunnel-box {{ |
| background: #1e2333; |
| border-radius: 12px; |
| padding: 20px; |
| border: 1px solid #2d3748; |
| margin-bottom: 24px; |
| }} |
| .tunnel-addr {{ |
| font-family: monospace; |
| font-size: 1.4rem; |
| color: {tunnel_color}; |
| word-break: break-all; |
| margin-top: 8px; |
| }} |
| .log-box {{ |
| background: #0a0c10; |
| border-radius: 12px; |
| padding: 20px; |
| border: 1px solid #2d3748; |
| }} |
| .log-box h2 {{ color: #94a3b8; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 12px; }} |
| pre {{ |
| font-family: 'Cascadia Code', 'Fira Code', monospace; |
| font-size: 0.78rem; |
| color: #94a3b8; |
| white-space: pre-wrap; |
| word-break: break-word; |
| line-height: 1.5; |
| max-height: 400px; |
| overflow-y: auto; |
| }} |
| .badge {{ |
| display: inline-block; |
| background: #2d3748; |
| border-radius: 6px; |
| padding: 2px 10px; |
| font-size: 0.8rem; |
| margin-left: 8px; |
| vertical-align: middle; |
| }} |
| .footer {{ color: #334155; font-size: 0.75rem; margin-top: 20px; text-align: center; }} |
| </style> |
| </head> |
| <body> |
| <h1>⛏️ Alpha MC</h1> |
| <p class="subtitle">Minecraft Bedrock Server · Auto-refreshes every 15s · Last updated: {now}</p> |
| |
| <div class="grid"> |
| <div class="card"> |
| <div class="card-label">Server Status</div> |
| <div class="card-value" style="color:{mc_status_color}">{mc_status_text}</div> |
| </div> |
| <div class="card"> |
| <div class="card-label">RAM Usage</div> |
| <div class="card-value">{stats['mem_used']} <span style="color:#64748b;font-size:0.9rem">/ {stats['mem_total']}</span></div> |
| </div> |
| <div class="card"> |
| <div class="card-label">Disk Usage</div> |
| <div class="card-value">{stats['disk_used']} <span style="color:#64748b;font-size:0.9rem">/ {stats['disk_total']} ({stats['disk_pct']})</span></div> |
| </div> |
| <div class="card"> |
| <div class="card-label">CPU Load</div> |
| <div class="card-value">{stats['load']}</div> |
| </div> |
| </div> |
| |
| <div class="tunnel-box"> |
| <div class="card-label">🔗 Connect Address <span class="badge">Minecraft → Add Server → paste below</span></div> |
| <div class="tunnel-addr">{tunnel_text}</div> |
| </div> |
| |
| <div class="log-box"> |
| <h2>📋 Minecraft Server Log (last 40 lines)</h2> |
| <pre>{mc_log}</pre> |
| </div> |
| |
| <p class="footer">Alpha MC · Powered by HuggingFace Spaces + playit.gg · Page auto-refreshes every 15 seconds</p> |
| </body> |
| </html>""" |
|
|
|
|
| class StatusHandler(http.server.BaseHTTPRequestHandler): |
| def do_GET(self): |
| html = render_html() |
| self.send_response(200) |
| self.send_header("Content-Type", "text/html; charset=utf-8") |
| self.send_header("Content-Length", str(len(html.encode()))) |
| self.end_headers() |
| self.wfile.write(html.encode()) |
|
|
| def log_message(self, format, *args): |
| pass |
|
|
|
|
| if __name__ == "__main__": |
| server = http.server.HTTPServer(("0.0.0.0", 7860), StatusHandler) |
| print(f"[OK] Status dashboard running on http://0.0.0.0:7860") |
| server.serve_forever() |
|
|