diff --git a/.gitignore b/.gitignore index 1212279..a26cbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ helm-charts/values*_collector.yaml *.pyc +.aider* diff --git a/helm-charts/stratum-work-collector/Chart.yaml b/helm-charts/stratum-work-collector/Chart.yaml index 2ed9419..80f4ae5 100644 --- a/helm-charts/stratum-work-collector/Chart.yaml +++ b/helm-charts/stratum-work-collector/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 name: stratum-work-collector description: A Helm chart for deploying the Pool Work Collector Python script -version: 0.1.2 -appVersion: v1.0.3 \ No newline at end of file +version: 0.1.6 +appVersion: v1.0.6 \ No newline at end of file diff --git a/helm-charts/stratum-work-webapp/Chart.yaml b/helm-charts/stratum-work-webapp/Chart.yaml index 148d4ff..3dccda5 100755 --- a/helm-charts/stratum-work-webapp/Chart.yaml +++ b/helm-charts/stratum-work-webapp/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 description: Web frontend/backend for Pool Work name: stratum-work-webapp -version: 0.1.3 +version: 0.1.6 # renovate: image=bboerst/stratum-work-webapp -appVersion: "v1.0.3" +appVersion: "v1.0.6" diff --git a/helm-charts/stratum-work-webapp/templates/deployment.yaml b/helm-charts/stratum-work-webapp/templates/deployment.yaml index 2228c96..e3bf577 100755 --- a/helm-charts/stratum-work-webapp/templates/deployment.yaml +++ b/helm-charts/stratum-work-webapp/templates/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: labels: - app: {{ template "stratum-work-webapp.name" . }} + app: {{ template "stratum-work-webapp.fullname" . }} chart: {{ template "stratum-work-webapp.chart" . }} heritage: {{ .Release.Service }} {{ .Values.componentLabelKeyOverride | default "app.kubernetes.io/component" }}: stratum-work-webapp @@ -21,7 +21,7 @@ spec: {{- end }} selector: matchLabels: - app: {{ template "stratum-work-webapp.name" . }} + app: {{ template "stratum-work-webapp.fullname" . }} {{- if .Values.useComponentLabel }} {{ .Values.componentLabelKeyOverride | default "app.kubernetes.io/component" }}: stratum-work-webapp {{- end }} @@ -37,7 +37,7 @@ spec: {{- end }} {{- end }} labels: - app: {{ template "stratum-work-webapp.name" . }} + app: {{ template "stratum-work-webapp.fullname" . }} component: "{{ .Values.name }}" {{ .Values.componentLabelKeyOverride | default "app.kubernetes.io/component" }}: stratum-work-webapp {{- if .Values.podLabels }} diff --git a/helm-charts/stratum-work-webapp/templates/service.yaml b/helm-charts/stratum-work-webapp/templates/service.yaml index 15d4759..fbb1abe 100755 --- a/helm-charts/stratum-work-webapp/templates/service.yaml +++ b/helm-charts/stratum-work-webapp/templates/service.yaml @@ -13,7 +13,7 @@ metadata: {{- if .Values.service.labels }} {{ toYaml .Values.service.labels | indent 4 }} {{- end }} - app: {{ template "stratum-work-webapp.name" . }} + app: {{ template "stratum-work-webapp.fullname" . }} chart: {{ template "stratum-work-webapp.chart" . }} component: "{{ .Values.name }}" heritage: {{ .Release.Service }} @@ -50,6 +50,6 @@ spec: protocol: TCP targetPort: "flask" selector: - app: {{ template "stratum-work-webapp.name" . }} + app: {{ template "stratum-work-webapp.fullname" . }} type: "{{ .Values.service.type }}" {{- end }} diff --git a/helm-charts/stratum-work-webapp/values.yaml b/helm-charts/stratum-work-webapp/values.yaml index 0084751..9e47661 100755 --- a/helm-charts/stratum-work-webapp/values.yaml +++ b/helm-charts/stratum-work-webapp/values.yaml @@ -30,6 +30,7 @@ env: MONGODB_USERNAME: "mongoadmin" MONGODB_PASSWORD: "hunter1" MONGODB_HOSTS: "mongodb-0.mongodb-svc.mongodb.svc.cluster.local:27017,mongodb-1.mongodb-svc.mongodb.svc.cluster.local:27017/" + CORS_ORIGINS: "http://127.0.0.1:8000,http://localhost:8000" arguments: [] diff --git a/webapp/main.py b/webapp/main.py index 7332b3e..093e274 100644 --- a/webapp/main.py +++ b/webapp/main.py @@ -19,13 +19,18 @@ import os import pika +# Get CORS origins from environment variable +CORS_ORIGINS = os.environ.get('CORS_ORIGINS', 'http://127.0.0.1:8000,http://localhost:8000').split(',') + app = Flask(__name__) -CORS(app, resources={r"/*": {"origins": ["http://127.0.0.1:8000", "http://localhost:8000", "https://poolwork.live", "https://stratum.work"]}}) -socketio = SocketIO(app, cors_allowed_origins=["http://127.0.0.1:8000", "http://localhost:8000", "https://poolwork.live", "https://stratum.work"]) +CORS(app, resources={r"/*": {"origins": CORS_ORIGINS}}) +socketio = SocketIO(app, cors_allowed_origins=CORS_ORIGINS) @app.after_request def add_headers(response): - response.headers['Access-Control-Allow-Origin'] = 'https://stratum.work' + origin = request.headers.get('Origin') + if origin in CORS_ORIGINS: + response.headers['Access-Control-Allow-Origin'] = origin return response connection = None @@ -47,17 +52,6 @@ def add_headers(response): rabbitmq_exchange = os.environ.get('RABBITMQ_EXCHANGE') def process_row_data(row): - if 'counters' not in globals(): - globals()['counters'] = {} - - pool_name = row['pool_name'] - if pool_name not in counters or counters[pool_name]['height'] != row['height']: - counters[pool_name] = {'height': row['height'], 'count': 1} - else: - counters[pool_name]['count'] += 1 - - template_revision = counters[pool_name]['count'] - coinbase1 = row['coinbase1'] coinbase2 = row['coinbase2'] extranonce1 = row['extranonce1'] @@ -84,19 +78,10 @@ def process_row_data(row): value = tx_out.coin_value / 1e8 coinbase_outputs.append({"address": address, "value": value}) - current_time = time.time() - if 'last_revision_time' not in counters[pool_name]: - counters[pool_name]['last_revision_time'] = current_time - time_since_last_revision = 0 - else: - time_since_last_revision = current_time - counters[pool_name]['last_revision_time'] - counters[pool_name]['last_revision_time'] = current_time - processed_row = { 'pool_name': row['pool_name'], 'timestamp': row['timestamp'], 'height': height, - 'template_revision': template_revision, 'prev_block_hash': prev_block_hash, 'block_version': block_version, 'coinbase_raw': coinbase_hex, @@ -110,8 +95,7 @@ def process_row_data(row): 'merkle_branches': merkle_branches, 'merkle_branch_colors': merkle_branch_colors, 'coinbase_output_value': output_value, - 'coinbase_outputs': coinbase_outputs, - 'time_since_last_revision': time_since_last_revision + 'coinbase_outputs': coinbase_outputs } return processed_row @@ -168,7 +152,7 @@ def get_transaction_fee_rate(first_transaction): def extract_coinbase_script_ascii(coinbase_tx): # Get the script_sig in hex from the input of the coinbase transaction script_sig_hex = coinbase_tx.txs_in[0].script.hex() - + # Remove the first 8 characters (4 bytes) which represent the block height script_sig_hex = script_sig_hex[8:] @@ -188,72 +172,52 @@ def precompute_merkle_branch_colors(merkle_branches): def hash_code(text): return sum(ord(char) for char in text) -def initialize_queue_connection(): +def consume_messages(): global connection, channel - try: - credentials = pika.PlainCredentials(rabbitmq_username, rabbitmq_password) - connection = pika.BlockingConnection(pika.ConnectionParameters(rabbitmq_host, rabbitmq_port, '/', credentials)) - channel = connection.channel() - # Declare the exchange as a fanout exchange - channel.exchange_declare(exchange=rabbitmq_exchange, exchange_type='fanout', durable=True) + credentials = pika.PlainCredentials(rabbitmq_username, rabbitmq_password) + connection = pika.BlockingConnection(pika.ConnectionParameters(rabbitmq_host, rabbitmq_port, '/', credentials)) + channel = connection.channel() - # Let RabbitMQ generate a unique queue name for each consumer - result = channel.queue_declare(queue='', exclusive=True) - queue_name = result.method.queue + # Declare the exchange as a fanout exchange + channel.exchange_declare(exchange=rabbitmq_exchange, exchange_type='fanout', durable=True) - # Bind the generated queue to the fanout exchange - channel.queue_bind(exchange=rabbitmq_exchange, queue=queue_name) + # Let RabbitMQ generate a unique queue name for each consumer + result = channel.queue_declare(queue='', exclusive=True) + queue_name = result.method.queue - logger.info('RabbitMQ connection initialized') - return queue_name - except Exception as e: - logger.exception(f"Error initializing RabbitMQ connection: {e}") - return None + # Bind the generated queue to the fanout exchange + channel.queue_bind(exchange=rabbitmq_exchange, queue=queue_name) -def consume_messages(queue_name): - try: - def callback(ch, method, properties, body): - try: - message = json.loads(body) - processed_message = process_row_data(message) - socketio.emit('mining_data', processed_message, to=None) - except Exception as e: - logger.exception(f"Error processing message: {e}") - - channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True) - logger.info('Started consuming messages from the exchange') - channel.start_consuming() - except Exception as e: - logger.exception(f"Error in consume_messages: {e}") - -# Initialize queue connection -queue_name = initialize_queue_connection() -if queue_name: - # Start consuming messages in a background thread - socketio.start_background_task(target=consume_messages, queue_name=queue_name) + def callback(ch, method, properties, body): + try: + message = json.loads(body) + processed_message = process_row_data(message) + for client_id in connected_clients: + socketio.emit('mining_data', processed_message, room=client_id) + except Exception as e: + logger.exception(f"Error processing message: {e}") + + channel.basic_consume(queue=queue_name, on_message_callback=callback, auto_ack=True) + + logger.info('Started consuming messages from the exchange') + channel.start_consuming() @socketio.on('connect') def handle_connect(): - try: - logger.info('Client connected') - connected_clients.add(request.sid) - except Exception as e: - logger.warning(f"Error during client connect: {e}") + logger.info('Client connected') + if len(connected_clients) == 0: + socketio.start_background_task(target=consume_messages) + connected_clients.add(request.sid) @socketio.on('disconnect') def handle_disconnect(): - try: - logger.info('Client disconnected') - connected_clients.remove(request.sid) - except KeyError: - logger.info('Client disconnected (already removed)') - except Exception as e: - logger.warning(f"Error during client disconnect: {e}") - -@socketio.on('heartbeat') -def handle_heartbeat(): - pass # Simply acknowledge the heartbeat + logger.info('Client disconnected') + connected_clients.remove(request.sid) + if len(connected_clients) == 0: + if channel and connection: + channel.stop_consuming() + connection.close() def gzip_response(response): accept_encoding = request.headers.get('Accept-Encoding', '') @@ -294,5 +258,4 @@ def handle_sigterm(*args): sys.exit(0) if __name__ == "__main__": - signal.signal(signal.SIGTERM, handle_sigterm) - socketio.run(app, debug=True, use_reloader=False) + socketio.run(app) \ No newline at end of file diff --git a/webapp/static/main.js b/webapp/static/main.js index 51e6f71..fd79678 100644 --- a/webapp/static/main.js +++ b/webapp/static/main.js @@ -1,57 +1,5 @@ document.addEventListener('DOMContentLoaded', () => { - function connectWebSocket() { - const socket = io(SOCKET_URL, { - reconnection: true, - reconnectionAttempts: Infinity, - reconnectionDelay: 1000, - reconnectionDelayMax: 5000, - randomizationFactor: 0.5, - }); - - socket.on('connect', () => { - console.log('WebSocket connected'); - hideReconnectionMessage(); - }); - - socket.on('disconnect', (reason) => { - console.log('WebSocket disconnected:', reason); - showReconnectionMessage(); - }); - - socket.on('connect_error', (error) => { - console.error('WebSocket connection error:', error); - showReconnectionMessage(); - }); - - return socket; - } - - function showReconnectionMessage() { - const message = document.getElementById('reconnection-message'); - if (!message) { - const newMessage = document.createElement('div'); - newMessage.id = 'reconnection-message'; - newMessage.innerHTML = 'Attempting to reconnect...'; - newMessage.style.position = 'fixed'; - newMessage.style.top = '10px'; - newMessage.style.right = '10px'; - newMessage.style.backgroundColor = 'rgba(255, 0, 0, 0.7)'; - newMessage.style.color = 'white'; - newMessage.style.padding = '10px'; - newMessage.style.borderRadius = '5px'; - newMessage.style.zIndex = '1000'; - document.body.appendChild(newMessage); - } - } - - function hideReconnectionMessage() { - const message = document.getElementById('reconnection-message'); - if (message) { - message.remove(); - } - } - - const socket = connectWebSocket(); + const socket = io(SOCKET_URL); let isPaused = false; const pauseButton = document.getElementById('pause-button'); @@ -104,27 +52,6 @@ document.addEventListener('DOMContentLoaded', () => { field: 'pool_name', width: 130, }, - { - title: 'Template Revision for Current Block', - field: 'template_revision', - width: 50, - sorter: 'number', - visible: true, - }, - { - title: 'Time Since Last Template Revision', - field: 'time_since_last_revision', - width: 80, - sorter: 'number', - formatter: function(cell) { - const value = cell.getValue(); - if (value === undefined || value === null) return ''; - const seconds = Math.floor(value); - const milliseconds = Math.round((value - seconds) * 1000); - return `${seconds}.${milliseconds.toString().padStart(3, '0')}s`; - }, - visible: true, - }, { title: 'Timestamp', field: 'timestamp', @@ -154,6 +81,7 @@ document.addEventListener('DOMContentLoaded', () => { .filter(output => !output.address.includes("nulldata")) .map(output => `${output.address}:${output.value}`) .join('|'); + const color = generateColorFromOutputs(outputs); cell.getElement().style.backgroundColor = color; cell.getElement().style.whiteSpace = 'nowrap'; @@ -371,13 +299,4 @@ document.addEventListener('DOMContentLoaded', () => { return text.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } - - setInterval(() => { - if (socket.connected) { - socket.emit('heartbeat'); - } else { - console.log('WebSocket not connected, attempting to reconnect...'); - socket.connect(); - } - }, 30000); // Send heartbeat every 30 seconds }); \ No newline at end of file diff --git a/webapp/templates/index.html b/webapp/templates/index.html index b0b1a9a..2c2929b 100644 --- a/webapp/templates/index.html +++ b/webapp/templates/index.html @@ -233,17 +233,6 @@ animation: flash-border 1s infinite; border: 2px solid red; } - - #reconnection-message { - position: fixed; - top: 10px; - right: 10px; - background-color: rgba(255, 0, 0, 0.7); - color: white; - padding: 10px; - border-radius: 5px; - z-index: 1000; - }