Improved error handling for email support (#4862)

* Improved error handling for email support

- Prevent email sending if email not configured
- Check for tx email address before sending

(cherry picked from commit de541f811ede030ea5eb3136132731e1dafccc31)

* Update InvenTree/email.py

Co-authored-by: Matthias Mair <code@mjmair.com>

* Update InvenTree/email.py

Co-authored-by: Matthias Mair <code@mjmair.com>

* Fix location of file email.py

* Allow dummy emails in testing

* Provide default email in testing mode

* Fix to get test working

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
Oliver 2023-05-24 07:33:31 +10:00 committed by GitHub
parent 96b7845d84
commit 91d79dc3ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 59 deletions

View File

@ -2,6 +2,7 @@
"""Provides extra global data to all templates.""" """Provides extra global data to all templates."""
import InvenTree.email
import InvenTree.status import InvenTree.status
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus, from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
ReturnOrderLineStatus, ReturnOrderStatus, ReturnOrderLineStatus, ReturnOrderStatus,
@ -28,7 +29,7 @@ def health_status(request):
status = { status = {
'django_q_running': InvenTree.status.is_worker_running(), 'django_q_running': InvenTree.status.is_worker_running(),
'email_configured': InvenTree.status.is_email_configured(), 'email_configured': InvenTree.email.is_email_configured(),
} }
# The following keys are required to denote system health # The following keys are required to denote system health

View File

@ -0,0 +1,82 @@
"""Code for managing email functionality in InvenTree."""
import logging
from django.conf import settings
from django.core import mail as django_mail
import InvenTree.ready
import InvenTree.tasks
logger = logging.getLogger('inventree')
def is_email_configured():
"""Check if email backend is configured.
NOTE: This does not check if the configuration is valid!
"""
configured = True
if InvenTree.ready.isInTestMode():
return False
if InvenTree.ready.isImportingData():
return False
if not settings.EMAIL_HOST:
configured = False
# Display warning unless in test mode
if not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST is not configured")
# Display warning unless in test mode
if not settings.EMAIL_HOST_USER and not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST_USER is not configured")
# Display warning unless in test mode
if not settings.EMAIL_HOST_PASSWORD and not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST_PASSWORD is not configured")
return configured
def send_email(subject, body, recipients, from_email=None, html_message=None):
"""Send an email with the specified subject and body, to the specified recipients list."""
if type(recipients) == str:
recipients = [recipients]
import InvenTree.ready
import InvenTree.status
if InvenTree.ready.isImportingData():
# If we are importing data, don't send emails
return
if not InvenTree.email.is_email_configured() and not settings.TESTING:
# Email is not configured / enabled
return
# If a *from_email* is not specified, ensure that the default is set
if not from_email:
from_email = settings.DEFAULT_FROM_EMAIL
# If we still don't have a valid from_email, then we can't send emails
if not from_email:
if settings.TESTING:
from_email = 'from@test.com'
else:
logger.error("send_email failed: DEFAULT_FROM_EMAIL not specified")
return
InvenTree.tasks.offload_task(
django_mail.send_mail,
subject,
body,
from_email,
recipients,
fail_silently=False,
html_message=html_message
)

View File

@ -4,13 +4,13 @@
import logging import logging
from datetime import timedelta from datetime import timedelta
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_q.models import Success from django_q.models import Success
from django_q.monitor import Stat from django_q.monitor import Stat
import InvenTree.email
import InvenTree.ready import InvenTree.ready
logger = logging.getLogger("inventree") logger = logging.getLogger("inventree")
@ -41,37 +41,6 @@ def is_worker_running(**kwargs):
return results.exists() return results.exists()
def is_email_configured():
"""Check if email backend is configured.
NOTE: This does not check if the configuration is valid!
"""
configured = True
if InvenTree.ready.isInTestMode():
return False
if InvenTree.ready.isImportingData():
return False
if not settings.EMAIL_HOST:
configured = False
# Display warning unless in test mode
if not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST is not configured")
# Display warning unless in test mode
if not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST_USER is not configured")
# Display warning unless in test mode
if not settings.TESTING: # pragma: no cover
logger.debug("EMAIL_HOST_PASSWORD is not configured")
return configured
def check_system_health(**kwargs): def check_system_health(**kwargs):
"""Check that the InvenTree system is running OK. """Check that the InvenTree system is running OK.
@ -91,7 +60,7 @@ def check_system_health(**kwargs):
result = False result = False
logger.warning(_("Background worker check failed")) logger.warning(_("Background worker check failed"))
if not is_email_configured(): # pragma: no cover if not InvenTree.email.is_email_configured(): # pragma: no cover
result = False result = False
logger.warning(_("Email backend not configured")) logger.warning(_("Email backend not configured"))

View File

@ -12,7 +12,6 @@ from datetime import datetime, timedelta
from typing import Callable, List from typing import Callable, List
from django.conf import settings from django.conf import settings
from django.core import mail as django_mail
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
from django.core.management import call_command from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, connections from django.db import DEFAULT_DB_ALIAS, connections
@ -559,28 +558,6 @@ def run_backup():
record_task_success('run_backup') record_task_success('run_backup')
def send_email(subject, body, recipients, from_email=None, html_message=None):
"""Send an email with the specified subject and body, to the specified recipients list."""
if type(recipients) == str:
recipients = [recipients]
import InvenTree.ready
if InvenTree.ready.isImportingData():
# If we are importing data, don't send emails
return
offload_task(
django_mail.send_mail,
subject,
body,
from_email,
recipients,
fail_silently=False,
html_message=html_message
)
@scheduled_task(ScheduledTask.DAILY) @scheduled_task(ScheduledTask.DAILY)
def check_for_migrations(worker: bool = True): def check_for_migrations(worker: bool = True):
"""Checks if migrations are needed. """Checks if migrations are needed.

View File

@ -12,6 +12,7 @@ from allauth.account.models import EmailAddress
from plugin.events import trigger_event from plugin.events import trigger_event
import common.notifications import common.notifications
import build.models import build.models
import InvenTree.email
import InvenTree.helpers import InvenTree.helpers
import InvenTree.tasks import InvenTree.tasks
from InvenTree.status_codes import BuildStatus from InvenTree.status_codes import BuildStatus
@ -101,7 +102,7 @@ def check_build_stock(build: build.models.Build):
recipients = emails.values_list('email', flat=True) recipients = emails.values_list('email', flat=True)
InvenTree.tasks.send_email(subject, '', recipients, html_message=html_message) InvenTree.email.send_email(subject, '', recipients, html_message=html_message)
def notify_overdue_build_order(bo: build.models.Build): def notify_overdue_build_order(bo: build.models.Build):

View File

@ -7,6 +7,7 @@ import requests
from allauth.account.models import EmailAddress from allauth.account.models import EmailAddress
import common.models import common.models
import InvenTree.email
import InvenTree.helpers import InvenTree.helpers
import InvenTree.tasks import InvenTree.tasks
from plugin import InvenTreePlugin, registry from plugin import InvenTreePlugin, registry
@ -115,7 +116,7 @@ class InvenTreeCoreNotificationsPlugin(SettingsContentMixin, SettingsMixin, Inve
if instance_title: if instance_title:
subject = f'[{instance_title}] {subject}' subject = f'[{instance_title}] {subject}'
InvenTree.tasks.send_email(subject, '', targets, html_message=html_message) InvenTree.email.send_email(subject, '', targets, html_message=html_message)
return True return True