From 765c07316aa9dbb3c47ae552500d47b3b976754c Mon Sep 17 00:00:00 2001 From: Kobe Date: Mon, 2 Jun 2025 14:30:20 +0200 Subject: [PATCH] SMTP Settings --- __pycache__/models.cpython-313.pyc | Bin 25830 -> 27882 bytes ...8006bd1732_add_key_value_settings_table.py | 35 ++ ...d_key_value_settings_table.cpython-313.pyc | Bin 0 -> 1551 bytes models.py | 34 +- routes/__pycache__/__init__.cpython-313.pyc | Bin 2170 -> 2170 bytes routes/__pycache__/main.cpython-313.pyc | Bin 75120 -> 78344 bytes routes/main.py | 72 +++- templates/settings/settings.html | 11 + templates/settings/tabs/smtp_settings.html | 311 ++++++++++++++++++ 9 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/0a8006bd1732_add_key_value_settings_table.py create mode 100644 migrations/versions/__pycache__/0a8006bd1732_add_key_value_settings_table.cpython-313.pyc create mode 100644 templates/settings/tabs/smtp_settings.html diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc index 92562baf61d0d00ffd12222bf0678da43a9514c1..704ae3bd54b8830163d5e5419889e23ee5d18269 100644 GIT binary patch delta 6632 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 diff --git a/routes/main.py b/routes/main.py index 62bd3a6..02e419b 100644 --- a/routes/main.py +++ b/routes/main.py @@ -1,6 +1,6 @@ from flask import render_template, Blueprint, redirect, url_for, request, flash, Response, jsonify, session from flask_login import current_user, login_required -from models import User, db, Room, RoomFile, RoomMemberPermission, SiteSettings, Event, Conversation, Message, MessageAttachment, Notif, EmailTemplate, Mail +from models import User, db, Room, RoomFile, RoomMemberPermission, SiteSettings, Event, Conversation, Message, MessageAttachment, Notif, EmailTemplate, Mail, KeyValueSettings from routes.auth import require_password_change import os from werkzeug.utils import secure_filename @@ -14,6 +14,8 @@ from utils import log_event, create_notification, get_unread_count from io import StringIO import csv from flask_wtf.csrf import generate_csrf +import json +import smtplib # Set up logging to show in console logging.basicConfig( @@ -631,6 +633,11 @@ def init_routes(main_bp): site_settings = SiteSettings.get_settings() company_form = CompanySettingsForm() + # Get SMTP settings for the SMTP tab + smtp_settings = None + if active_tab == 'smtp': + smtp_settings = KeyValueSettings.get_value('smtp_settings') + # Get events for the events tab events = None total_pages = 0 @@ -691,8 +698,71 @@ def init_routes(main_bp): users=users, email_templates=email_templates, form=company_form, + smtp_settings=smtp_settings, csrf_token=generate_csrf()) + @main_bp.route('/settings/update-smtp', methods=['POST']) + @login_required + def update_smtp_settings(): + if not current_user.is_admin: + return jsonify({'error': 'Unauthorized'}), 403 + + try: + # Get SMTP settings from form + smtp_settings = { + 'smtp_host': request.form.get('smtp_host'), + 'smtp_port': int(request.form.get('smtp_port')), + 'smtp_security': request.form.get('smtp_security'), + 'smtp_username': request.form.get('smtp_username'), + 'smtp_password': request.form.get('smtp_password'), + 'smtp_from_email': request.form.get('smtp_from_email'), + 'smtp_from_name': request.form.get('smtp_from_name') + } + + # Save to database using KeyValueSettings + KeyValueSettings.set_value('smtp_settings', smtp_settings) + + flash('SMTP settings updated successfully.', 'success') + return redirect(url_for('main.settings', tab='smtp')) + + except Exception as e: + db.session.rollback() + flash(f'Error updating SMTP settings: {str(e)}', 'error') + return redirect(url_for('main.settings', tab='smtp')) + + @main_bp.route('/settings/test-smtp', methods=['POST']) + @login_required + def test_smtp_connection(): + if not current_user.is_admin: + return jsonify({'error': 'Unauthorized'}), 403 + + try: + data = request.get_json() + + # Create SMTP connection + if data['smtp_security'] == 'ssl': + smtp = smtplib.SMTP_SSL(data['smtp_host'], int(data['smtp_port'])) + else: + smtp = smtplib.SMTP(data['smtp_host'], int(data['smtp_port'])) + + # Start TLS if needed + if data['smtp_security'] == 'tls': + smtp.starttls() + + # Login + smtp.login(data['smtp_username'], data['smtp_password']) + + # Close connection + smtp.quit() + + return jsonify({'success': True}) + + except Exception as e: + return jsonify({ + 'success': False, + 'error': str(e) + }) + @main_bp.route('/settings/colors', methods=['POST']) @login_required def update_colors(): diff --git a/templates/settings/settings.html b/templates/settings/settings.html index c57a6e7..ded4261 100644 --- a/templates/settings/settings.html +++ b/templates/settings/settings.html @@ -7,6 +7,7 @@ {% from "settings/tabs/events.html" import events_tab %} {% from "settings/tabs/email_templates.html" import email_templates_tab %} {% from "settings/tabs/mails.html" import mails_tab %} +{% from "settings/tabs/smtp_settings.html" import smtp_settings_tab %} {% from "settings/components/reset_colors_modal.html" import reset_colors_modal %} {% block title %}Settings - DocuPulse{% endblock %} @@ -64,6 +65,11 @@ Debugging +
@@ -102,6 +108,11 @@
{{ debugging_tab() }}
+ + +
+ {{ smtp_settings_tab(smtp_settings, csrf_token) }} +
diff --git a/templates/settings/tabs/smtp_settings.html b/templates/settings/tabs/smtp_settings.html new file mode 100644 index 0000000..0d343dd --- /dev/null +++ b/templates/settings/tabs/smtp_settings.html @@ -0,0 +1,311 @@ +{% macro smtp_settings_tab(smtp_settings, csrf_token) %} +
+
+
+
+
+ + +
+ +
+
SMTP Server Settings
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
Authentication
+
+ + +
+
+ + +
+
+ + +
+
Sender Information
+
+ + +
+
+ + +
+
+ + +
+
+
+ Email Deliverability Best Practices +
+

To prevent your emails from being marked as spam, ensure you have:

+
    +
  • SPF Record: Add a TXT record to your domain's DNS settings: + v=spf1 include:_spf.your-email-provider.com ~all +
  • +
  • DKIM: Add the DKIM record provided by your email provider to your domain's DNS settings
  • +
  • DMARC: Add a TXT record named "_dmarc" with: + v=DMARC1; p=none; rua=mailto:dmarc-reports@yourdomain.com +
  • +
  • Reverse DNS (PTR): Ensure your sending IP has a valid reverse DNS record
  • +
  • Domain Age: Use a domain that's at least 30 days old
  • +
  • Email Volume: Start with a small volume and gradually increase
  • +
+
+
+
+ + +
+ +
+
+ + +
+ +
+
+
+
+
+
+ + +{% endmacro %} \ No newline at end of file