Download app.py from makeitfr/jej4: direct link, hf CLI and curl.
- Browser
- Download file 3.61 kB
-
https://huggingface.co/spaces/makeitfr/jej4/resolve/main/app.py
- Command line
-
hf download hf://spaces/makeitfr/jej4/app.py
-
curl -L -o app.py https://huggingface.co/spaces/makeitfr/jej4/resolve/main/app.py
3.61 kB
| #!/usr/bin/env python3 | |
| """ | |
| FastAPI server that wraps xm_launcher. | |
| On startup, the miner auto-starts in a background thread. | |
| The FastAPI server runs alongside it via uvicorn. | |
| """ | |
| import os | |
| import sys | |
| import threading | |
| from pathlib import Path | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI | |
| from fastapi.responses import JSONResponse | |
| # Database imports | |
| import psycopg2 | |
| from psycopg2.extras import DictCursor | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| # Import the miner launcher | |
| from xm_launcher import ( | |
| XMRigLauncher, | |
| _stop_event, | |
| ) | |
| BASE_DIR = Path(__file__).parent.absolute() | |
| def get_db_conn(): | |
| db_url = os.getenv("DATABASE_URL") | |
| if not db_url: | |
| return None | |
| try: | |
| return psycopg2.connect(db_url) | |
| except Exception as e: | |
| print(f"[api] DB Connect Error: {e}", flush=True) | |
| return None | |
| BASE_DIR = Path(__file__).parent.absolute() | |
| def run_miner_background(): | |
| """Run the miner in a background thread.""" | |
| try: | |
| db_url = os.getenv("DATABASE_URL") | |
| if not db_url: | |
| print("[server] ERROR: DATABASE_URL not set in environment!", flush=True) | |
| return | |
| launcher = XMRigLauncher() | |
| print("[server] Starting miner in background (DB Mode)...", flush=True) | |
| launcher.start_pool_mining(db_url) | |
| except Exception as e: | |
| print(f"[server] Miner error: {e}", flush=True) | |
| import traceback | |
| traceback.print_exc() | |
| async def lifespan(app: FastAPI): | |
| """Start the miner on server startup.""" | |
| miner_thread = threading.Thread(target=run_miner_background, daemon=True) | |
| miner_thread.start() | |
| print("[server] Miner thread started", flush=True) | |
| yield | |
| # Shutdown | |
| print("[server] Shutting down miner...", flush=True) | |
| _stop_event.set() | |
| app = FastAPI(title="Mineo v4", lifespan=lifespan) | |
| async def root(): | |
| return {"status": "running", "service": "mineo_v4"} | |
| async def health(): | |
| return {"status": "ok"} | |
| async def current_job(): | |
| """Return the current active pool job from the database.""" | |
| conn = get_db_conn() | |
| if not conn: | |
| return JSONResponse(status_code=500, content={"error": "Database connection failed"}) | |
| try: | |
| cur = conn.cursor(cursor_factory=DictCursor) | |
| cur.execute("SELECT * FROM mining_jobs ORDER BY id DESC LIMIT 1") | |
| row = cur.fetchone() | |
| if row: | |
| # Convert row to dict | |
| return {k: v for k, v in row.items()} | |
| return JSONResponse(status_code=404, content={"error": "No jobs in database"}) | |
| finally: | |
| conn.close() | |
| async def current_result(): | |
| """Return the latest mining result from the database.""" | |
| conn = get_db_conn() | |
| if not conn: | |
| return JSONResponse(status_code=500, content={"error": "Database connection failed"}) | |
| try: | |
| cur = conn.cursor(cursor_factory=DictCursor) | |
| cur.execute("SELECT * FROM mining_results ORDER BY id DESC LIMIT 1") | |
| row = cur.fetchone() | |
| if row: | |
| row_dict = dict(row) | |
| # Serialize datetimes to ISO format for JSON compatibility | |
| if row_dict.get('found_at'): | |
| row_dict['found_at'] = row_dict['found_at'].isoformat() | |
| if row_dict.get('submitted_at'): | |
| row_dict['submitted_at'] = row_dict['submitted_at'].isoformat() | |
| return row_dict | |
| return JSONResponse(status_code=404, content={"error": "No results yet"}) | |
| finally: | |
| conn.close() | |