Browse Source

Add the "Email Settings..." dialog and the "Subject Must Contain" setting

pull/211/head
Pēteris Caune 6 years ago
parent
commit
5edcd42033
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
13 changed files with 124 additions and 12 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +14
    -1
      hc/api/management/commands/smtpd.py
  3. +18
    -0
      hc/api/migrations/0053_check_subject.py
  4. +5
    -2
      hc/api/models.py
  5. +4
    -0
      hc/front/forms.py
  6. +1
    -0
      hc/front/urls.py
  7. +13
    -1
      hc/front/views.py
  8. +5
    -0
      static/css/base.css
  9. +16
    -8
      templates/front/details.html
  10. +2
    -0
      templates/front/details_events.html
  11. +41
    -0
      templates/front/email_settings_modal.html
  12. +2
    -0
      templates/front/log.html
  13. +2
    -0
      templates/front/ping_details.html

+ 1
- 0
CHANGELOG.md View File

@ -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 uniqueness constraint to Check.code
- Database schema: add Ping.kind field - Database schema: add Ping.kind field
- Database schema: remove Ping.start and Ping.fail fields - Database schema: remove Ping.start and Ping.fail fields
- Add "Email Settings..." dialog and "Subject Must Contain" setting
## 1.4.0 - 2018-12-25 ## 1.4.0 - 2018-12-25


+ 14
- 1
hc/api/management/commands/smtpd.py View File

@ -1,4 +1,5 @@
import asyncore import asyncore
import email
import re import re
from smtpd import SMTPServer from smtpd import SMTPServer
@ -17,6 +18,11 @@ class Listener(SMTPServer):
to_parts = rcpttos[0].split("@") to_parts = rcpttos[0].split("@")
code = to_parts[0] code = to_parts[0]
try:
data = data.decode()
except UnicodeError:
data = "[binary data]"
if not RE_UUID.match(code): if not RE_UUID.match(code):
self.stdout.write("Not an UUID: %s" % code) self.stdout.write("Not an UUID: %s" % code)
return return
@ -27,8 +33,15 @@ class Listener(SMTPServer):
self.stdout.write("Check not found: %s" % code) self.stdout.write("Check not found: %s" % code)
return 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 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) self.stdout.write("Processed ping for %s" % code)


+ 18
- 0
hc/api/migrations/0053_check_subject.py View File

@ -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),
),
]

+ 5
- 2
hc/api/models.py View File

@ -74,6 +74,7 @@ class Check(models.Model):
grace = models.DurationField(default=DEFAULT_GRACE) grace = models.DurationField(default=DEFAULT_GRACE)
schedule = models.CharField(max_length=100, default="* * * * *") schedule = models.CharField(max_length=100, default="* * * * *")
tz = models.CharField(max_length=36, default="UTC") tz = models.CharField(max_length=36, default="UTC")
subject = models.CharField(max_length=100, blank=True)
n_pings = models.IntegerField(default=0) n_pings = models.IntegerField(default=0)
last_ping = models.DateTimeField(null=True, blank=True) last_ping = models.DateTimeField(null=True, blank=True)
last_start = 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): def ping(self, remote_addr, scheme, method, ua, body, action):
if action == "start": if action == "start":
self.last_start = timezone.now() self.last_start = timezone.now()
# DOn't update "last_ping" field.
# Don't update "last_ping" field.
elif action == "ign":
pass
else: else:
self.last_start = None self.last_start = None
self.last_ping = timezone.now() self.last_ping = timezone.now()
@ -230,7 +233,7 @@ class Check(models.Model):
ping = Ping(owner=self) ping = Ping(owner=self)
ping.n = self.n_pings ping.n = self.n_pings
if action in ("start", "fail"):
if action in ("start", "fail", "ign"):
ping.kind = action ping.kind = action
ping.remote_addr = remote_addr ping.remote_addr = remote_addr


+ 4
- 0
hc/front/forms.py View File

@ -24,6 +24,10 @@ class NameTagsForm(forms.Form):
return " ".join(result) return " ".join(result)
class EmailSettingsForm(forms.Form):
subject = forms.CharField(max_length=100)
class TimeoutForm(forms.Form): class TimeoutForm(forms.Form):
timeout = forms.IntegerField(min_value=60, max_value=2592000) timeout = forms.IntegerField(min_value=60, max_value=2592000)
grace = forms.IntegerField(min_value=60, max_value=2592000) grace = forms.IntegerField(min_value=60, max_value=2592000)


+ 1
- 0
hc/front/urls.py View File

