diff --git a/hc/accounts/views.py b/hc/accounts/views.py index cee590ba..69ac102b 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -8,9 +8,11 @@ from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.http import HttpResponseBadRequest from django.shortcuts import redirect, render +from django.template import loader from hc.accounts.forms import EmailForm from hc.api.models import Check +from hc.lib.emails import send def _make_user(email): @@ -28,6 +30,18 @@ def _associate_demo_check(request, user): check.save() +def _send_login_link(user): + token = str(uuid.uuid4()) + user.set_password(token) + user.save() + + login_link = reverse("hc-check-token", args=[user.username, token]) + login_link = settings.SITE_ROOT + login_link + ctx = {"login_link": login_link} + + send(user.email, "emails/login", ctx) + + def login(request): if request.method == 'POST': form = EmailForm(request.POST) @@ -43,16 +57,7 @@ def login(request): if user.is_staff: return HttpResponseBadRequest() - token = str(uuid.uuid4()) - user.set_password(token) - user.save() - - login_link = reverse("hc-check-token", args=[user.username, token]) - login_link = settings.SITE_ROOT + login_link - body = "login link: %s" % login_link - - send_mail('Log In', body, settings.DEFAULT_FROM_EMAIL, [email], - fail_silently=False) + _send_login_link(user) return redirect("hc-login-link-sent") diff --git a/hc/api/admin.py b/hc/api/admin.py index c3fc7e73..00f77a7d 100644 --- a/hc/api/admin.py +++ b/hc/api/admin.py @@ -6,3 +6,12 @@ from hc.api.models import Check @admin.register(Check) class ChecksAdmin(admin.ModelAdmin): list_display = ("id", "code", "user", "last_ping") + actions = ["send_alert"] + + def send_alert(self, request, qs): + for check in qs: + check.send_alert() + + self.message_user(request, "%d alert(s) sent" % qs.count()) + + send_alert.short_description = "Send Alert" diff --git a/hc/api/management/commands/sendalerts.py b/hc/api/management/commands/sendalerts.py index bb6f75cb..066578ab 100644 --- a/hc/api/management/commands/sendalerts.py +++ b/hc/api/management/commands/sendalerts.py @@ -5,7 +5,6 @@ from django.core.management.base import BaseCommand from django.utils import timezone from hc.api.models import Check -from hc.lib.emails import send_status_notification def _log(message): @@ -14,7 +13,7 @@ def _log(message): class Command(BaseCommand): - help = 'Ensures triggers exist in database' + help = 'Sends UP/DOWN email alerts' def handle(self, *args, **options): @@ -29,7 +28,7 @@ class Command(BaseCommand): check.status = "down" _log("\nSending email about going down for %s\n" % check.code) - send_status_notification(check) + check.send_alert() ticks = 0 # Save status after the notification is sent @@ -44,7 +43,7 @@ class Command(BaseCommand): check.status = "up" _log("\nSending email about going up for %s\n" % check.code) - send_status_notification(check) + check.send_alert() ticks = 0 # Save status after the notification is sent diff --git a/hc/api/models.py b/hc/api/models.py index dadb3241..679aa16b 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -5,8 +5,22 @@ from django.conf import settings from django.contrib.auth.models import User from django.db import models +from hc.lib.emails import send + STATUSES = (("up", "Up"), ("down", "Down"), ("new", "New")) DEFAULT_TIMEOUT = td(days=1) +TIMEOUT_CHOICES = ( + ("15 minutes", td(minutes=15)), + ("30 minutes", td(minutes=30)), + ("1 hour", td(hours=1)), + ("3 hours", td(hours=3)), + ("6 hours", td(hours=6)), + ("12 hours", td(hours=12)), + ("1 day", td(days=1)), + ("2 days", td(days=2)), + ("3 days", td(days=3)), + ("1 week", td(weeks=1)) +) class Check(models.Model): @@ -21,3 +35,15 @@ class Check(models.Model): def url(self): return settings.PING_ENDPOINT + str(self.code) + + def send_alert(self): + ctx = { + "timeout_choices": TIMEOUT_CHOICES, + "check": self, + "checks": self.user.check_set.order_by("created") + } + + if self.status in ("up", "down"): + send(self.user.email, "emails/alert", ctx) + else: + raise NotImplemented("Unexpected status: %s" % self.status) diff --git a/hc/front/forms.py b/hc/front/forms.py index 077bf035..1450c2d8 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -1,19 +1,6 @@ -from datetime import timedelta as td - from django import forms -TIMEOUT_CHOICES = ( - ("15 minutes", td(minutes=15)), - ("30 minutes", td(minutes=30)), - ("1 hour", td(hours=1)), - ("3 hours", td(hours=3)), - ("6 hours", td(hours=6)), - ("12 hours", td(hours=12)), - ("1 day", td(days=1)), - ("2 days", td(days=2)), - ("3 days", td(days=3)), - ("1 week", td(weeks=1)) -) +from hc.api.models import TIMEOUT_CHOICES class TimeoutForm(forms.Form): diff --git a/hc/lib/emails.py b/hc/lib/emails.py index 70d9792e..092c0149 100644 --- a/hc/lib/emails.py +++ b/hc/lib/emails.py @@ -1,16 +1,18 @@ from django.conf import settings from django.core.mail import send_mail +from django.template.loader import render_to_string -def send_status_notification(check): - if check.status == "down": - subject = "Alert DOWN" - body = "Hi, the check %s has gone down" % check.code - elif check.status == "up": - subject = "Alert UP" - body = "Hi, the check %s has gone up" % check.code - else: - raise NotImplemented("Unexpected status: %s" % check.status) +def send(to, template_directory, ctx): + """ Send HTML email using Mandrill. - send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [check.user.email], - fail_silently=False) + Expect template_directory to be a path containing + - subject.txt + - body.html + + """ + + from_email = settings.DEFAULT_FROM_EMAIL + subject = render_to_string("%s/subject.txt" % template_directory, ctx) + body = render_to_string("%s/body.html" % template_directory, ctx) + send_mail(subject, "", from_email, [to], html_message=body) diff --git a/hc/settings.py b/hc/settings.py index 9331756f..c672d526 100644 --- a/hc/settings.py +++ b/hc/settings.py @@ -30,6 +30,7 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'djrill', 'hc.accounts', 'hc.api', @@ -92,13 +93,7 @@ STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] STATIC_ROOT = os.path.join(BASE_DIR, 'static-collected') -# AWS -EMAIL_BACKEND = 'django_ses_backend.SESBackend' -AWS_SES_ACCESS_KEY_ID = "---" -AWS_SES_SECRET_ACCESS_KEY = "---" -AWS_SES_REGION_NAME = 'us-east-1' -AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com' - +EMAIL_BACKEND = "djrill.mail.backends.djrill.DjrillBackend" try: from local_settings import * diff --git a/requirements.txt b/requirements.txt index d79e7077..6f8d1e62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ Django==1.8.2 django-ses psycopg2==2.6 django-ses-backend +djrill pygments \ No newline at end of file diff --git a/templates/emails/alert/body.html b/templates/emails/alert/body.html new file mode 100644 index 00000000..0908ec12 --- /dev/null +++ b/templates/emails/alert/body.html @@ -0,0 +1,51 @@ +{% load humanize %} + +

