Browse Source

Implement alternative ping URLs, WIP

pull/563/head
Pēteris Caune 3 years ago
parent
commit
5b9008e321
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
20 changed files with 182 additions and 18 deletions
  1. +23
    -0
      hc/accounts/migrations/0045_auto_20210908_1257.py
  2. +11
    -2
      hc/accounts/models.py
  3. +1
    -0
      hc/accounts/tests/test_signup.py
  4. +2
    -1
      hc/accounts/views.py
  5. +22
    -0
      hc/api/migrations/0079_auto_20210907_0918.py
  6. +19
    -0
      hc/api/migrations/0080_fill_slug.py
  7. +25
    -2
      hc/api/models.py
  8. +2
    -0
      hc/api/tests/test_create_check.py
  9. +5
    -3
      hc/api/tests/test_get_check.py
  10. +1
    -0
      hc/api/tests/test_update_check.py
  11. +1
    -0
      hc/api/urls.py
  12. +6
    -1
      hc/api/views.py
  13. +2
    -0
      hc/front/tests/test_copy.py
  14. +1
    -0
      hc/front/tests/test_update_name.py
  15. +6
    -2
      hc/front/views.py
  16. +10
    -2
      static/css/my_checks_desktop.css
  17. +10
    -2
      templates/accounts/project.html
  18. +10
    -0
      templates/docs/api.html
  19. +10
    -0
      templates/docs/api.md
  20. +15
    -3
      templates/front/my_checks_desktop.html

+ 23
- 0
hc/accounts/migrations/0045_auto_20210908_1257.py View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.6 on 2021-09-08 12:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0044_auto_20210730_0942'),
]
operations = [
migrations.AddField(
model_name='project',
name='ping_key',
field=models.CharField(blank=True, max_length=128, null=True, unique=True),
),
migrations.AddField(
model_name='project',
name='show_slugs',
field=models.BooleanField(default=False),
),
]

+ 11
- 2
hc/accounts/models.py View File

@ -322,6 +322,8 @@ class Project(models.Model):
api_key = models.CharField(max_length=128, blank=True, db_index=True) api_key = models.CharField(max_length=128, blank=True, db_index=True)
api_key_readonly = models.CharField(max_length=128, blank=True, db_index=True) api_key_readonly = models.CharField(max_length=128, blank=True, db_index=True)
badge_key = models.CharField(max_length=150, unique=True) badge_key = models.CharField(max_length=150, unique=True)
ping_key = models.CharField(max_length=128, blank=True, null=True, unique=True)
show_slugs = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return self.name or self.owner.email return self.name or self.owner.email
@ -337,8 +339,15 @@ class Project(models.Model):
return self.owner_profile.num_checks_available() return self.owner_profile.num_checks_available()
def set_api_keys(self): def set_api_keys(self):
self.api_key = token_urlsafe(nbytes=24)
self.api_key_readonly = token_urlsafe(nbytes=24)
def pick(nbytes=24):
while True:
candidate = token_urlsafe(nbytes)
if candidate[0] not in "-_" and candidate[-1] not in "-_":
return candidate
self.api_key = pick()
self.api_key_readonly = pick()
self.ping_key = pick(16)
self.save() self.save()
def invite_suggestions(self): def invite_suggestions(self):


+ 1
- 0
hc/accounts/tests/test_signup.py View File

@ -39,6 +39,7 @@ class SignupTestCase(TestCase):
# And check should be associated with the new user # And check should be associated with the new user
check = Check.objects.get() check = Check.objects.get()
self.assertEqual(check.name, "My First Check") self.assertEqual(check.name, "My First Check")
self.assertEqual(check.slug, "my-first-check")
self.assertEqual(check.project, project) self.assertEqual(check.project, project)
# A channel should have been created # A channel should have been created


+ 2
- 1
hc/accounts/views.py View File