@ -5,6 +5,7 @@ from hc.front import views
check_urls = [ check_urls = [
path('name/', views.update_name, name="hc-update-name"), path('name/', views.update_name, name="hc-update-name"),
path('details/', views.details, name="hc-details"), 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('timeout/', views.update_timeout, name="hc-update-timeout"),
path('pause/', views.pause, name="hc-pause"), path('pause/', views.pause, name="hc-pause"),
path('remove/', views.remove_check, name="hc-remove-check"), path('remove/', views.remove_check, name="hc-remove-check"),


+ 13
- 1
hc/front/views.py View File

@ -23,7 +23,7 @@ from hc.api.transports import Telegram
from hc.front.forms import (AddWebhookForm, NameTagsForm, from hc.front.forms import (AddWebhookForm, NameTagsForm,
TimeoutForm, AddUrlForm, AddEmailForm, TimeoutForm, AddUrlForm, AddEmailForm,
AddOpsGenieForm, CronForm, AddSmsForm, AddOpsGenieForm, CronForm, AddSmsForm,
ChannelNameForm)
ChannelNameForm, EmailSettingsForm)
from hc.front.schemas import telegram_callback from hc.front.schemas import telegram_callback
from hc.front.templatetags.hc_extras import (num_down_title, down_title, from hc.front.templatetags.hc_extras import (num_down_title, down_title,
sortchecks) sortchecks)
@ -274,6 +274,18 @@ def update_name(request, code):
return redirect("hc-checks") 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 @require_POST
@login_required @login_required
def update_timeout(request, code): def update_timeout(request, code):


+ 5
- 0
static/css/base.css View File

@ -74,6 +74,11 @@ body {
border: 1px solid #117a3f; border: 1px solid #117a3f;
} }
.label-ign {
background: #e0e0e0;
color: #333;
}
.hc-dialog { .hc-dialog {
background: #FFF; background: #FFF;
padding: 2em; padding: 2em;


+ 16
- 8
templates/front/details.html View File

@ -33,7 +33,14 @@
<div> <div>
<p>Keep this check up by making HTTP requests to this URL:</p> <p>Keep this check up by making HTTP requests to this URL:</p>
<code>{{ check.url }}</code> <code>{{ check.url }}</code>
<p>Or by sending emails to this address:</p>
<p>
{% 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 %}
</p>
<code>{{ check.email }}</code> <code>{{ check.email }}</code>
<p>You can also explictly <p>You can also explictly
@ -46,17 +53,17 @@
</div> </div>
<div class="text-right"> <div class="text-right">
<button <button
data-label="Copy URL"
data-clipboard-text="{{ check.url }}"
class="btn btn-sm btn-default copy-btn">Copy URL</button>
<button
data-label="Copy Email"
data-clipboard-text="{{ check.email }}"
class="btn btn-sm btn-default copy-btn">Copy Email</button>
data-toggle="modal"
data-target="#email-settings-modal"
class="btn btn-sm btn-default">Email Settings&hellip;</button>
<button <button
data-toggle="modal" data-toggle="modal"
data-target="#show-usage-modal" data-target="#show-usage-modal"
class="btn btn-sm btn-default">Usage Examples</button> class="btn btn-sm btn-default">Usage Examples</button>
<button
data-label="Copy URL"
data-clipboard-text="{{ check.url }}"
class="btn btn-sm btn-default copy-btn">Copy URL</button>
</div> </div>
</div> </div>
@ -207,6 +214,7 @@
{% include "front/update_timeout_modal.html" %} {% include "front/update_timeout_modal.html" %}
{% include "front/show_usage_modal.html" %} {% include "front/show_usage_modal.html" %}
{% include "front/remove_check_modal.html" %} {% include "front/remove_check_modal.html" %}
{% include "front/email_settings_modal.html" %}
{% endblock %} {% endblock %}


+ 2
- 0
templates/front/details_events.html View File

@ -14,6 +14,8 @@
<span class="label label-danger">Failure</span> <span class="label label-danger">Failure</span>
{% elif event.kind == "start" %} {% elif event.kind == "start" %}
<span class="label label-start">Started</span> <span class="label label-start">Started</span>
{% elif event.kind == "ign" %}
<span class="label label-ign">Ignored</span>
{% else %} {% else %}
<span class="label label-success">OK</span> <span class="label label-success">OK</span>
{% endif %} {% endif %}


+ 41
- 0
templates/front/email_settings_modal.html View File

@ -0,0 +1,41 @@
{% load hc_extras %}
<div id="email-settings-modal" class="modal">
<div class="modal-dialog">
<form
action="{% url 'hc-email-settings' check.code %}"
class="form-horizontal"
method="post">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="update-timeout-title">Inbound Email Settings</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label for="update-name-input" class="col-sm-4 control-label">
Subject Must Contain
</label>
<div class="col-sm-7">
<input
name="subject"
type="text"
value="{{ check.subject }}"
class="input-name form-control" />
<span class="help-block">
If set, {% site_name %} will ignore emails
without this value in the Subject line.
</span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
</form>
</div>
</div>

+ 2
- 0
templates/front/log.html View File

@ -48,6 +48,8 @@
<span class="label label-danger">Failure</span> <span class="label label-danger">Failure</span>
{% elif event.kind == "start" %} {% elif event.kind == "start" %}
<span class="label label-start">Started</span> <span class="label label-start">Started</span>
{% elif event.kind == "ign" %}
<span class="label label-ign">Ignored</span>
{% else %} {% else %}
<span class="label label-success">OK</span> <span class="label label-success">OK</span>
{% endif %} {% endif %}


+ 2
- 0
templates/front/ping_details.html View File

@ -4,6 +4,8 @@
<span class="text-danger">(received via the <code>/fail</code> endpoint)</span> <span class="text-danger">(received via the <code>/fail</code> endpoint)</span>
{% elif ping.kind == "start" %} {% elif ping.kind == "start" %}
<span class="text-success">(received via the <code>/start</code> endpoint)</span> <span class="text-success">(received via the <code>/start</code> endpoint)</span>
{% elif ping.kind == "ign" %}
<span class="text-muted">(ignored)</span>
{% endif %} {% endif %}
</h3> </h3>


Loading…
Cancel
Save