Hello,

+

This is a notification sent by healthchecks.io

+

The check "{{ check.name }}" has gone {{ check.status }}.

+

Here is a summary of all your checks:

+ + + + + + + + + + {% for check in checks %} + + + + + + + + {% endfor %} +
NameURLFrequencyLast Ping
+ {% if check.status == "new" %} + + {% elif now < check.alert_after %} + + {% else %} + + {% endif %} + + {{ check.name }} + + {{ check.url }} + + {% for label, value in timeout_choices %} + {% if check.timeout == value %} + {{ label }} + {% endif %} + {% endfor %} + + {% if check.last_ping %} + {{ check.last_ping|naturaltime }} + {% else %} + Never + {% endif %} +
+ + diff --git a/templates/emails/alert/subject.txt b/templates/emails/alert/subject.txt new file mode 100644 index 00000000..518bacc7 --- /dev/null +++ b/templates/emails/alert/subject.txt @@ -0,0 +1,2 @@ +{{ check.name }} is {{ check.status }} + diff --git a/templates/emails/login/body.html b/templates/emails/login/body.html new file mode 100644 index 00000000..eb14b04d --- /dev/null +++ b/templates/emails/login/body.html @@ -0,0 +1,5 @@ +

Hello from healthchecks.io!

+ +

Here's a link to log yourself in:

+

{{ login_link }}

+ diff --git a/templates/emails/login/subject.txt b/templates/emails/login/subject.txt new file mode 100644 index 00000000..701a0b78 --- /dev/null +++ b/templates/emails/login/subject.txt @@ -0,0 +1 @@ +Log in to healthchecks.io