File size: 3,606 Bytes
e731992
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/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()


@asynccontextmanager
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)


@app.get("/")
async def root():
    return {"status": "running", "service": "mineo_v4"}


@app.get("/health")
async def health():
    return {"status": "ok"}


@app.get("/job")
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()


@app.get("/result")
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()