diff --git a/hc/front/urls.py b/hc/front/urls.py index a0c52263..28b68c35 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -4,6 +4,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('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 b51b64db..7c01173e 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -34,6 +34,7 @@ import requests VALID_SORT_VALUES = ("name", "-name", "last_ping", "-last_ping", "created") STATUS_TEXT_TMPL = get_template("front/log_status_text.html") LAST_PING_TMPL = get_template("front/last_ping_cell.html") +EVENTS_TMPL = get_template("front/details_events.html") def _tags_statuses(checks): @@ -324,23 +325,10 @@ def remove_check(request, code): return redirect("hc-checks") -@login_required -def log(request, code): - check = get_object_or_404(Check, code=code) - if check.user != request.team.user: - return HttpResponseForbidden() - - limit = 20 - team_limit = request.team.ping_log_limit - if "full_log" in request.GET: - limit = team_limit - - pings = Ping.objects.filter(owner=check).order_by("-id")[:limit + 1] +def _get_events(check, limit): + pings = Ping.objects.filter(owner=check).order_by("-id")[:limit] pings = list(pings) - can_load_more = len(pings) > limit - pings = pings[:limit] - alerts = [] if len(pings): cutoff = pings[-1].created @@ -350,21 +338,42 @@ def log(request, code): events = pings + list(alerts) events.sort(key=lambda el: el.created, reverse=True) + return events + + +@login_required +def log(request, code): + check = get_object_or_404(Check, code=code) + if check.user != request.team.user: + return HttpResponseForbidden() + + limit = request.team.ping_log_limit + ctx = { + "check": check, + "events": _get_events(check, limit), + "limit": limit, + "show_limit_notice": check.n_pings > limit and settings.USE_PAYMENTS + } + + return render(request, "front/log.html", ctx) + + +@login_required +def details(request, code): + check = get_object_or_404(Check, code=code) + if check.user != request.team.user: + return HttpResponseForbidden() channels = Channel.objects.filter(user=request.team.user) channels = list(channels.order_by("created")) ctx = { - "page": "log", + "page": "details", "check": check, - "ping_endpoint": settings.PING_ENDPOINT, - "channels": channels, - "events": events, - "num_showing": len(pings), - "can_load_more": can_load_more + "channels": channels } - return render(request, "front/log.html", ctx) + return render(request, "front/details.html", ctx) @login_required @@ -374,9 +383,16 @@ def status_single(request, code): return HttpResponseForbidden() status = check.get_status() + events = _get_events(check, 20) + updated = None + if len(events): + updated = events[0].created.isoformat() + return JsonResponse({ "status": status, - "status_text": STATUS_TEXT_TMPL.render({"check": check}) + "status_text": STATUS_TEXT_TMPL.render({"check": check}), + "events": EVENTS_TMPL.render({"check": check, "events": events}), + "updated": updated }) diff --git a/static/css/base.css b/static/css/base.css index 51bf3d8a..4084357e 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -52,7 +52,7 @@ body { font-size: small; } -.page-checks .container-fluid, .page-log .container-fluid { +.page-checks .container-fluid, .page-details .container-fluid { /* Fluid below 1320px, but max width capped to 1320px ... */ max-width: 1320px; } diff --git a/static/js/adaptive-setinterval.js b/static/js/adaptive-setinterval.js index 91eb8d12..ff23c682 100644 --- a/static/js/adaptive-setinterval.js +++ b/static/js/adaptive-setinterval.js @@ -1,4 +1,4 @@ -function adaptiveSetInterval(fn) { +function adaptiveSetInterval(fn, runNow) { // unconditionally run every minute setInterval(fn, 60000); @@ -39,4 +39,9 @@ function adaptiveSetInterval(fn) { quota = 20; } }); + + if (runNow) { + quota = 20; + scheduleRun(); + } } diff --git a/static/js/checks.js b/static/js/checks.js index cbb0e077..b1921353 100644 --- a/static/js/checks.js +++ b/static/js/checks.js @@ -112,7 +112,7 @@ $(function () { $(".show-log").click(function(e) { var code = $(this).closest("tr.checks-row").attr("id"); - var url = "/checks/" + code + "/log/"; + var url = "/checks/" + code + "/details/"; window.location = url; return false; }); diff --git a/static/js/details.js b/static/js/details.js new file mode 100644 index 00000000..e192884c --- /dev/null +++ b/static/js/details.js @@ -0,0 +1,108 @@ +$(function () { + $("#edit-name").click(function() { + $('#update-name-modal').modal("show"); + $("#update-name-input").focus(); + + return false; + }); + + $("#pause").click(function(e) { + $("#pause-form").submit(); + return false; + }); + + $("#ping-now").click(function(e) { + var button = this; + $.get(this.dataset.url, function() { + button.textContent = "Success!"; + }); + }); + + $("#ping-now").mouseout(function(e) { + setTimeout(function() { + e.target.textContent = "Ping Now!"; + }, 300); + }); + + var code = document.getElementById("edit-timeout").dataset.code; + var statusUrl = "/checks/" + code + "/status/"; + var lastStatusText = ""; + var lastUpdated = ""; + adaptiveSetInterval(function() { + $.ajax({ + url: statusUrl, + dataType: "json", + timeout: 2000, + success: function(data) { + if (data.status_text != lastStatusText) { + lastStatusText = data.status_text; + $("#log-status-icon").attr("class", "status icon-" + data.status); + $("#log-status-text").text(data.status_text); + } + + if (data.updated != lastUpdated) { + lastUpdated = data.updated; + $("#events").html(data.events); + switchDateFormat(lastFormat); + } + } + }); + }, true); + + // Copy to clipboard + var clipboard = new Clipboard('button.copy-btn'); + $("button.copy-btn").mouseout(function(e) { + setTimeout(function() { + e.target.textContent = e.target.dataset.label; + }, 300); + }); + + clipboard.on('success', function(e) { + e.trigger.textContent = "Copied!"; + e.clearSelection(); + }); + + clipboard.on('error', function(e) { + var text = e.trigger.getAttribute("data-clipboard-text"); + prompt("Press Ctrl+C to select:", text) + }); + + $("#log tr.ok").on("click", function() { + $("#ping-details-body").text("Updating..."); + $('#ping-details-modal').modal("show"); + + var token = $('input[name=csrfmiddlewaretoken]').val(); + $.ajax({ + url: this.dataset.url, + type: "post", + headers: {"X-CSRFToken": token}, + success: function(data) { + $("#ping-details-body" ).html(data); + } + }); + + return false; + }); + + var lastFormat = "local"; + function switchDateFormat(format) { + lastFormat = format; + $("#log tr").each(function(index, row) { + var dt = moment(row.getAttribute("data-dt")); + format == "local" ? dt.local() : dt.utc(); + + $(".date", row).text(dt.format("MMM D")); + $(".time", row).text(dt.format("HH:mm")); + }) + + // The table is initially hidden to avoid flickering as we convert dates. + // Once it's ready, set it to visible: + $("#log").css("visibility", "visible"); + } + + + $("#format-switcher").click(function(ev) { + var format = ev.target.getAttribute("data-format"); + switchDateFormat(format); + }); +}); diff --git a/static/js/log.js b/static/js/log.js index e2069047..84dd33cc 100644 --- a/static/js/log.js +++ b/static/js/log.js @@ -1,66 +1,4 @@ $(function () { - $("#edit-name").click(function() { - $('#update-name-modal').modal("show"); - $("#update-name-input").focus(); - - return false; - }); - - $("#pause").click(function(e) { - $("#pause-form").submit(); - return false; - }); - - $("#ping-now").click(function(e) { - var button = this; - $.get(this.dataset.url, function() { - button.textContent = "Success!"; - }); - }); - - $("#ping-now").mouseout(function(e) { - setTimeout(function() { - e.target.textContent = "Ping Now!"; - }, 300); - }); - - var code = document.getElementById("edit-timeout").dataset.code; - var statusUrl = "/checks/" + code + "/status/"; - var lastStatusText = ""; - adaptiveSetInterval(function() { - $.ajax({ - url: statusUrl, - dataType: "json", - timeout: 2000, - success: function(data) { - if (data.status_text == lastStatusText) { - return; - } - lastStatusText = data.status_text; - $("#log-status-icon").attr("class", "status icon-" + data.status); - $("#log-status-text").text(data.status_text); - } - }); - }); - - // Copy to clipboard - var clipboard = new Clipboard('button.copy-btn'); - $("button.copy-btn").mouseout(function(e) { - setTimeout(function() { - e.target.textContent = e.target.dataset.label; - }, 300); - }); - - clipboard.on('success', function(e) { - e.trigger.textContent = "Copied!"; - e.clearSelection(); - }); - - clipboard.on('error', function(e) { - var text = e.trigger.getAttribute("data-clipboard-text"); - prompt("Press Ctrl+C to select:", text) - }); - $("#log tr.ok").on("click", function() { $("#ping-details-body").text("Updating..."); $('#ping-details-modal').modal("show"); diff --git a/templates/base.html b/templates/base.html index d16a5bc1..9cb3ea04 100644 --- a/templates/base.html +++ b/templates/base.html @@ -45,7 +45,7 @@