favoredone commited on
Commit
9aad16f
·
verified ·
1 Parent(s): 995bdb1

Upload 10 files

Browse files
Files changed (5) hide show
  1. backend.py +112 -0
  2. requirements.txt +8 -0
  3. static/css/style.css +141 -0
  4. static/index.html +58 -0
  5. static/js/main.js +142 -0
backend.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
+ from parallel_miner_v3 import ParallelMiner
5
+ import threading
6
+ from typing import Dict, Optional
7
+ import uvicorn
8
+ import logging
9
+
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+ app = FastAPI(title="Mining Dashboard API")
15
+
16
+ # Enable CORS
17
+ app.add_middleware(
18
+ CORSMiddleware,
19
+ allow_origins=["*"], # In production, replace with your frontend URL
20
+ allow_credentials=True,
21
+ allow_methods=["*"],
22
+ allow_headers=["*"],
23
+ )
24
+
25
+ # Global variables for mining state
26
+ miner_instance: Optional[ParallelMiner] = None
27
+ mining_thread: Optional[threading.Thread] = None
28
+ is_mining = False
29
+
30
+ @app.get("/status") # Remove /api prefix since we're mounting at /api
31
+ async def get_status() -> Dict:
32
+ """Get current mining status and statistics"""
33
+ global miner_instance, is_mining
34
+
35
+ logger.info("Status endpoint called")
36
+ try:
37
+ if not miner_instance:
38
+ logger.info("No miner instance found, returning default values")
39
+ return {
40
+ "status": "Stopped",
41
+ "hashrate": 0,
42
+ "total_hashes": 0,
43
+ "blocks_found": 0,
44
+ "best_hash": None,
45
+ "difficulty": 0
46
+ }
47
+
48
+ stats = {
49
+ "status": "Running" if is_mining else "Stopped",
50
+ "hashrate": round(miner_instance.current_hashrate / 1000, 2), # KH/s
51
+ "total_hashes": miner_instance.total_hashes,
52
+ "blocks_found": miner_instance.blocks_found,
53
+ "best_hash": miner_instance.best_hash.hex() if miner_instance.best_hash else None,
54
+ "difficulty": miner_instance.best_hash_difficulty
55
+ }
56
+ logger.info(f"Returning stats: {stats}")
57
+ return stats
58
+ except Exception as e:
59
+ logger.error(f"Error getting status: {str(e)}")
60
+ raise HTTPException(status_code=500, detail=str(e))
61
+
62
+ @app.post("/start") # Remove /api prefix since we're mounting at /api
63
+ async def start_mining() -> Dict:
64
+ """Start the mining process"""
65
+ global miner_instance, mining_thread, is_mining
66
+
67
+ logger.info("Start mining endpoint called")
68
+
69
+ if is_mining:
70
+ logger.warning("Mining is already running")
71
+ raise HTTPException(status_code=400, detail="Mining is already running")
72
+
73
+ try:
74
+ logger.info("Initializing miner...")
75
+ miner_instance = ParallelMiner(num_cores=5)
76
+ miner_instance.mining = True
77
+ is_mining = True
78
+
79
+ # Start mining in background thread
80
+ logger.info("Starting mining thread...")
81
+ mining_thread = threading.Thread(
82
+ target=miner_instance.start_mining,
83
+ kwargs={"duration": None}
84
+ )
85
+ mining_thread.daemon = True
86
+ mining_thread.start()
87
+
88
+ logger.info("Mining started successfully")
89
+ return {"message": "Mining started successfully"}
90
+ except Exception as e:
91
+ logger.error(f"Error starting mining: {str(e)}")
92
+ raise HTTPException(status_code=500, detail=str(e))
93
+
94
+ @app.post("/api/stop")
95
+ async def stop_mining() -> Dict:
96
+ """Stop the mining process"""
97
+ global miner_instance, is_mining
98
+
99
+ if not is_mining:
100
+ raise HTTPException(status_code=400, detail="Mining is not running")
101
+
102
+ try:
103
+ if miner_instance:
104
+ miner_instance.mining = False
105
+ is_mining = False
106
+ return {"message": "Mining stopped successfully"}
107
+ raise HTTPException(status_code=400, detail="No active mining instance found")
108
+ except Exception as e:
109
+ raise HTTPException(status_code=500, detail=str(e))
110
+
111
+ if __name__ == "__main__":
112
+ uvicorn.run(app, host="0.0.0.0", port=8000)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ transformers>=4.30.0
2
+ torch>=2.0.0
3
+ pillow>=9.0.0
4
+ numpy>=1.24.0
5
+ fastapi
6
+ plotly
7
+ tqdm
8
+ accelerate
static/css/style.css ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #2ecc71;
3
+ --secondary-color: #3498db;
4
+ --background-color: #1a1a1a;
5
+ --card-background: #2d2d2d;
6
+ --text-color: #ffffff;
7
+ --danger-color: #e74c3c;
8
+ }
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
18
+ background-color: var(--background-color);
19
+ color: var(--text-color);
20
+ line-height: 1.6;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1200px;
25
+ margin: 0 auto;
26
+ padding: 20px;
27
+ }
28
+
29
+ header {
30
+ display: flex;
31
+ justify-content: space-between;
32
+ align-items: center;
33
+ margin-bottom: 30px;
34
+ }
35
+
36
+ h1 {
37
+ font-size: 2.5rem;
38
+ font-weight: 700;
39
+ }
40
+
41
+ .controls {
42
+ display: flex;
43
+ gap: 10px;
44
+ }
45
+
46
+ .btn {
47
+ padding: 12px 24px;
48
+ border: none;
49
+ border-radius: 8px;
50
+ font-size: 1rem;
51
+ font-weight: 600;
52
+ cursor: pointer;
53
+ transition: all 0.3s ease;
54
+ }
55
+
56
+ .btn.primary {
57
+ background-color: var(--primary-color);
58
+ color: white;
59
+ }
60
+
61
+ .btn.secondary {
62
+ background-color: var(--secondary-color);
63
+ color: white;
64
+ }
65
+
66
+ .btn:hover {
67
+ transform: translateY(-2px);
68
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
69
+ }
70
+
71
+ .alert-box {
72
+ background-color: var(--card-background);
73
+ padding: 20px;
74
+ border-radius: 10px;
75
+ margin-bottom: 30px;
76
+ text-align: center;
77
+ font-size: 1.2rem;
78
+ font-weight: 600;
79
+ border-left: 5px solid var(--primary-color);
80
+ }
81
+
82
+ .alert-box.success {
83
+ border-left-color: var(--primary-color);
84
+ }
85
+
86
+ .stats-grid {
87
+ display: grid;
88
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
89
+ gap: 20px;
90
+ margin-bottom: 30px;
91
+ }
92
+
93
+ .stat-card {
94
+ background-color: var(--card-background);
95
+ padding: 20px;
96
+ border-radius: 10px;
97
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
98
+ }
99
+
100
+ .stat-card h3 {
101
+ font-size: 1.1rem;
102
+ color: #888;
103
+ margin-bottom: 10px;
104
+ }
105
+
106
+ .stat-card p {
107
+ font-size: 1.4rem;
108
+ font-weight: 600;
109
+ }
110
+
111
+ .hash {
112
+ font-family: monospace;
113
+ font-size: 1rem !important;
114
+ word-break: break-all;
115
+ }
116
+
117
+ .charts {
118
+ display: grid;
119
+ grid-template-columns: 1fr;
120
+ gap: 20px;
121
+ }
122
+
123
+ #hashrateChart,
124
+ #totalHashesChart {
125
+ background-color: var(--card-background);
126
+ border-radius: 10px;
127
+ padding: 20px;
128
+ height: 400px;
129
+ }
130
+
131
+ @media (max-width: 768px) {
132
+ header {
133
+ flex-direction: column;
134
+ gap: 20px;
135
+ text-align: center;
136
+ }
137
+
138
+ .stats-grid {
139
+ grid-template-columns: 1fr;
140
+ }
141
+ }
static/index.html ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Bitcoin Mining Dashboard</title>
7
+ <link rel="stylesheet" href="css/style.css">
8
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
9
+ </head>
10
+ <body>
11
+ <div class="container">
12
+ <header>
13
+ <h1>⛏️ Bitcoin Mining Dashboard</h1>
14
+ <div class="controls">
15
+ <button id="startMining" class="btn primary">▶️ Start Mining</button>
16
+ <button id="stopMining" class="btn secondary">⏹️ Stop Mining</button>
17
+ </div>
18
+ </header>
19
+
20
+ <div class="alert-box" id="blockAlert">
21
+ No blocks found yet
22
+ </div>
23
+
24
+ <div class="stats-grid">
25
+ <div class="stat-card">
26
+ <h3>Status</h3>
27
+ <p id="status">Stopped</p>
28
+ </div>
29
+ <div class="stat-card">
30
+ <h3>Hashrate</h3>
31
+ <p id="hashrate">0 H/s</p>
32
+ </div>
33
+ <div class="stat-card">
34
+ <h3>Total Hashes</h3>
35
+ <p id="totalHashes">0</p>
36
+ </div>
37
+ <div class="stat-card">
38
+ <h3>Blocks Found</h3>
39
+ <p id="blocksFound">0</p>
40
+ </div>
41
+ <div class="stat-card">
42
+ <h3>Best Hash</h3>
43
+ <p id="bestHash" class="hash">None</p>
44
+ </div>
45
+ <div class="stat-card">
46
+ <h3>Best Difficulty</h3>
47
+ <p id="difficulty">0</p>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="charts">
52
+ <div id="hashrateChart"></div>
53
+ <div id="totalHashesChart"></div>
54
+ </div>
55
+ </div>
56
+ <script src="js/main.js"></script>
57
+ </body>
58
+ </html>
static/js/main.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class MiningDashboard {
2
+ constructor() {
3
+ this.statsHistory = {
4
+ timestamps: [],
5
+ hashrates: [],
6
+ totalHashes: [],
7
+ blocksFound: []
8
+ };
9
+ this.setupEventListeners();
10
+ this.setupCharts();
11
+ this.startAutoRefresh();
12
+ }
13
+
14
+ setupEventListeners() {
15
+ document.getElementById('startMining').addEventListener('click', () => this.startMining());
16
+ document.getElementById('stopMining').addEventListener('click', () => this.stopMining());
17
+ }
18
+
19
+ setupCharts() {
20
+ // Initialize empty charts
21
+ this.hashrateChart = Plotly.newPlot('hashrateChart', [{
22
+ x: [],
23
+ y: [],
24
+ type: 'scatter',
25
+ mode: 'lines+markers',
26
+ name: 'Hashrate',
27
+ line: { color: '#2ecc71' }
28
+ }], {
29
+ title: 'Mining Hashrate (KH/s)',
30
+ template: 'plotly_dark',
31
+ paper_bgcolor: '#2d2d2d',
32
+ plot_bgcolor: '#2d2d2d',
33
+ xaxis: { title: 'Time' },
34
+ yaxis: { title: 'Hashrate (KH/s)' }
35
+ });
36
+
37
+ this.totalHashesChart = Plotly.newPlot('totalHashesChart', [{
38
+ x: [],
39
+ y: [],
40
+ type: 'scatter',
41
+ mode: 'lines',
42
+ name: 'Total Hashes',
43
+ line: { color: '#3498db' }
44
+ }], {
45
+ title: 'Total Hashes',
46
+ template: 'plotly_dark',
47
+ paper_bgcolor: '#2d2d2d',
48
+ plot_bgcolor: '#2d2d2d',
49
+ xaxis: { title: 'Time' },
50
+ yaxis: { title: 'Total Hashes' }
51
+ });
52
+ }
53
+
54
+ async startMining() {
55
+ try {
56
+ const response = await fetch('/start_mining', { method: 'POST' });
57
+ const data = await response.json();
58
+ this.updateStatus(data.message);
59
+ } catch (error) {
60
+ console.error('Error starting mining:', error);
61
+ this.updateStatus('Error starting mining');
62
+ }
63
+ }
64
+
65
+ async stopMining() {
66
+ try {
67
+ const response = await fetch('/stop_mining', { method: 'POST' });
68
+ const data = await response.json();
69
+ this.updateStatus(data.message);
70
+ } catch (error) {
71
+ console.error('Error stopping mining:', error);
72
+ this.updateStatus('Error stopping mining');
73
+ }
74
+ }
75
+
76
+ async updateStats() {
77
+ try {
78
+ const response = await fetch('/get_stats');
79
+ const stats = await response.json();
80
+
81
+ // Update stats display
82
+ document.getElementById('status').textContent = stats.status;
83
+ document.getElementById('hashrate').textContent = stats.hashrate;
84
+ document.getElementById('totalHashes').textContent = stats.total_hashes;
85
+ document.getElementById('blocksFound').textContent = stats.blocks_found;
86
+ document.getElementById('bestHash').textContent = stats.best_hash;
87
+ document.getElementById('difficulty').textContent = stats.difficulty;
88
+
89
+ // Update block alert
90
+ const alertBox = document.getElementById('blockAlert');
91
+ alertBox.textContent = stats.block_alert;
92
+ alertBox.className = 'alert-box' + (stats.blocks_found > 0 ? ' success' : '');
93
+
94
+ // Update charts
95
+ if (stats.status === 'Running') {
96
+ this.updateCharts(stats);
97
+ }
98
+ } catch (error) {
99
+ console.error('Error updating stats:', error);
100
+ }
101
+ }
102
+
103
+ updateCharts(stats) {
104
+ const currentTime = new Date().toLocaleTimeString();
105
+
106
+ // Update history
107
+ this.statsHistory.timestamps.push(currentTime);
108
+ this.statsHistory.hashrates.push(parseFloat(stats.hashrate));
109
+ this.statsHistory.totalHashes.push(parseInt(stats.total_hashes.replace(/,/g, '')));
110
+
111
+ // Keep only last 100 points
112
+ if (this.statsHistory.timestamps.length > 100) {
113
+ this.statsHistory.timestamps.shift();
114
+ this.statsHistory.hashrates.shift();
115
+ this.statsHistory.totalHashes.shift();
116
+ }
117
+
118
+ // Update plots
119
+ Plotly.update('hashrateChart', {
120
+ x: [this.statsHistory.timestamps],
121
+ y: [this.statsHistory.hashrates]
122
+ });
123
+
124
+ Plotly.update('totalHashesChart', {
125
+ x: [this.statsHistory.timestamps],
126
+ y: [this.statsHistory.totalHashes]
127
+ });
128
+ }
129
+
130
+ startAutoRefresh() {
131
+ setInterval(() => this.updateStats(), 1000);
132
+ }
133
+
134
+ updateStatus(message) {
135
+ document.getElementById('status').textContent = message;
136
+ }
137
+ }
138
+
139
+ // Initialize dashboard when page loads
140
+ document.addEventListener('DOMContentLoaded', () => {
141
+ window.dashboard = new MiningDashboard();
142
+ });