Enhance Ticket management functionalities
This commit is contained in:
@@ -77,15 +77,26 @@ def create_notification(user_id, notif_type, title, message, ticket_id=None, lin
|
||||
db.session.commit()
|
||||
logger.info(f'[NOTIFICATION CREATE] user_id={user_id} type={notif_type} ticket_id={ticket_id}')
|
||||
|
||||
# Real-time push
|
||||
socketio.emit('new_notification', {
|
||||
'id' : notif.id,
|
||||
'type' : notif_type,
|
||||
'title' : title,
|
||||
'message' : message,
|
||||
'link' : link,
|
||||
# Real-time push — schedule via socketio.start_background_task so the
|
||||
# emit runs inside eventlet's green-thread pool, not inline in the WSGI
|
||||
# request context. Inline emits during a polling→WebSocket upgrade can
|
||||
# race with the upgrade handshake and disconnect the client.
|
||||
payload = {
|
||||
'id' : notif.id,
|
||||
'type' : notif_type,
|
||||
'title' : title,
|
||||
'message' : message,
|
||||
'link' : link,
|
||||
'created_at': notif.created_at.isoformat(),
|
||||
}, room=f'user_{user_id}')
|
||||
}
|
||||
def _emit():
|
||||
socketio.emit(
|
||||
'new_notification',
|
||||
payload,
|
||||
to=f'user_{user_id}', # 'to' is the modern alias for 'room'
|
||||
namespace='/', # explicit default namespace — avoids
|
||||
) # ambiguity under reverse-proxy setups
|
||||
socketio.start_background_task(_emit)
|
||||
|
||||
except Exception as exc:
|
||||
db.session.rollback()
|
||||
@@ -95,13 +106,30 @@ def create_notification(user_id, notif_type, title, message, ticket_id=None, lin
|
||||
# ─── Email Notification ───────────────────────────────────────────────────────
|
||||
|
||||
def send_email(subject, recipients, html_body):
|
||||
"""Send an email; silently log errors so it never blocks the main flow."""
|
||||
try:
|
||||
msg = Message(subject=subject, recipients=recipients, html=html_body)
|
||||
mail.send(msg)
|
||||
logger.info(f'[EMAIL SENT] subject="{subject}" to={recipients}')
|
||||
except Exception as exc:
|
||||
logger.error(f'[EMAIL ERROR] Failed to send "{subject}" to {recipients}: {exc}')
|
||||
"""
|
||||
Send an email in a background thread so it never blocks the request.
|
||||
|
||||
Flask-Mail opens an SMTP connection synchronously. Running it in the main
|
||||
request thread means a slow or failing SMTP server delays the entire
|
||||
response — including the WebSocket notification push that follows.
|
||||
Using a daemon thread isolates SMTP failures from the request lifecycle.
|
||||
"""
|
||||
from threading import Thread
|
||||
from flask import current_app
|
||||
|
||||
app = current_app._get_current_object() # real app, not the proxy
|
||||
|
||||
def _send():
|
||||
with app.app_context():
|
||||
try:
|
||||
msg = Message(subject=subject, recipients=recipients, html=html_body)
|
||||
mail.send(msg)
|
||||
logger.info(f'[EMAIL SENT] subject="{subject}" to={recipients}')
|
||||
except Exception as exc:
|
||||
logger.error(f'[EMAIL ERROR] Failed to send "{subject}" to {recipients}: {exc}')
|
||||
|
||||
t = Thread(target=_send, daemon=True)
|
||||
t.start()
|
||||
|
||||
|
||||
# ─── Ticket Event Helpers ─────────────────────────────────────────────────────
|
||||
@@ -252,4 +280,4 @@ def notify_assignment(ticket, assigned_by):
|
||||
[ticket.assignee.email],
|
||||
html,
|
||||
)
|
||||
logger.info(f'[TICKET ASSIGN] ticket_id={ticket.id} assigned_to={ticket.assigned_to_id} by={assigned_by.id}')
|
||||
logger.info(f'[TICKET ASSIGN] ticket_id={ticket.id} assigned_to={ticket.assigned_to_id} by={assigned_by.id}')
|
||||
Reference in New Issue
Block a user