Fred808 commited on
Commit
636293c
Β·
verified Β·
1 Parent(s): ac07881

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +294 -69
app.py CHANGED
@@ -1,14 +1,22 @@
1
  """
2
- FastAPI server for Bitcoin mining dashboard
3
  """
4
- from fastapi import FastAPI, HTTPException
5
  from fastapi.staticfiles import StaticFiles
6
- from fastapi.responses import FileResponse
 
7
  import uvicorn
8
- from parallel_miner_v3 import ParallelMiner
9
  import threading
10
- from typing import Dict, Optional
 
 
 
11
  import logging
 
 
 
 
 
12
 
13
  # Configure logging
14
  logging.basicConfig(
@@ -16,76 +24,206 @@ logging.basicConfig(
16
  format='%(asctime)s - %(levelname)s - %(message)s'
17
  )
18
 
19
- app = FastAPI(title="Bitcoin Mining Dashboard")
 
 
 
 
 
 
 
 
 
20
 
21
  # Mount static files
22
  app.mount("/static", StaticFiles(directory="static"), name="static")
23
- app.mount("/css", StaticFiles(directory="static/css"), name="css")
24
- app.mount("/js", StaticFiles(directory="static/js"), name="js")
25
 
26
- # Global state
27
- miner_instance: Optional[ParallelMiner] = None
28
- mining_thread: Optional[threading.Thread] = None
29
- is_mining: bool = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- @app.get("/")
32
  async def get_index():
33
  """Serve the dashboard HTML"""
34
- return FileResponse("static/index.html")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  @app.post("/start_mining")
37
- async def start_mining():
38
  """Start the mining process"""
39
- global miner_instance, mining_thread, is_mining
40
 
41
- if is_mining:
42
  raise HTTPException(status_code=400, detail="Mining is already running")
43
 
44
  try:
45
- miner_instance = ParallelMiner()
46
- miner_instance.mining = True
47
- is_mining = True
48
 
49
  # Start mining in background thread
50
- mining_thread = threading.Thread(
51
- target=miner_instance.start_mining,
52
- kwargs={"duration": None}
53
  )
54
- mining_thread.daemon = True
55
- mining_thread.start()
 
 
 
56
 
57
- return {"message": "Mining started successfully"}
58
  except Exception as e:
 
59
  logging.error(f"Error starting mining: {e}")
60
  raise HTTPException(status_code=500, detail=str(e))
61
 
62
  @app.post("/stop_mining")
63
  async def stop_mining():
64
  """Stop the mining process"""
65
- global miner_instance, is_mining
66
 
67
- if not is_mining:
68
  raise HTTPException(status_code=400, detail="Mining is not running")
69
 
70
  try:
71
- if miner_instance:
72
- # Log final stats
73
- logging.info("\n=== Final Mining Statistics ===")
74
- grand_total = 0
75
- for core_idx, core in enumerate(miner_instance.cores):
76
- core_total = core.total_hashes
77
- grand_total += core_total
78
- logging.info(f"Core {core_idx}: {core_total:,} hashes")
79
- logging.info(f"Grand Total: {grand_total:,} hashes")
80
- logging.info(f"Overall Hashrate: {miner_instance.current_hashrate/1000:.2f} KH/s")
81
- logging.info(f"Blocks Found: {miner_instance.blocks_found}")
82
- logging.info("============================\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
- miner_instance.mining = False
85
- is_mining = False
86
- return {"message": "Mining stopped successfully"}
87
 
88
  raise HTTPException(status_code=400, detail="No active mining instance")
 
89
  except Exception as e:
90
  logging.error(f"Error stopping mining: {e}")
91
  raise HTTPException(status_code=500, detail=str(e))
@@ -93,45 +231,132 @@ async def stop_mining():
93
  @app.get("/get_stats")
94
  async def get_mining_stats():
95
  """Get current mining statistics"""
96
- global miner_instance, is_mining
 
 
 
97
 
98
- if not miner_instance or not is_mining:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  return {
100
- "status": "Stopped",
101
  "hashrate": "0 H/s",
102
  "total_hashes": "0",
103
  "blocks_found": "0",
104
- "best_hash": "None",
105
  "difficulty": "0",
106
- "block_alert": "Mining stopped"
 
 
107
  }
 
 
 
 
 
108
 
109
- # Create block alert message
110
- if miner_instance.blocks_found > 0:
111
- 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'}"
112
- else:
113
- progress = miner_instance.best_hash_difficulty * 100 if miner_instance.best_hash_difficulty else 0
114
- block_alert = f"Mining in progress... Best hash difficulty: {progress:.8f}%"
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  return {
117
- "status": "Running" if is_mining else "Stopped",
118
- "hashrate": f"{miner_instance.current_hashrate/1000:.2f} KH/s",
119
- "total_hashes": f"{miner_instance.total_hashes:,}",
120
- "blocks_found": str(miner_instance.blocks_found),
121
- "best_hash": miner_instance.best_hash.hex() if miner_instance.best_hash else "None",
122
- "difficulty": f"{miner_instance.best_hash_difficulty:,}",
123
- "block_alert": block_alert
124
  }
125
 
126
- processing_thread = None
127
-
128
  @app.on_event("startup")
129
  async def startup_event():
130
- global processing_thread
131
- if not (processing_thread and processing_thread.is_alive()):
132
- processing_thread = threading.Thread(target=get_index())
133
- processing_thread.daemon = True
134
- processing_thread.start()
 
 
 
 
 
 
135
 
136
  if __name__ == "__main__":
137
- uvicorn.run("app", host="0.0.0.0", port=7868, reload=False)
 
 
 
 
 
 
 
1
  """
2
+ FastAPI server for Bitcoin mining dashboard - Complete Integration
3
  """
4
+ from fastapi import FastAPI, HTTPException, BackgroundTasks
5
  from fastapi.staticfiles import StaticFiles
6
+ from fastapi.responses import FileResponse, HTMLResponse
7
+ from fastapi.middleware.cors import CORSMiddleware
8
  import uvicorn
 
9
  import threading
10
+ import time
11
+ import json
12
+ import asyncio
13
+ from typing import Dict, Optional, List
14
  import logging
15
+ import sys
16
+ import os
17
+
18
+ # Add the current directory to Python path to import your miner
19
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
20
 
21
  # Configure logging
22
  logging.basicConfig(
 
24
  format='%(asctime)s - %(levelname)s - %(message)s'
25
  )
26
 
27
+ app = FastAPI(title="Bitcoin Mining Dashboard", version="1.0.0")
28
+
29
+ # Add CORS middleware
30
+ app.add_middleware(
31
+ CORSMiddleware,
32
+ allow_origins=["*"],
33
+ allow_credentials=True,
34
+ allow_methods=["*"],
35
+ allow_headers=["*"],
36
+ )
37
 
38
  # Mount static files
39
  app.mount("/static", StaticFiles(directory="static"), name="static")
 
 
40
 
41
+ # Global mining state
42
+ class MiningState:
43
+ def __init__(self):
44
+ self.is_mining = False
45
+ self.miner_instance = None
46
+ self.mining_thread = None
47
+ self.stats = {
48
+ "status": "Stopped",
49
+ "hashrate": "0 H/s",
50
+ "total_hashes": "0",
51
+ "blocks_found": "0",
52
+ "best_hash": "None",
53
+ "difficulty": "0",
54
+ "network_difficulty": "0",
55
+ "block_alert": "Ready to start mining",
56
+ "mining_time": "0s",
57
+ "cores_active": "0",
58
+ "wallet_address": "1Ks4WtCEK96BaBF7HSuCGt3rEpVKPqcJKf"
59
+ }
60
+ self.start_time = None
61
+
62
+ mining_state = MiningState()
63
 
64
+ @app.get("/", response_class=HTMLResponse)
65
  async def get_index():
66
  """Serve the dashboard HTML"""
67
+ try:
68
+ return FileResponse("static/index.html")
69
+ except:
70
+ # Fallback minimal HTML
71
+ return """
72
+ <!DOCTYPE html>
73
+ <html>
74
+ <head>
75
+ <title>Bitcoin Mining Dashboard</title>
76
+ <style>
77
+ body { font-family: Arial, sans-serif; margin: 40px; background: #f0f0f0; }
78
+ .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; }
79
+ .stats { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; margin: 10px 0; }
80
+ .alert { background: #e74c3c; color: white; padding: 15px; border-radius: 5px; margin: 10px 0; }
81
+ .success { background: #27ae60; }
82
+ .button { background: #3498db; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; margin: 5px; }
83
+ .button:disabled { background: #95a5a6; }
84
+ </style>
85
+ </head>
86
+ <body>
87
+ <div class="container">
88
+ <h1>⛏️ Bitcoin Mining Dashboard</h1>
89
+ <div id="blockAlert" class="alert">Ready to start mining</div>
90
+ <div class="stats">
91
+ <h3>Mining Statistics</h3>
92
+ <div id="statsDisplay"></div>
93
+ </div>
94
+ <button class="button" onclick="startMining()" id="startBtn">Start Mining</button>
95
+ <button class="button" onclick="stopMining()" id="stopBtn" disabled>Stop Mining</button>
96
+ <script>
97
+ function updateStats(stats) {
98
+ document.getElementById('statsDisplay').innerHTML = `
99
+ <p><strong>Status:</strong> ${stats.status}</p>
100
+ <p><strong>Hash Rate:</strong> ${stats.hashrate}</p>
101
+ <p><strong>Total Hashes:</strong> ${stats.total_hashes}</p>
102
+ <p><strong>Blocks Found:</strong> ${stats.blocks_found}</p>
103
+ <p><strong>Best Hash:</strong> ${stats.best_hash}</p>
104
+ <p><strong>Mining Time:</strong> ${stats.mining_time}</p>
105
+ <p><strong>Wallet:</strong> ${stats.wallet_address}</p>
106
+ `;
107
+ document.getElementById('blockAlert').textContent = stats.block_alert;
108
+ document.getElementById('blockAlert').className = stats.blocks_found > 0 ? 'alert success' : 'alert';
109
+
110
+ document.getElementById('startBtn').disabled = stats.status === 'Running';
111
+ document.getElementById('stopBtn').disabled = stats.status !== 'Running';
112
+ }
113
+
114
+ async function startMining() {
115
+ const response = await fetch('/start_mining', { method: 'POST' });
116
+ const result = await response.json();
117
+ alert(result.message);
118
+ }
119
+
120
+ async function stopMining() {
121
+ const response = await fetch('/stop_mining', { method: 'POST' });
122
+ const result = await response.json();
123
+ alert(result.message);
124
+ }
125
+
126
+ // Poll for stats updates
127
+ setInterval(async () => {
128
+ const response = await fetch('/get_stats');
129
+ const stats = await response.json();
130
+ updateStats(stats);
131
+ }, 1000);
132
+ </script>
133
+ </div>
134
+ </body>
135
+ </html>
136
+ """
137
+
138
+ def mining_worker(duration=None):
139
+ """Worker function to run mining in background thread"""
140
+ try:
141
+ # Import and initialize the miner
142
+ from parallel_miner_v3 import ParallelMiner
143
+
144
+ mining_state.miner_instance = ParallelMiner(
145
+ num_cores=7,
146
+ wallet_address="1Ks4WtCEK96BaBF7HSuCGt3rEpVKPqcJKf"
147
+ )
148
+ mining_state.start_time = time.time()
149
+
150
+ # Start mining
151
+ mining_state.miner_instance.start_mining(duration=duration)
152
+
153
+ except Exception as e:
154
+ logging.error(f"Mining worker error: {e}")
155
+ finally:
156
+ mining_state.is_mining = False
157
+ mining_state.mining_thread = None
158
 
159
  @app.post("/start_mining")
160
+ async def start_mining(background_tasks: BackgroundTasks):
161
  """Start the mining process"""
162
+ global mining_state
163
 
164
+ if mining_state.is_mining:
165
  raise HTTPException(status_code=400, detail="Mining is already running")
166
 
167
  try:
168
+ mining_state.is_mining = True
169
+ mining_state.stats["status"] = "Starting..."
170
+ mining_state.stats["block_alert"] = "πŸ”„ Starting mining process..."
171
 
172
  # Start mining in background thread
173
+ mining_state.mining_thread = threading.Thread(
174
+ target=mining_worker,
175
+ kwargs={"duration": None} # Mine indefinitely
176
  )
177
+ mining_state.mining_thread.daemon = True
178
+ mining_state.mining_thread.start()
179
+
180
+ logging.info("βœ… Mining started via API")
181
+ return {"message": "Mining started successfully", "status": "started"}
182
 
 
183
  except Exception as e:
184
+ mining_state.is_mining = False
185
  logging.error(f"Error starting mining: {e}")
186
  raise HTTPException(status_code=500, detail=str(e))
187
 
188
  @app.post("/stop_mining")
189
  async def stop_mining():
190
  """Stop the mining process"""
191
+ global mining_state
192
 
193
+ if not mining_state.is_mining:
194
  raise HTTPException(status_code=400, detail="Mining is not running")
195
 
196
  try:
197
+ if mining_state.miner_instance:
198
+ mining_state.miner_instance.mining = False
199
+ mining_state.is_mining = False
200
+
201
+ # Log final statistics
202
+ if hasattr(mining_state.miner_instance, 'total_hashes'):
203
+ total_hashes = mining_state.miner_instance.total_hashes
204
+ blocks_found = mining_state.miner_instance.blocks_found
205
+ mining_time = time.time() - mining_state.start_time
206
+
207
+ logging.info("\n" + "="*50)
208
+ logging.info("⛏️ FINAL MINING STATISTICS")
209
+ logging.info("="*50)
210
+ logging.info(f"⏱️ Total mining time: {mining_time:.2f} seconds")
211
+ logging.info(f"πŸ”’ Total hashes: {total_hashes:,}")
212
+ logging.info(f"πŸ’° Blocks found: {blocks_found}")
213
+ logging.info(f"⚑ Average hash rate: {total_hashes/max(mining_time,1)/1000:.2f} KH/s")
214
+
215
+ if hasattr(mining_state.miner_instance, 'cores'):
216
+ for core_idx, core in enumerate(mining_state.miner_instance.cores):
217
+ logging.info(f"πŸ”© Core {core_idx}: {core.total_hashes:,} hashes")
218
+ logging.info("="*50)
219
+
220
+ mining_state.stats["status"] = "Stopped"
221
+ mining_state.stats["block_alert"] = "πŸ›‘ Mining stopped"
222
 
223
+ return {"message": "Mining stopped successfully", "status": "stopped"}
 
 
224
 
225
  raise HTTPException(status_code=400, detail="No active mining instance")
226
+
227
  except Exception as e:
228
  logging.error(f"Error stopping mining: {e}")
229
  raise HTTPException(status_code=500, detail=str(e))
 
231
  @app.get("/get_stats")
232
  async def get_mining_stats():
233
  """Get current mining statistics"""
234
+ global mining_state
235
+
236
+ if not mining_state.is_mining or not mining_state.miner_instance:
237
+ return mining_state.stats
238
 
239
+ try:
240
+ miner = mining_state.miner_instance
241
+
242
+ # Calculate mining time
243
+ mining_time = time.time() - mining_state.start_time
244
+ time_str = f"{int(mining_time//3600)}h {int((mining_time%3600)//60)}m {int(mining_time%60)}s"
245
+
246
+ # Create block alert message
247
+ if miner.blocks_found > 0:
248
+ block_alert = f"πŸŽ‰ FOUND {miner.blocks_found} BLOCK(S)! πŸŽ‰"
249
+ if miner.best_hash:
250
+ block_alert += f" Hash: {miner.best_hash.hex()[:16]}..."
251
+ else:
252
+ progress = (miner.best_hash_difficulty / max(miner.network_difficulty, 1)) * 100 if miner.best_hash_difficulty else 0
253
+ block_alert = f"⛏️ Mining... Progress: {progress:.8f}%"
254
+
255
+ # Update stats
256
+ mining_state.stats.update({
257
+ "status": "Running",
258
+ "hashrate": f"{miner.current_hashrate/1000:.2f} KH/s",
259
+ "total_hashes": f"{miner.total_hashes:,}",
260
+ "blocks_found": str(miner.blocks_found),
261
+ "best_hash": miner.best_hash.hex()[:32] + "..." if miner.best_hash else "None",
262
+ "difficulty": f"{miner.best_hash_difficulty:.8f}" if miner.best_hash_difficulty else "0",
263
+ "network_difficulty": f"{miner.network_difficulty:,.2f}" if hasattr(miner, 'network_difficulty') else "0",
264
+ "block_alert": block_alert,
265
+ "mining_time": time_str,
266
+ "cores_active": f"{len(miner.cores)}" if hasattr(miner, 'cores') else "0"
267
+ })
268
+
269
+ return mining_state.stats
270
+
271
+ except Exception as e:
272
+ logging.error(f"Error getting mining stats: {e}")
273
  return {
274
+ "status": "Error",
275
  "hashrate": "0 H/s",
276
  "total_hashes": "0",
277
  "blocks_found": "0",
278
+ "best_hash": "Error",
279
  "difficulty": "0",
280
+ "block_alert": f"Error: {str(e)}",
281
+ "mining_time": "0s",
282
+ "cores_active": "0"
283
  }
284
+
285
+ @app.get("/get_detailed_stats")
286
+ async def get_detailed_stats():
287
+ """Get detailed mining statistics including per-core info"""
288
+ global mining_state
289
 
290
+ if not mining_state.is_mining or not mining_state.miner_instance:
291
+ return {"error": "Mining not active"}
 
 
 
 
292
 
293
+ try:
294
+ miner = mining_state.miner_instance
295
+ detailed_stats = {
296
+ "overview": {
297
+ "status": "Running",
298
+ "total_hashes": miner.total_hashes,
299
+ "blocks_found": miner.blocks_found,
300
+ "current_hashrate": miner.current_hashrate,
301
+ "best_hash_difficulty": miner.best_hash_difficulty,
302
+ "network_difficulty": miner.network_difficulty,
303
+ "mining_time": time.time() - mining_state.start_time
304
+ },
305
+ "cores": []
306
+ }
307
+
308
+ if hasattr(miner, 'cores'):
309
+ for core in miner.cores:
310
+ core_info = {
311
+ "core_id": core.core_id,
312
+ "total_hashes": core.total_hashes,
313
+ "blocks_found": core.blocks_found,
314
+ "units": []
315
+ }
316
+
317
+ for unit in core.units:
318
+ core_info["units"].append({
319
+ "unit_id": unit.unit_id,
320
+ "total_hashes": unit.total_hashes,
321
+ "blocks_found": unit.blocks_found
322
+ })
323
+
324
+ detailed_stats["cores"].append(core_info)
325
+
326
+ return detailed_stats
327
+
328
+ except Exception as e:
329
+ return {"error": str(e)}
330
+
331
+ @app.get("/health")
332
+ async def health_check():
333
+ """Health check endpoint"""
334
  return {
335
+ "status": "healthy",
336
+ "mining_active": mining_state.is_mining,
337
+ "timestamp": time.time()
 
 
 
 
338
  }
339
 
340
+ # Background task to periodically update stats
 
341
  @app.on_event("startup")
342
  async def startup_event():
343
+ """Initialize on startup"""
344
+ logging.info("πŸš€ Bitcoin Mining Dashboard starting up...")
345
+
346
+ @app.on_event("shutdown")
347
+ async def shutdown_event():
348
+ """Cleanup on shutdown"""
349
+ global mining_state
350
+ if mining_state.is_mining and mining_state.miner_instance:
351
+ mining_state.miner_instance.mining = False
352
+ mining_state.is_mining = False
353
+ logging.info("πŸ›‘ Bitcoin Mining Dashboard shutting down...")
354
 
355
  if __name__ == "__main__":
356
+ uvicorn.run(
357
+ "app:app",
358
+ host="0.0.0.0",
359
+ port=7868,
360
+ reload=False,
361
+ log_level="info"
362
+ )