@ -79,7 +79,7 @@ def _make_user(email, tz=None, with_project=True):
project.save() project.save()
check = Check(project=project) check = Check(project=project)
check.name = "My First Check"
check.set_name_slug("My First Check")
check.save() check.save()
channel = Channel(project=project) channel = Channel(project=project)
@ -336,6 +336,7 @@ def project(request, code):
project.api_key = "" project.api_key = ""
project.api_key_readonly = "" project.api_key_readonly = ""
project.ping_key = None
project.save() project.save()
ctx["api_keys_revoked"] = True ctx["api_keys_revoked"] = True


+ 22
- 0
hc/api/migrations/0079_auto_20210907_0918.py View File

@ -0,0 +1,22 @@
# Generated by Django 3.2.6 on 2021-09-07 09:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0078_sms_values'),
]
operations = [
migrations.AddField(
model_name='check',
name='slug',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddIndex(
model_name='check',
index=models.Index(fields=['project_id', 'slug'], name='api_check_project_slug'),
),
]

+ 19
- 0
hc/api/migrations/0080_fill_slug.py View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.6 on 2021-09-07 09:19
from django.db import migrations
from django.utils.text import slugify
def fill_slug(apps, schema_editor):
Check = apps.get_model("api", "CHeck")
for c in Check.objects.exclude(name="").only("name"):
Check.objects.filter(id=c.id).update(slug=slugify(c.name))
class Migration(migrations.Migration):
dependencies = [
("api", "0079_auto_20210907_0918"),
]
operations = [migrations.RunPython(fill_slug, migrations.RunPython.noop)]

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

@ -12,6 +12,7 @@ from django.core.signing import TimestampSigner
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.text import slugify
from hc.accounts.models import Project from hc.accounts.models import Project
from hc.api import transports from hc.api import transports
from hc.lib import emails from hc.lib import emails
@ -68,6 +69,7 @@ def isostring(dt):
class Check(models.Model): class Check(models.Model):
name = models.CharField(max_length=100, blank=True) name = models.CharField(max_length=100, blank=True)
slug = models.CharField(max_length=100, blank=True)
tags = models.CharField(max_length=500, blank=True) tags = models.CharField(max_length=500, blank=True)
code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) code = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
desc = models.TextField(blank=True) desc = models.TextField(blank=True)
@ -100,7 +102,8 @@ class Check(models.Model):
fields=["alert_after"], fields=["alert_after"],
name="api_check_aa_not_down", name="api_check_aa_not_down",
condition=~models.Q(status="down"), condition=~models.Q(status="down"),
)
),
models.Index(fields=["project_id", "slug"], name="api_check_project_slug"),
] ]
def __str__(self): def __str__(self):
@ -112,8 +115,23 @@ class Check(models.Model):
return str(self.code) return str(self.code)
def relative_url(self):
if self.project.show_slugs:
if not self.slug:
return None
key = self.project.ping_key
# If ping_key is not set, use dummy placeholder
if key is None:
key = "{ping_key}"
return key + "/" + self.slug
return str(self.code)
def url(self): def url(self):
return settings.PING_ENDPOINT + str(self.code)
s = self.relative_url()
if s:
return settings.PING_ENDPOINT + s
def details_url(self): def details_url(self):
return settings.SITE_ROOT + reverse("hc-details", args=[self.code]) return settings.SITE_ROOT + reverse("hc-details", args=[self.code])
@ -128,6 +146,10 @@ class Check(models.Model):
if self.last_duration and self.last_duration < MAX_DELTA: if self.last_duration and self.last_duration < MAX_DELTA:
return self.last_duration return self.last_duration
def set_name_slug(self, name):
self.name = name
self.slug = slugify(name)
def get_grace_start(self, with_started=True): def get_grace_start(self, with_started=True):
""" Return the datetime when the grace period starts. """ Return the datetime when the grace period starts.
@ -224,6 +246,7 @@ class Check(models.Model):
result = { result = {
"name": self.name, "name": self.name,
"slug": self.slug,
"tags": self.tags, "tags": self.tags,
"desc": self.desc, "desc": self.desc,
"grace": int(self.grace.total_seconds()), "grace": int(self.grace.total_seconds()),


+ 2
- 0
hc/api/tests/test_create_check.py View File

@ -28,6 +28,7 @@ class CreateCheckTestCase(BaseTestCase):
doc = r.json() doc = r.json()
assert "ping_url" in doc assert "ping_url" in doc
self.assertEqual(doc["name"], "Foo") self.assertEqual(doc["name"], "Foo")
self.assertEqual(doc["slug"], "foo")
self.assertEqual(doc["tags"], "bar,baz") self.assertEqual(doc["tags"], "bar,baz")
self.assertEqual(doc["last_ping"], None) self.assertEqual(doc["last_ping"], None)
self.assertEqual(doc["n_pings"], 0) self.assertEqual(doc["n_pings"], 0)
@ -38,6 +39,7 @@ class CreateCheckTestCase(BaseTestCase):
check = Check.objects.get() check = Check.objects.get()
self.assertEqual(check.name, "Foo") self.assertEqual(check.name, "Foo")
self.assertEqual(check.slug, "foo")
self.assertEqual(check.tags, "bar,baz") self.assertEqual(check.tags, "bar,baz")
self.assertEqual(check.methods, "") self.assertEqual(check.methods, "")
self.assertEqual(check.timeout.total_seconds(), 3600) self.assertEqual(check.timeout.total_seconds(), 3600)


+ 5
- 3
hc/api/tests/test_get_check.py View File

@ -11,7 +11,8 @@ class GetCheckTestCase(BaseTestCase):
self.now = now().replace(microsecond=0) self.now = now().replace(microsecond=0)
self.a1 = Check(project=self.project, name="Alice 1")
self.a1 = Check(project=self.project)
self.a1.set_name_slug("Alice 1")
self.a1.timeout = td(seconds=3600) self.a1.timeout = td(seconds=3600)
self.a1.grace = td(seconds=900) self.a1.grace = td(seconds=900)
self.a1.n_pings = 0 self.a1.n_pings = 0
@ -33,8 +34,9 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(r["Access-Control-Allow-Origin"], "*") self.assertEqual(r["Access-Control-Allow-Origin"], "*")
doc = r.json() doc = r.json()
self.assertEqual(len(doc), 15)
self.assertEqual(len(doc), 16)
self.assertEqual(doc["slug"], "alice-1")
self.assertEqual(doc["timeout"], 3600) self.assertEqual(doc["timeout"], 3600)
self.assertEqual(doc["grace"], 900) self.assertEqual(doc["grace"], 900)
self.assertEqual(doc["ping_url"], self.a1.url()) self.assertEqual(doc["ping_url"], self.a1.url())
@ -61,7 +63,7 @@ class GetCheckTestCase(BaseTestCase):
self.assertEqual(r["Access-Control-Allow-Origin"], "*") self.assertEqual(r["Access-Control-Allow-Origin"], "*")
doc = r.json() doc = r.json()
self.assertEqual(len(doc), 15)
self.assertEqual(len(doc), 16)
self.assertEqual(doc["timeout"], 3600) self.assertEqual(doc["timeout"], 3600)
self.assertEqual(doc["grace"], 900) self.assertEqual(doc["grace"], 900)


+ 1
- 0
hc/api/tests/test_update_check.py View File

@ -38,6 +38,7 @@ class UpdateCheckTestCase(BaseTestCase):
doc = r.json() doc = r.json()
assert "ping_url" in doc assert "ping_url" in doc
self.assertEqual(doc["name"], "Foo") self.assertEqual(doc["name"], "Foo")
self.assertEqual(doc["slug"], "foo")
self.assertEqual(doc["tags"], "bar,baz") self.assertEqual(doc["tags"], "bar,baz")
self.assertEqual(doc["desc"], "My description") self.assertEqual(doc["desc"], "My description")
self.assertEqual(doc["n_pings"], 0) self.assertEqual(doc["n_pings"], 0)


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

@ -33,6 +33,7 @@ urlpatterns = [
path("ping/<uuid:code>/fail", views.ping, {"action": "fail"}, name="hc-fail"), path("ping/<uuid:code>/fail", views.ping, {"action": "fail"}, name="hc-fail"),
path("ping/<uuid:code>/start", views.ping, {"action": "start"}, name="hc-start"), path("ping/<uuid:code>/start", views.ping, {"action": "start"}, name="hc-start"),
path("ping/<uuid:code>/<int:exitstatus>", views.ping), path("ping/<uuid:code>/<int:exitstatus>", views.ping),
path("ping/<slug:ping_key>/<slug:slug>", views.ping_by_slug),
path("api/v1/checks/", views.checks), path("api/v1/checks/", views.checks),
path("api/v1/checks/<uuid:code>", views.single, name="hc-api-single"), path("api/v1/checks/<uuid:code>", views.single, name="hc-api-single"),
path("api/v1/checks/<sha1:unique_key>", views.get_check_by_unique_key), path("api/v1/checks/<sha1:unique_key>", views.get_check_by_unique_key),


+ 6
- 1
hc/api/views.py View File

@ -55,6 +55,11 @@ def ping(request, code, action="success", exitstatus=None):
return response return response
def ping_by_slug(request, ping_key, slug, action="success", exitstatus=None):
check = get_object_or_404(Check, slug=slug, project__ping_key=ping_key)
return ping(request, check.code, action, exitstatus)
def _lookup(project, spec): def _lookup(project, spec):
unique_fields = spec.get("unique", []) unique_fields = spec.get("unique", [])
if unique_fields: if unique_fields:
@ -108,7 +113,7 @@ def _update(check, spec):
need_save = True need_save = True
if "name" in spec and check.name != spec["name"]: if "name" in spec and check.name != spec["name"]:
check.name = spec["name"]
check.set_name_slug(spec["name"])
need_save = True need_save = True
if "tags" in spec and check.tags != spec["tags"]: if "tags" in spec and check.tags != spec["tags"]:


+ 2
- 0
hc/front/tests/test_copy.py View File

@ -7,6 +7,7 @@ class CopyCheckTestCase(BaseTestCase):
super().setUp() super().setUp()
self.check = Check(project=self.project) self.check = Check(project=self.project)
self.check.name = "Foo" self.check.name = "Foo"
self.check.slug = "foo"
self.check.subject = "success-keyword" self.check.subject = "success-keyword"
self.check.subject_fail = "failure-keyword" self.check.subject_fail = "failure-keyword"
self.check.methods = "POST" self.check.methods = "POST"
@ -21,6 +22,7 @@ class CopyCheckTestCase(BaseTestCase):
self.assertContains(r, "This is a brand new check") self.assertContains(r, "This is a brand new check")
copy = Check.objects.get(name="Foo (copy)") copy = Check.objects.get(name="Foo (copy)")
self.assertEqual(copy.slug, "foo-copy")
self.assertEqual(copy.subject, "success-keyword") self.assertEqual(copy.subject, "success-keyword")
self.assertEqual(copy.subject_fail, "failure-keyword") self.assertEqual(copy.subject_fail, "failure-keyword")
self.assertEqual(copy.methods, "POST") self.assertEqual(copy.methods, "POST")


+ 1
- 0
hc/front/tests/test_update_name.py View File

@ -17,6 +17,7 @@ class UpdateNameTestCase(BaseTestCase):
self.check.refresh_from_db() self.check.refresh_from_db()
self.assertEqual(self.check.name, "Alice Was Here") self.assertEqual(self.check.name, "Alice Was Here")
self.assertEqual(self.check.slug, "alice-was-here")
def test_team_access_works(self): def test_team_access_works(self):
payload = {"name": "Bob Was Here"} payload = {"name": "Bob Was Here"}


+ 6
- 2
hc/front/views.py View File

@ -174,6 +174,10 @@ def my_checks(request, code):
request.profile.sort = request.GET["sort"] request.profile.sort = request.GET["sort"]
request.profile.save() request.profile.save()
if request.GET.get("urls") in ("uuid", "slug") and rw:
project.show_slugs = request.GET["urls"] == "slug"
project.save()
if request.session.get("last_project_id") != project.id: if request.session.get("last_project_id") != project.id:
request.session["last_project_id"] = project.id request.session["last_project_id"] = project.id
@ -402,7 +406,7 @@ def update_name(request, code):
form = forms.NameTagsForm(request.POST) form = forms.NameTagsForm(request.POST)
if form.is_valid(): if form.is_valid():
check.name = form.cleaned_data["name"]
check.set_name_slug(form.cleaned_data["name"])
check.tags = form.cleaned_data["tags"] check.tags = form.cleaned_data["tags"]
check.desc = form.cleaned_data["desc"] check.desc = form.cleaned_data["desc"]
check.save() check.save()
@ -694,7 +698,7 @@ def copy(request, code):
new_name = check.name[:90] + "... (copy)" new_name = check.name[:90] + "... (copy)"
copied = Check(project=check.project) copied = Check(project=check.project)
copied.name = new_name
copied.set_name_slug(new_name)
copied.desc, copied.tags = check.desc, check.tags copied.desc, copied.tags = check.desc, check.tags
copied.subject, copied.subject_fail = check.subject, check.subject_fail copied.subject, copied.subject_fail = check.subject, check.subject_fail
copied.methods = check.methods copied.methods = check.methods


+ 10
- 2
static/css/my_checks_desktop.css View File

@ -6,7 +6,7 @@
background-color: var(--table-bg-hover); background-color: var(--table-bg-hover);
} }
.my-checks-name.unnamed {
.my-checks-name.unnamed, .url span.unavailable {
color: var(--text-muted); color: var(--text-muted);
font-style: italic; font-style: italic;
} }
@ -132,7 +132,7 @@ tr:hover .copy-link {
white-space: nowrap; white-space: nowrap;
} }
#checks-table .btn {
#checks-table td .btn {
border-color: transparent; border-color: transparent;
background-color: transparent; background-color: transparent;
font-size: 20px; font-size: 20px;
@ -159,3 +159,11 @@ tr:hover .copy-link {
color: #FFF; color: #FFF;
outline: none; outline: none;
} }
#url-style-switcher a {
color: var(--text-muted);
}
#url-style-switcher a.active {
text-decoration: underline;
}

