diff --git a/CHANGELOG.md b/CHANGELOG.md index 8afcc73d..a51803b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Database schema: add uniqueness constraint to Check.code - Database schema: add Ping.kind field - Database schema: remove Ping.start and Ping.fail fields +- Add "Email Settings..." dialog and "Subject Must Contain" setting ## 1.4.0 - 2018-12-25 diff --git a/hc/api/management/commands/smtpd.py b/hc/api/management/commands/smtpd.py index 2df5750c..8ac6664c 100644 --- a/hc/api/management/commands/smtpd.py +++ b/hc/api/management/commands/smtpd.py @@ -1,4 +1,5 @@ import asyncore +import email import re from smtpd import SMTPServer @@ -17,6 +18,11 @@ class Listener(SMTPServer): to_parts = rcpttos[0].split("@") code = to_parts[0] + try: + data = data.decode() + except UnicodeError: + data = "[binary data]" + if not RE_UUID.match(code): self.stdout.write("Not an UUID: %s" % code) return @@ -27,8 +33,15 @@ class Listener(SMTPServer): self.stdout.write("Check not found: %s" % code) return + action = "success" + if check.subject: + parsed = email.message_from_string(data) + received_subject = parsed.get("subject", "") + if check.subject not in received_subject: + action = "ign" + ua = "Email from %s" % mailfrom - check.ping(peer[0], "email", "", ua, data) + check.ping(peer[0], "email", "", ua, data, action) self.stdout.write("Processed ping for %s" % code) diff --git a/hc/api/migrations/0053_check_subject.py b/hc/api/migrations/0053_check_subject.py new file mode 100644 index 00000000..1e99fae3 --- /dev/null +++ b/hc/api/migrations/0053_check_subject.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.4 on 2019-01-04 12:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_auto_20190104_1122'), + ] + + operations = [ + migrations.AddField( + model_name='check', + name='subject', + field=models.CharField(blank=True, max_length=100), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 3a2185a0..60739ce2 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -74,6 +74,7 @@ class Check(models.Model): grace = models.DurationField(default=DEFAULT_GRACE) schedule = models.CharField(max_length=100, default="* * * * *") tz = models.CharField(max_length=36, default="UTC") + subject = models.CharField(max_length=100, blank=True) n_pings = models.IntegerField(default=0) last_ping = models.DateTimeField(null=True, blank=True) last_start = models.DateTimeField(null=True, blank=True) @@ -207,7 +208,9 @@ class Check(models.Model): def ping(self, remote_addr, scheme, method, ua, body, action): if action == "start": self.last_start = timezone.now() - # DOn't update "last_ping" field. + # Don't update "last_ping" field. + elif action == "ign": + pass else: self.last_start = None self.last_ping = timezone.now() @@ -230,7 +233,7 @@ class Check(models.Model): ping = Ping(owner=self) ping.n = self.n_pings - if action in ("start", "fail"): + if action in ("start", "fail", "ign"): ping.kind = action ping.remote_addr = remote_addr diff --git a/hc/front/forms.py b/hc/front/forms.py index 6afead1c..c82de82c 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -24,6 +24,10 @@ class NameTagsForm(forms.Form): return " ".join(result) +class EmailSettingsForm(forms.Form): + subject = forms.CharField(max_length=100) + + class TimeoutForm(forms.Form): timeout = forms.IntegerField(min_value=60, max_value=2592000) grace = forms.IntegerField(min_value=60, max_value=2592000) diff --git a/hc/front/urls.py b/hc/front/urls.py index 6e57b5c7..6aea4edc 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -5,6 +5,7 @@ from hc.front import views check_urls = [ path('name/', views.update_name, name="hc-update-name"), path('details/', views.details, name="hc-details"), + path('email_settings/', views.email_settings, name="hc-email-settings"), path('timeout/', views.update_timeout, name="hc-update-timeout"), path('pause/', views.pause, name="hc-pause"), path('remove/', views.remove_check, name="hc-remove-check"), diff --git a/hc/front/views.py b/hc/front/views.py index 668189b7..51a70fa7 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -23,7 +23,7 @@ from hc.api.transports import Telegram from hc.front.forms import (AddWebhookForm, NameTagsForm, TimeoutForm, AddUrlForm, AddEmailForm, AddOpsGenieForm, CronForm, AddSmsForm, - ChannelNameForm) + ChannelNameForm, EmailSettingsForm) from hc.front.schemas import telegram_callback from hc.front.templatetags.hc_extras import (num_down_title, down_title, sortchecks) @@ -274,6 +274,18 @@ def update_name(request, code): return redirect("hc-checks") +@require_POST +@login_required +def email_settings(request, code): + check = _get_check_for_user(request, code) + form = EmailSettingsForm(request.POST) + if form.is_valid(): + check.subject = form.cleaned_data["subject"] + check.save() + + return redirect("hc-details", code) + + @require_POST @login_required def update_timeout(request, code): diff --git a/static/css/base.css b/static/css/base.css index 3cc3cadf..1e22f588 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -74,6 +74,11 @@ body { border: 1px solid #117a3f; } +.label-ign { + background: #e0e0e0; + color: #333; +} + .hc-dialog { background: #FFF; padding: 2em; diff --git a/templates/front/details.html b/templates/front/details.html index 27999658..07a5bd63 100644 --- a/templates/front/details.html +++ b/templates/front/details.html @@ -33,7 +33,14 @@
Keep this check up by making HTTP requests to this URL:
{{ check.url }}
- Or by sending emails to this address:
++ {% if check.subject %} + Or by sending emails with "{{ check.subject }}" + in the subject line to this address: + {% else %} + Or by sending emails to this address: + {% endif %} +
{{ check.email }}
You can also explictly @@ -46,17 +53,17 @@
/fail
endpoint)
{% elif ping.kind == "start" %}
(received via the /start
endpoint)
+ {% elif ping.kind == "ign" %}
+ (ignored)
{% endif %}