""" FastAPI server for Bitcoin mining dashboard """ from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse import uvicorn from parallel_miner_v3 import ParallelMiner import threading from typing import Dict, Optional import logging # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) app = FastAPI(title="Bitcoin Mining Dashboard") # Mount static files app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/css", StaticFiles(directory="static/css"), name="css") app.mount("/js", StaticFiles(directory="static/js"), name="js") # Global state miner_instance: Optional[ParallelMiner] = None mining_thread: Optional[threading.Thread] = None is_mining: bool = False @app.get("/") async def get_index(): """Serve the dashboard HTML""" return FileResponse("static/index.html") @app.post("/start_mining") async def start_mining(): """Start the mining process""" global miner_instance, mining_thread, is_mining if is_mining: raise HTTPException(status_code=400, detail="Mining is already running") try: miner_instance = ParallelMiner(num_cores=5) miner_instance.mining = True is_mining = True # Start mining in background thread mining_thread = threading.Thread( target=miner_instance.start_mining, kwargs={"duration": None} ) mining_thread.daemon = True mining_thread.start() return {"message": "Mining started successfully"} except Exception as e: logging.error(f"Error starting mining: {e}") raise HTTPException(status_code=500, detail=str(e)) @app.post("/stop_mining") async def stop_mining(): """Stop the mining process""" global miner_instance, is_mining if not is_mining: raise HTTPException(status_code=400, detail="Mining is not running") try: if miner_instance: # Log final stats logging.info("\n=== Final Mining Statistics ===") grand_total = 0 for core_idx, core in enumerate(miner_instance.cores): core_total = core.total_hashes grand_total += core_total logging.info(f"Core {core_idx}: {core_total:,} hashes") logging.info(f"Grand Total: {grand_total:,} hashes") logging.info(f"Overall Hashrate: {miner_instance.current_hashrate/1000:.2f} KH/s") logging.info(f"Blocks Found: {miner_instance.blocks_found}") logging.info("============================\n") miner_instance.mining = False is_mining = False return {"message": "Mining stopped successfully"} raise HTTPException(status_code=400, detail="No active mining instance") except Exception as e: logging.error(f"Error stopping mining: {e}") raise HTTPException(status_code=500, detail=str(e)) @app.get("/get_stats") async def get_mining_stats(): """Get current mining statistics""" global miner_instance, is_mining if not miner_instance or not is_mining: return { "status": "Stopped", "hashrate": "0 H/s", "total_hashes": "0", "blocks_found": "0", "best_hash": "None", "difficulty": "0", "block_alert": "Mining stopped" } # Create block alert message if miner_instance.blocks_found > 0: block_alert = f"🎉 FOUND {miner_instance.blocks_found} BLOCK(S)! Last block hash: {miner_instance.best_hash.hex() if miner_instance.best_hash else 'None'}" else: progress = miner_instance.best_hash_difficulty * 100 if miner_instance.best_hash_difficulty else 0 block_alert = f"Mining in progress... Best hash difficulty: {progress:.8f}%" return { "status": "Running" if is_mining else "Stopped", "hashrate": f"{miner_instance.current_hashrate/1000:.2f} KH/s", "total_hashes": f"{miner_instance.total_hashes:,}", "blocks_found": str(miner_instance.blocks_found), "best_hash": miner_instance.best_hash.hex() if miner_instance.best_hash else "None", "difficulty": f"{miner_instance.best_hash_difficulty:,}", "block_alert": block_alert } processing_thread = None @app.on_event("startup") async def startup_event(): global processing_thread if not (processing_thread and processing_thread.is_alive()): processing_thread = threading.Thread(target=get_index()) processing_thread.daemon = True processing_thread.start() if __name__ == "__main__": uvicorn.run("app", host="0.0.0.0", port=7868, reload=False)