feat: Add email settings management and templates functionality
All checks were successful
CI / test (3.11) (push) Successful in 1m36s
CI / build-image (push) Successful in 1m27s

- Implemented email settings configuration in the admin panel, allowing for SMTP settings and notification preferences.
- Created a new template for email settings with fields for SMTP host, port, username, password, sender address, and recipients.
- Added JavaScript functionality to handle loading, saving, and validating email settings.
- Introduced email templates management, enabling the listing, editing, and saving of email templates.
- Updated navigation to include links to email settings and templates.
- Added tests for email settings and templates to ensure proper functionality and validation.
This commit is contained in:
2025-11-15 11:12:23 +01:00
parent 2629f6b25f
commit e192086833
19 changed files with 1537 additions and 192 deletions

View File

@@ -6,6 +6,17 @@ import logging
from .. import auth, settings
from ..database import delete_app_setting, get_app_settings, get_subscribers, update_app_setting
from ..services.email_settings import (
EMAIL_SETTINGS_DEFINITIONS,
load_email_settings,
persist_email_settings,
validate_email_settings,
)
from ..services.email_templates import (
list_templates as list_email_templates,
load_template as load_email_template,
persist_template as persist_email_template,
)
bp = Blueprint("admin", __name__, url_prefix="/admin")
@@ -96,6 +107,13 @@ def email_templates():
return render_template('admin/admin_email_templates.html')
@bp.route('/email-settings')
@auth.login_required
def email_settings_page():
"""Render the dedicated email settings management page."""
return render_template('admin/admin_email_settings.html')
@bp.route("/api/settings", methods=["GET"])
@auth.login_required
def get_settings_api():
@@ -179,6 +197,121 @@ def delete_setting_api(key: str):
return jsonify({"status": "error", "message": "Failed to delete setting."}), 500
@bp.route("/api/email-settings", methods=["GET"])
@auth.login_required
def get_email_settings_api():
"""Return the current email settings with field metadata."""
try:
data = load_email_settings()
return jsonify({
"status": "ok",
"settings": data,
"schema": EMAIL_SETTINGS_DEFINITIONS,
})
except Exception as exc: # pragma: no cover - logged and surfaced to client
logging.exception("Failed to load email settings: %s", exc)
return jsonify({
"status": "error",
"message": "Failed to load email settings."
}), 500
@bp.route("/api/email-settings", methods=["PUT"])
@auth.login_required
def update_email_settings_api():
"""Validate and persist email settings updates."""
try:
payload = request.get_json(silent=True) or {}
errors = validate_email_settings(payload)
if errors:
return jsonify({
"status": "error",
"message": "Email settings validation failed.",
"errors": errors,
}), 400
updated = persist_email_settings(payload)
return jsonify({
"status": "ok",
"message": "Email settings updated successfully.",
"settings": updated,
})
except Exception as exc: # pragma: no cover - logged and surfaced to client
logging.exception("Failed to update email settings: %s", exc)
return jsonify({
"status": "error",
"message": "Failed to update email settings."
}), 500
@bp.route("/api/email-templates", methods=["GET"])
@auth.login_required
def list_email_templates_api():
"""Return metadata for editable email templates."""
try:
templates = list_email_templates()
return jsonify({"status": "ok", "templates": templates})
except Exception as exc: # pragma: no cover
logging.exception("Failed to list email templates: %s", exc)
return jsonify({
"status": "error",
"message": "Failed to load email templates."
}), 500
@bp.route("/api/email-templates/<template_id>", methods=["GET"])
@auth.login_required
def get_email_template_api(template_id: str):
"""Return the requested email template."""
try:
template = load_email_template(template_id)
return jsonify({"status": "ok", "template": template})
except KeyError:
return jsonify({
"status": "error",
"message": "Email template not found."
}), 404
except Exception as exc: # pragma: no cover
logging.exception(
"Failed to load email template %s: %s", template_id, exc)
return jsonify({
"status": "error",
"message": "Failed to load email template."
}), 500
@bp.route("/api/email-templates/<template_id>", methods=["PUT"])
@auth.login_required
def update_email_template_api(template_id: str):
"""Persist updates to an email template."""
try:
payload = request.get_json(silent=True) or {}
content = payload.get("content", "")
if not isinstance(content, str) or not content.strip():
return jsonify({
"status": "error",
"message": "Template content is required.",
}), 400
updated = persist_email_template(template_id, content)
return jsonify({
"status": "ok",
"template": updated,
})
except KeyError:
return jsonify({
"status": "error",
"message": "Email template not found."
}), 404
except Exception as exc: # pragma: no cover
logging.exception(
"Failed to update email template %s: %s", template_id, exc)
return jsonify({
"status": "error",
"message": "Failed to update email template."
}), 500
@bp.route("/api/newsletter", methods=["GET"])
@auth.login_required
def get_subscribers_api():