Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions hookreceiver/hookreceiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
import json
import string
import time
from flask import Flask, request, abort, g, redirect, render_template, url_for
from flask import Flask, request, abort, g, redirect, render_template, url_for, Response
import hashlib
import hmac
from werkzeug.exceptions import HTTPException # Add this import
import sqlite3
from pathlib import Path
import queue

# Create a global event queue for SSE notifications
event_queue = queue.Queue()

def verify_signature(payload_body, secret_token, signature_header):
"""
Expand Down Expand Up @@ -146,11 +149,16 @@ def slurphook():
))
db.commit()
app.logger.debug(f"Webhook data stored in database: {config.db_name}")

# Clear queue and send refresh message
clear_event_queue()
event_queue.put("refresh")
app.logger.debug("Refresh message sent to event queue")

return '', config.status_code
except Exception as e:
app.logger.error(f"Failed to store webhook data: {str(e)}")
raise

return ('status', config.status_code)


@app.route('/truncate', methods=['POST'])
Expand Down Expand Up @@ -272,6 +280,48 @@ def clear_events():
return {'status': 'error', 'message': str(e)}, 500


@app.route('/stream')
def stream():
def event_stream():
app.logger.debug("New client connected to event stream")
while True:
try:
message = event_queue.get(timeout=5) # Reduced timeout
app.logger.debug(f"Sending SSE message: {message}")
yield f"data: {message}\n\n"
app.logger.debug("SSE message sent successfully")
except queue.Empty:
yield ": keep-alive\n\n"
app.logger.debug("Sent keep-alive message")
except GeneratorExit:
app.logger.debug("Client disconnected from event stream")
break
except Exception as e:
app.logger.error(f"Error in event stream: {e}")
break

response = Response(
event_stream(),
mimetype="text/event-stream",
headers={
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no',
'Access-Control-Allow-Origin': '*'
}
)
return response


def clear_event_queue():
"""Clear all messages from the event queue."""
while not event_queue.empty():
try:
event_queue.get_nowait()
except queue.Empty:
break


if __name__ == '__main__':
# Keep command line argument support for direct Python execution
parser = argparse.ArgumentParser()
Expand Down Expand Up @@ -307,6 +357,10 @@ def clear_events():
config.status_code = args.status_code
config.db_name = args.db_name

# Clear the event queue on startup
clear_event_queue()
app.logger.debug("Event queue cleared on startup")

# Create app context before initializing database
with app.app_context():
init_db()
Expand Down
77 changes: 63 additions & 14 deletions hookreceiver/templates/hookdb.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@ <h2>All Events</h2>
<div class="details">
<h3>Payload
<button class="copy-button"
onclick="copyToClipboard(this, `{{ formatted_payload.replace('`', '\\`') }}`)">
onclick="copyToClipboard(this, '{{ formatted_payload|escape }}')">
Copy
</button>
</h3>
<pre>{{ formatted_payload }}</pre>

<h3>Headers
<button class="copy-button"
onclick="copyToClipboard(this, `{{ formatted_headers.replace('`', '\\`') }}`)">
onclick="copyToClipboard(this, '{{ formatted_headers|escape }}')">
Copy
</button>
</h3>
Expand Down Expand Up @@ -241,21 +241,70 @@ <h3>Headers
{% endif %}

<script>
let eventSource = null;

function setupEventSource() {
if (eventSource) {
eventSource.close();
}

console.log("Setting up EventSource connection...");
eventSource = new EventSource('/stream');

eventSource.onopen = function(e) {
console.log("EventSource connection established");
};

eventSource.onmessage = function(e) {
console.log("Received SSE message:", e.data);
if (e.data === "refresh") {
console.log("Refreshing page...");
location.reload(true); // Force reload from server
}
};

eventSource.onerror = function(e) {
console.error("EventSource error:", e);
if (eventSource.readyState === EventSource.CLOSED) {
console.log("Connection closed, attempting to reconnect...");
setTimeout(setupEventSource, 1000);
}
};

// Clean up on page unload
window.addEventListener('beforeunload', function() {
if (eventSource) {
console.log("Closing EventSource connection");
eventSource.close();
}
});
}

// Initialize EventSource when the page loads
document.addEventListener('DOMContentLoaded', function() {
setupEventSource();
});

function clearEvents() {
if (confirm('Are you sure you want to clear all events?')) {
fetch('/clear', { method: 'POST' })
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
window.location.reload();
} else {
alert('Failed to clear events');
}
})
.catch(error => {
console.error('Error:', error);
fetch('/clear', {
method: 'POST',
headers: {
'Accept': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
location.reload(true);
} else {
alert('Failed to clear events');
});
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to clear events');
});
}
}

Expand Down