From fdba65a7f804d4aeee19f4df274e1a797f33f118 Mon Sep 17 00:00:00 2001
From: Bjarke Madsen <bjarke@nordu.net>
Date: Mon, 21 Aug 2023 14:05:00 +0200
Subject: [PATCH] Add email module with send_mail function

---
 compendium_v2/email/__init__.py | 60 +++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 compendium_v2/email/__init__.py

diff --git a/compendium_v2/email/__init__.py b/compendium_v2/email/__init__.py
new file mode 100644
index 00000000..573a6f03
--- /dev/null
+++ b/compendium_v2/email/__init__.py
@@ -0,0 +1,60 @@
+import smtplib
+import threading
+import logging
+from typing import Sequence, Union
+from sqlalchemy import select
+from flask import current_app
+from compendium_v2 import db
+from compendium_v2.db.auth_model import User, ROLES
+
+logger = logging.getLogger(__name__)
+
+
+def _send_mail(smtp_server, port, sender_email, recipients, message):
+
+    try:
+        with smtplib.SMTP(smtp_server, port) as server:
+            server.sendmail(from_addr=sender_email, to_addrs=recipients, msg=message)
+        logger.debug('Successfully sent email')
+    except Exception:
+        logger.exception('Unable to send email:')
+
+
+def send_mail(
+    contents: str,
+    subject: str = 'New user signed up for Compendium',
+    recipients: Union[str, Sequence[str]] = ''
+):
+    if not current_app.config['MAIL_ENABLE']:
+        logger.warning('No mail configuration, cannot send email.')
+        return
+
+    if not isinstance(contents, str):
+        raise ValueError('Contents must be a string.')
+
+    if not isinstance(subject, str):
+        raise ValueError('Subject must be a string.')
+
+    if not isinstance(recipients, (str, Sequence)):
+        raise ValueError('Recipients must be a string or a sequence of strings.')
+
+    excluded_admins = set(email.lower() for email in current_app.config['MAIL_EXCLUDED_ADMINS'])
+
+    admins = db.session.scalars(select(User).where(User.roles == ROLES.admin)).unique().all()
+
+    admin_emails = [admin.email for admin in admins if admin.email.lower() not in excluded_admins]
+
+    if not recipients:
+        recipients = admin_emails
+
+    subject = subject.replace('\n', ' ')
+    message = f"""Subject: {subject}\n\n{contents}"""
+
+    smtp_server = current_app.config['MAIL_SERVER']
+    port = current_app.config['MAIL_PORT']
+    sender_email = current_app.config['MAIL_SENDER_EMAIL']
+
+    # spin off a thread since this can take some time..
+    logger.debug('Sending email')
+    thread = threading.Thread(target=_send_mail, args=(smtp_server, port, sender_email, recipients, message))
+    thread.start()
-- 
GitLab