Make webhooks async

This commit is contained in:
Minecon724 2025-04-03 18:04:27 +02:00
commit 94e9d5ea75
Signed by untrusted user who does not match committer: m724
GPG key ID: A02E6E67AB961189
2 changed files with 74 additions and 54 deletions

View file

@ -97,18 +97,18 @@ def admin_settings_webhook():
settings.webhook_url = request.form.get('webhook_url', '')
settings.webhook_secret = request.form.get('webhook_secret', '')
db.session.commit()
# Test webhook if enabled and URL is provided
if settings.webhook_enabled and settings.webhook_url:
try:
inquiry_created('1234abcd1234abcd', 'This is a test message')
inquiry_created('1234abcd1234abcd', 'This is a test message', is_async=False)
flash('Webhook settings saved and test sent successfully')
except WebhookError as e:
flash(f'Webhook test failed: {str(e)}')
else:
flash('Webhook settings saved')
db.session.commit()
return redirect(url_for('admin_settings'))
@app.route('/admin/settings', methods=['GET'])

View file

@ -3,6 +3,8 @@ import urllib.error
import hmac
import hashlib
import json
import threading
import logging
from datetime import datetime
from flask import current_app
from .models import Settings
@ -23,8 +25,25 @@ class WebhookError(Exception):
super().__init__(self.message)
def _send_webhook(event_type, data):
"""Send webhook if enabled with proper signature"""
def _send_webhook(event_type, data, is_async=False):
app = current_app._get_current_object()
if is_async:
_send_webhook_async(event_type, data, app)
else:
_send_webhook_worker(event_type, data, app)
def _send_webhook_async(event_type, data, app):
"""Queue webhook to be sent in a background thread"""
thread = threading.Thread(target=_send_webhook_worker, args=(event_type, data, app))
thread.daemon = True # Thread will exit when main thread exits
thread.start()
def _send_webhook_worker(event_type, data, app):
"""Worker function that sends the webhook in a background thread"""
try:
with app.app_context():
settings = Settings.query.first()
if not settings or not settings.webhook_enabled or not settings.webhook_url:
return False
@ -48,7 +67,6 @@ def _send_webhook(event_type, data):
).hexdigest()
headers['X-Webhook-Signature'] = signature
try:
# Create request object
req = urllib.request.Request(
settings.webhook_url,
@ -60,34 +78,36 @@ def _send_webhook(event_type, data):
# Set timeout
with urllib.request.urlopen(req, timeout=5) as response:
if response.status != 200:
raise WebhookError(f"Webhook error: {response.status}", status_code=response.status)
raise WebhookError("Status code not 200", status_code=response.status)
except Exception as e:
status_code = None
if hasattr(e, 'status_code'):
status_code = e.status_code
raise WebhookError(f"Webhook error: {str(e)}", status_code=status_code, original_exception=e)
app.logger.error(f"Webhook error: {e}")
def inquiry_created(inquiry_id, message):
if isinstance(e, WebhookError):
raise e
raise WebhookError(f"Exception: {e}", original_exception=e)
def inquiry_created(inquiry_id, message, is_async=True):
_send_webhook('inquiry_created', {
'inquiry_id': inquiry_id,
'message': message
})
}, is_async)
def inquiry_reopened(inquiry_id):
def inquiry_reopened(inquiry_id, is_async=True):
_send_webhook('inquiry_reopened', {
'inquiry_id': inquiry_id
})
}, is_async)
def inquiry_closed(inquiry_id):
def inquiry_closed(inquiry_id, is_async=True):
_send_webhook('inquiry_closed', {
'inquiry_id': inquiry_id
})
}, is_async)
def inquiry_message(inquiry_id, message):
def inquiry_message(inquiry_id, message, is_async=True):
_send_webhook('inquiry_message', {
'inquiry_id': inquiry_id,
'message': message
})
}, is_async)