Sending Email - GTAC-MGI/GTAC-ESP-LIMS GitHub Wiki
From L7:
We don't have an SMTP server running on the ESP server by default, but you can configure the notifications API to send emails. Below you will find the configuration for the Notifications emails and the function that go into that.
I have developed a custom email integration for a customer using Jinja and data accessors (similar to our BarTender pipeline) to template out emails with ESP expressions. You might also consider commercial services like SendGrid which other customers have used in the past.
There are 2 parts to smtp in the esp configuration. The plaintext config, and the masked secrets:# plaintext config - left hand side
"smtp": {
"server": "localhost",
"ssl": false,
"tls": true
},
masked secrets - right hand side
"email": {
"smtp_password": "XXXXXXX",
"smtp_user": "XXXXXXX"
},
There is also a connection to the Notifications API
"login_failures_notify": {
"email_body": "Username {} has failed login {} times.",
"email_recipients": [],
"email_sender": "admin@localhost",
"email_subject": "WARNING: Potential brute force attack",
"enabled": false,
"failure_threshold": 6,
"notification_json": {
"msg": {
"body": {
"text": "Username {} has failed login {} times."
},
"title": {
"text": "WARNING: Potential brute force attack"
}
},
"msg_type": "System",
"severity": "info"
},
"notification_recipients": {
"roles": [],
"users": []
},
"notify_method": "email"
},
The notifications
configuration has similar settings, and looking at the code that may be the configuration points you want to use.
On the backend we have this function at:
from lab.queue.tasks.sharedTasks import send_email
Out of the box, this can be used to send emails about multiple failed logins for a user. (Security email)
You could also use this in an invokable to write your own email automations.
# lab7.queue.tasks.sharedTasks
@celery.task
@traced_function_l7(filename="queue/tasks/sharedTasks.py", classname=None)
@resource_op
def send_email(data: dict, agent, session=None, session_data=None) -> dict:
config, secrets = read_notification_config(session)
email_section = config.get('email', {'enabled': False})
if not email_section['enabled'] or email_section['enabled'].lower() != 'true':
return {'status': 'Failure', 'error': 'Email service not enabled'}
if not data:
raise ValueError('No email data provided!')
try:
subject = data['subject']
except KeyError:
raise ValueError('Missing Subject field')
try:
body = data['body']
except KeyError:
raise ValueError('Missing Body field')
try:
recipients = data['recipients']
except KeyError:
raise ValueError('Missing Recipients field')
if not isinstance(recipients, list):
raise ValueError('Recipients is not a list')
try:
sender = data['sender']
except KeyError:
raise ValueError('Missing Sender field')
smtp_server = email_section.get('server', {})
smtp_user = secrets.get('smtp_user', '')
smtp_pwd = secrets.get('smtp_password', '')
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = sender
msg.set_content(body)
context = ssl.create_default_context()
is_ssl = email_section.get('ssl', 'false').lower() == 'true'
is_tls = email_section.get('tls', 'false').lower() == 'true'
if is_tls and is_ssl:
raise ValueError('Both TLS and SSL cannot be enabled')
if is_ssl:
s = smtplib.SMTP_SSL(smtp_server, port=email_section.get('ssl_port', 465), context=context)
else:
smtp_port = email_section.get('tls_port', 587) if is_tls else email_section.get('smtp_port', 25)
s = smtplib.SMTP(smtp_server, port=smtp_port)
if is_tls:
s.starttls(context=context)
s.ehlo()
if smtp_user:
s.login(smtp_user, smtp_pwd)
for recipient in recipients:
msg['To'] = recipient
s.send_message(msg)
del msg['To']
s.quit()
return {'status': 'Sent'}
The associated client-lims-api.js
helper function on the frontend:
sendEmailNotification: async function (subject, body, recipients = {
roles: [],
workgroups: [],
users: [],
emails: []
}, sender) {
try {
const recipientsEmails = await NotificationService.recipientsEmails(recipients)
if (!recipientsEmails.length) {
this.activeComponent.$toast({
status: 'info',
title: 'Email notification',
description: 'Recipients not found. Notification has not been sent.'
})
return
}
const emailNotification = new EmailNotification(subject, body, recipientsEmails, sender)
await NotificationService.emailNotification(emailNotification)
this.activeComponent.$toast({
status: 'success',
title: 'Email notification',
description: 'Email notification has been sent successfully.'
})
} catch (error) {
$logger.error(error)
this.activeComponent.$toast({
status: 'error',
title: 'Email notification',
description: 'An error occurred.'
})
}
},
async emailNotification(emailNotification) {
const payload = EmailNotification.toAPI(emailNotification)
const { data, status } = await httpClient.post(`/notification/email`, payload) // this endpoint lets you send emails
return { data, status }
}