+ 10
- 2
templates/accounts/project.html View File

@ -89,9 +89,17 @@
API key (read-only): <br /> API key (read-only): <br />
<pre>{{ project.api_key_readonly }}</pre> <pre>{{ project.api_key_readonly }}</pre>
</p> </p>
<p>Related links:</p>
{% endif %}
{% if project.ping_key %}
<p>
Ping key: <br />
<pre>{{ project.ping_key }}</pre>
</p>
{% endif %}
<p>See also:</p>
<ul> <ul>
<li><a href="{% url 'hc-serve-doc' 'api' %}">API documentation</a></li> <li><a href="{% url 'hc-serve-doc' 'api' %}">API documentation</a></li>
{% if project.api_key_readonly %}
{% if enable_prometheus %} {% if enable_prometheus %}
<li> <li>
<a href="{% url 'hc-metrics' project.code project.api_key_readonly %}">Prometheus metrics endpoint</a> <a href="{% url 'hc-metrics' project.code project.api_key_readonly %}">Prometheus metrics endpoint</a>
@ -101,8 +109,8 @@
<a href="{{ project.dashboard_url }}">Read-only dashboard</a> <a href="{{ project.dashboard_url }}">Read-only dashboard</a>
(<a href="https://github.com/healthchecks/dashboard/#security">security considerations</a>) (<a href="https://github.com/healthchecks/dashboard/#security">security considerations</a>)
</li> </li>
{% endif %}
</ul> </ul>
{% endif %}
<button <button
data-toggle="modal" data-toggle="modal"
data-target="#revoke-api-key-modal" data-target="#revoke-api-key-modal"


