diff --git a/src/anonchat/admin_routes.py b/src/anonchat/admin_routes.py index bcf4d87..da6da64 100644 --- a/src/anonchat/admin_routes.py +++ b/src/anonchat/admin_routes.py @@ -2,9 +2,8 @@ from flask import request, render_template, redirect, url_for, flash, session from functools import wraps from . import app, db, limiter, csrf from .models import Inquiry, Message, Settings, Admin -from .notifiers import webhooks, emails +from .notifiers import webhooks from .notifiers.webhooks import WebhookError -from .notifiers.emails import EmailNotificationError def is_admin(): return 'admin_authenticated' in session and session['admin_authenticated'] @@ -174,43 +173,4 @@ def admin_settings_webhook(): else: flash('Webhook settings saved', 'success') - return redirect(url_for('admin_settings')) - -@app.route('/admin/settings/email', methods=['POST']) -@admin_required -def admin_settings_email(): - settings = Settings.query.first() - if not settings: - settings = Settings() - db.session.add(settings) - - settings.email_notifications_enabled = request.form.get('email_notifications_enabled') == 'on' - settings.smtp_server = request.form.get('smtp_server', '') - settings.smtp_use_ssl = request.form.get('smtp_use_ssl') == 'on' - settings.smtp_username = request.form.get('smtp_username', '') - settings.smtp_password = request.form.get('smtp_password', '') - settings.recipient_email = request.form.get('recipient_email', '') - - db.session.commit() - - if settings.email_notifications_enabled: - result = emails.verify_email_settings(settings) - if result['errors']: - flash(f'Email settings verification failed: {", ".join(result["errors"])}', 'error') - else: - try: - emails.send_email( - title='Inquiry #1234abcd1234abcd Created', - inquiry_id='1234abcd1234abcd', - data={ - 'message': 'This is a test message' - }, - is_async=False - ) - flash('Email settings saved and test sent successfully') - except EmailNotificationError as e: - flash(f'Email test failed: {str(e)}', 'error') - else: - flash('Email notifications svaed', 'success') - - return redirect(url_for('admin_settings')) + return redirect(url_for('admin_settings')) \ No newline at end of file diff --git a/src/anonchat/migrations/add_email_settings.py b/src/anonchat/migrations/add_email_settings.py deleted file mode 100644 index 8885196..0000000 --- a/src/anonchat/migrations/add_email_settings.py +++ /dev/null @@ -1,49 +0,0 @@ -from sqlalchemy import Column, Boolean, String, inspect -from sqlalchemy.sql import text -from flask import current_app - -def run_migration(db): - """Add email notification settings columns to the Settings table if they don't exist.""" - current_app.logger.info("Running migration: add_email_settings") - - # Check if the table exists first - inspector = inspect(db.engine) - if 'settings' not in inspector.get_table_names(): - current_app.logger.info("Settings table doesn't exist yet, skipping migration") - return False - - # Check existing columns - columns = [col['name'] for col in inspector.get_columns('settings')] - - # Track if we need to commit changes - changes_made = False - - # Dictionary of columns to add with their types - columns_to_add = { - 'email_notifications_enabled': 'BOOLEAN DEFAULT FALSE', - 'smtp_server': 'VARCHAR(255)', - 'smtp_use_ssl': 'BOOLEAN DEFAULT FALSE', - 'smtp_username': 'VARCHAR(255)', - 'smtp_password': 'VARCHAR(255)', - 'recipient_email': 'VARCHAR(255)', - 'notification_show_message': 'BOOLEAN DEFAULT FALSE', - 'auto_delete_hours': 'INTEGER DEFAULT 48' - } - - # Add each column if it doesn't exist - for column_name, column_type in columns_to_add.items(): - if column_name not in columns: - current_app.logger.info(f"Adding {column_name} column to settings table") - with db.engine.connect() as conn: - conn.execute(text(f"ALTER TABLE settings ADD COLUMN {column_name} {column_type}")) - conn.commit() - changes_made = True - else: - current_app.logger.info(f"{column_name} column already exists in settings table") - - if changes_made: - current_app.logger.info("Migration completed successfully") - else: - current_app.logger.info("No changes needed, all columns already exist") - - return changes_made \ No newline at end of file diff --git a/src/anonchat/models/settings.py b/src/anonchat/models/settings.py index 45df5fa..f6586fc 100644 --- a/src/anonchat/models/settings.py +++ b/src/anonchat/models/settings.py @@ -8,14 +8,6 @@ class Settings(db.Model): webhook_url = db.Column(db.String(255), nullable=True) webhook_secret = db.Column(db.String(255), nullable=True) - # Email notification settings - email_notifications_enabled = db.Column(db.Boolean, default=False) - smtp_server = db.Column(db.String(255), nullable=True) - smtp_use_ssl = db.Column(db.Boolean, default=False) - smtp_username = db.Column(db.String(255), nullable=True) - smtp_password = db.Column(db.String(255), nullable=True) # Consider storing this securely - recipient_email = db.Column(db.String(255), nullable=True) - notification_show_message = db.Column(db.Boolean, default=False) auto_delete_hours = db.Column(db.Integer, default=48) \ No newline at end of file diff --git a/src/anonchat/notifiers/__init__.py b/src/anonchat/notifiers/__init__.py index cf63aae..327427d 100644 --- a/src/anonchat/notifiers/__init__.py +++ b/src/anonchat/notifiers/__init__.py @@ -1,7 +1,7 @@ from flask import current_app -from .emails import send_email from .webhooks import send_webhook + MESSAGE_PREVIEW_LENGTH = 30 EVENT_TYPES = { 'inquiry_created': 'Inquiry #{} Created', @@ -46,15 +46,6 @@ def inquiry_message(inquiry_id: str, message: str = None, is_async: bool = True) ### Private methods ### def _send_event(event_type: str, inquiry_id: str, message: str = None, is_async: bool = True) -> None: - _send_email_ignore_errors( - title=EVENT_TYPES[event_type].format(inquiry_id), - inquiry_id=inquiry_id, - data={ - 'message': _get_message_preview(message) if message else None - }, - is_async=is_async - ) - _send_webhook_ignore_errors( event_type=event_type, data={ @@ -64,12 +55,6 @@ def _send_event(event_type: str, inquiry_id: str, message: str = None, is_async: is_async=is_async ) -def _send_email_ignore_errors(title, inquiry_id, data, is_async=True): - try: - send_email(title, inquiry_id, data, is_async) - except: - pass - def _send_webhook_ignore_errors(event_type, data, is_async=True): try: send_webhook(event_type, data, is_async) diff --git a/src/anonchat/notifiers/emails.py b/src/anonchat/notifiers/emails.py deleted file mode 100644 index 9477c13..0000000 --- a/src/anonchat/notifiers/emails.py +++ /dev/null @@ -1,156 +0,0 @@ -import smtplib -from email.message import EmailMessage -import threading -from datetime import datetime -from flask import current_app -from ..models import Settings - -class EmailNotificationError(Exception): - """Exception raised for errors in the email notification process. - - Attributes: - message -- explanation of the error - original_exception -- the original exception that caused this error (optional) - """ - - def __init__(self, message, original_exception=None): - self.message = message - self.original_exception = original_exception - super().__init__(self.message) - - -def verify_email_settings(settings: Settings = None): - """ - Verifies that email notification settings are properly configured. - - Returns: - dict: A dictionary containing: - - 'valid' (bool): Whether settings are valid for sending emails - - 'errors' (list): List of error messages if any settings are invalid - - 'settings' (dict): Current settings state (without sensitive values) - """ - - if settings is None: - settings = Settings.query.first() - - result = { - 'valid': False, - 'errors': [], - 'settings': {} - } - - # Check if settings exist and notifications are enabled - if not settings: - result['errors'].append("Settings not found in database") - return result - - result['settings']['email_notifications_enabled'] = settings.email_notifications_enabled - if not settings.email_notifications_enabled: - result['errors'].append("Email notifications are disabled") - return result - - # Required fields - required_fields = { - 'smtp_server': settings.smtp_server, - 'smtp_username': settings.smtp_username, - 'recipient_email': settings.recipient_email - } - - # Check required fields - for field, value in required_fields.items(): - result['settings'][field] = value - if not value: - result['errors'].append(f"Missing required setting: {field}") - - # Test SMTP connection if desired (commented out to avoid actual connection attempts) - # This could be implemented as a separate function that calls this one first - - # Mark as valid if no errors - result['valid'] = len(result['errors']) == 0 - - return result - -def send_email(title: str, inquiry_id: str, data: dict = {}, is_async=False): - """Sends an email notification, either synchronously or asynchronously.""" - app = current_app._get_current_object() - - if is_async: - _send_email_async(app, title, inquiry_id, data) - else: - _send_email_worker(app, title, inquiry_id, data) - -def _send_email_async(app, title: str, inquiry_id: str, data: dict = {}): - """Queue email to be sent in a background thread""" - thread = threading.Thread(target=_send_email_worker, args=(app, title, inquiry_id, data)) - thread.daemon = True # Thread will exit when main thread exits - thread.start() - - -def _send_email_worker(app, title: str, inquiry_id: str, data: dict = {}): - """Worker function that sends the email notification.""" - try: - with app.app_context(): - settings = Settings.query.first() - if not settings or not settings.email_notifications_enabled: - return False - - verification_result = verify_email_settings(settings) - if not verification_result['valid']: - raise EmailNotificationError(f"Invalid email settings: {', '.join(verification_result['errors'])}") - - # Construct email message - msg = EmailMessage() - msg['Subject'] = "[AnonChat] " + title - msg['From'] = settings.smtp_username - msg['To'] = settings.recipient_email - - content = f""" - Title: {title} - Timestamp: {datetime.utcnow().isoformat()} UTC - Inquiry ID: {inquiry_id} - - {data.get('message', '') if settings.notification_show_message else ''}""" - - msg.set_content(content) - - _send_smtp_message(settings, msg) - app.logger.info(f"Email notification sent for title: {title}") - except Exception as e: - app.logger.error(f"Email error: {e}") - - if isinstance(e, EmailNotificationError): - raise e - - raise EmailNotificationError(f"Exception: {e}", original_exception=e) - -def _get_smtp_server(smtp_server: str): - smtp_server_parts = smtp_server.split(':') - smtp_host = smtp_server_parts[0] - - if len(smtp_server_parts) > 1: - smtp_port = int(smtp_server_parts[1]) - else: - smtp_port = 0 - - return (smtp_host, smtp_port) - -def _send_smtp_message(settings, msg): - smtp_server = None - - smtp_host, smtp_port = _get_smtp_server(settings.smtp_server) - - try: - if settings.smtp_use_ssl: - smtp_server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10) - else: - smtp_server = smtplib.SMTP(smtp_host, smtp_port, timeout=10) - - if settings.smtp_username and settings.smtp_password: - smtp_server.login(settings.smtp_username, settings.smtp_password) - - smtp_server.send_message(msg) - except smtplib.SMTPException as e: - raise EmailNotificationError(f"SMTP Error: {e}", original_exception=e) - finally: - if smtp_server: - smtp_server.quit() \ No newline at end of file diff --git a/src/anonchat/templates/admin/settings.html b/src/anonchat/templates/admin/settings.html index 19efac8..22445f7 100644 --- a/src/anonchat/templates/admin/settings.html +++ b/src/anonchat/templates/admin/settings.html @@ -45,13 +45,6 @@ This will have no effect on inquiries that are already closed. - -
- -
@@ -84,6 +77,13 @@ +
+ + +
@@ -112,53 +112,4 @@ }
- -
- -
-

Email Notification Settings

- -
-

Configure email settings to receive notifications for new inquiries and responses.

-
- -
- -
- -
- -
- - -
- -
- -
- -
- - -
- -
- - -
- -
- - -
- - -
-
{% endblock %} \ No newline at end of file