feat: Add email settings management and templates functionality
- 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:
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user