+ 10
- 0
templates/docs/api.html View File

@ -116,6 +116,7 @@ specified value.</p>
<span class="nt">&quot;checks&quot;</span><span class="p">:</span> <span class="p">[</span> <span class="nt">&quot;checks&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Filesystem Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Filesystem Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;filesystem-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;backup fs&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;backup fs&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs incremental backup every hour&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs incremental backup every hour&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
@ -133,6 +134,7 @@ specified value.</p>
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;database-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
@ -165,6 +167,7 @@ and <a href="#list-flips">Get a list of check's status changes</a> API calls.</p
<span class="nt">&quot;checks&quot;</span><span class="p">:</span> <span class="p">[</span> <span class="nt">&quot;checks&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Filesystem Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Filesystem Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;filesystem-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;backup fs&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;backup fs&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs incremental backup every hour&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs incremental backup every hour&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
@ -179,6 +182,7 @@ and <a href="#list-flips">Get a list of check's status changes</a> API calls.</p
<span class="p">},</span> <span class="p">},</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;database-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
@ -220,6 +224,7 @@ using the read-only API key) as an identifier.</p>
<h3>Example Response</h3> <h3>Example Response</h3>
<div class="highlight"><pre><span></span><code><span class="p">{</span> <div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;database-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
@ -249,6 +254,7 @@ API responses, the client can easily construct these URLs themselves <em>if</em>
check's unique UUID.</p> check's unique UUID.</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span> <div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Database Backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;database-backup&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span> <span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;production db&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span> <span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Runs ~/db-backup.sh&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span> <span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">1200</span><span class="p">,</span>
@ -416,6 +422,7 @@ the limit is 20 checks per account.</dd>
<span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span>
@ -558,6 +565,7 @@ field values.</dd>
<span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span>
@ -602,6 +610,7 @@ header is sometimes required by some network proxies and web servers.</p>
<span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span>
@ -643,6 +652,7 @@ check that was just deleted.</p>
<span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span> <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;slug&quot;</span><span class="p">:</span> <span class="s2">&quot;backups&quot;</span><span class="p">,</span>
<span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;manual_resume&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nt">&quot;methods&quot;</span><span class="p">:</span> <span class="s2">&quot;&quot;</span><span class="p">,</span>


+ 10
- 0
templates/docs/api.md View File

@ -97,6 +97,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/
"checks": [ "checks": [
{ {
"name": "Filesystem Backup", "name": "Filesystem Backup",
"slug": "filesystem-backup",
"tags": "backup fs", "tags": "backup fs",
"desc": "Runs incremental backup every hour", "desc": "Runs incremental backup every hour",
"grace": 600, "grace": 600,
@ -114,6 +115,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/
}, },
{ {
"name": "Database Backup", "name": "Database Backup",
"slug": "database-backup",
"tags": "production db", "tags": "production db",
"desc": "Runs ~/db-backup.sh", "desc": "Runs ~/db-backup.sh",
"grace": 1200, "grace": 1200,
@ -150,6 +152,7 @@ Example:
"checks": [ "checks": [
{ {
"name": "Filesystem Backup", "name": "Filesystem Backup",
"slug": "filesystem-backup",
"tags": "backup fs", "tags": "backup fs",
"desc": "Runs incremental backup every hour", "desc": "Runs incremental backup every hour",
"grace": 600, "grace": 600,
@ -164,6 +167,7 @@ Example:
}, },
{ {
"name": "Database Backup", "name": "Database Backup",
"slug": "database-backup",
"tags": "production db", "tags": "production db",
"desc": "Runs ~/db-backup.sh", "desc": "Runs ~/db-backup.sh",
"grace": 1200, "grace": 1200,
@ -215,6 +219,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/<uuid>
```json ```json
{ {
"name": "Database Backup", "name": "Database Backup",
"slug": "database-backup",
"tags": "production db", "tags": "production db",
"desc": "Runs ~/db-backup.sh", "desc": "Runs ~/db-backup.sh",
"grace": 1200, "grace": 1200,
@ -249,6 +254,7 @@ check's unique UUID.
```json ```json
{ {
"name": "Database Backup", "name": "Database Backup",
"slug": "database-backup",
"tags": "production db", "tags": "production db",
"desc": "Runs ~/db-backup.sh", "desc": "Runs ~/db-backup.sh",
"grace": 1200, "grace": 1200,
@ -456,6 +462,7 @@ curl SITE_ROOT/api/v1/checks/ \
"last_ping": null, "last_ping": null,
"n_pings": 0, "n_pings": 0,
"name": "Backups", "name": "Backups",
"slug": "backups",
"next_ping": null, "next_ping": null,
"manual_resume": false, "manual_resume": false,
"methods": "", "methods": "",
@ -636,6 +643,7 @@ curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc \
"last_ping": null, "last_ping": null,
"n_pings": 0, "n_pings": 0,
"name": "Backups", "name": "Backups",
"slug": "backups",
"next_ping": null, "next_ping": null,
"manual_resume": false, "manual_resume": false,
"methods": "", "methods": "",
@ -692,6 +700,7 @@ header is sometimes required by some network proxies and web servers.
"last_ping": null, "last_ping": null,
"n_pings": 0, "n_pings": 0,
"name": "Backups", "name": "Backups",
"slug": "backups",
"next_ping": null, "next_ping": null,
"manual_resume": false, "manual_resume": false,
"methods": "", "methods": "",
@ -744,6 +753,7 @@ curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc \
"last_ping": null, "last_ping": null,
"n_pings": 0, "n_pings": 0,
"name": "Backups", "name": "Backups",
"slug": "backups",
"next_ping": null, "next_ping": null,
"manual_resume": false, "manual_resume": false,
"methods": "", "methods": "",


+ 15
- 3
templates/front/my_checks_desktop.html View File

@ -22,8 +22,16 @@
{% endif %} {% endif %}
</th> </th>
<th class="hidden-xs hidden-sm"> <th class="hidden-xs hidden-sm">
<span class="visible-lg">Ping URL</span>
<span class="visible-md">Code</span>
<span class="visible-lg-inline">
Ping URL
</span>
<span class="visible-md-inline">{{ project.show_slugs|yesno:"Key/Slug,UUID" }}</span>
{% if rw %}
<small id="url-style-switcher">
<a href="?urls=uuid" {% if not project.show_slugs %}class="active"{% endif %}>uuid</a>
<a href="?urls=slug" {% if project.show_slugs %}class="active"{% endif %}>slug</a>
</small>
{% endif %}
</th> </th>
<th class="th-integrations hidden-xs">Integrations</th> <th class="th-integrations hidden-xs">Integrations</th>
<th class="th-period hidden-xs"> <th class="th-period hidden-xs">
@ -73,12 +81,16 @@
</div> </div>
</td> </td>
<td class="hidden-xs hidden-sm url"> <td class="hidden-xs hidden-sm url">
{% if project.show_slugs and not check.slug %}
<span class="unavailable">unavailable, set name first</span>
{% else %}
<span class="my-checks-url"> <span class="my-checks-url">
<span class="base hidden-md">{{ ping_endpoint }}</span>{{ check.code }}
<span class="base hidden-md">{{ ping_endpoint }}</span>{{ check.relative_url }}
</span> </span>
<button class="copy-link" data-clipboard-text="{{ check.url }}"> <button class="copy-link" data-clipboard-text="{{ check.url }}">
copy copy
</button> </button>
{% endif %}
</td> </td>
<td class="hidden-xs"> <td class="hidden-xs">
{% if channels|length <= 10 %} {% if channels|length <= 10 %}


Loading…
Cancel
Save