defmodule Integrations.Http.Display do @moduledoc """ Dashboard panel for `Integrations.Http` (same package). Nested under the monitor's own namespace deliberately — `#299`'s split pattern: the package's top-level module name (`Integrations.Http`) never changes, so existing `raven.toml` monitor configs keep resolving unmodified, and this module's name is guaranteed distinct from the separately-published `raven_integration_http_display`'s `Integrations.HttpDisplay` (no dot) — both packages can install and load simultaneously without colliding. `Display.BundledDefault` auto-hooks this whenever `Integrations.Http` starts, the same as if they were still one module — an integrator bundling Monitor and Display in one package, even across two files, is a statement of intent that they belong together by default. """ use CodeNameRaven.Display, category: :web, size_hint: :full def display_timezone, do: "America/Chicago" @impl true def display_name, do: "HTTP Check" @impl true def compatible_monitors, do: [Integrations.Http] @impl true def render(assigns) do window_seconds = assigns.config.display_window_seconds cutoff = DateTime.add(DateTime.utc_now(), -window_seconds, :second) sample_key = Map.get(assigns.config, :target_id) || assigns.config.id samples = CodeNameRaven.Runtime.recent_samples(sample_key, limit: 120) samples = Enum.filter(samples, fn s -> DateTime.compare(s.collected_at, cutoff) != :lt end) local_id = CodeNameRaven.Runtime.instance_id() latest_local = samples |> Enum.filter(&(&1.instance_id == local_id)) |> List.last() result = assigns[:result] || (latest_local && %{ status_code: latest_local.status_code, latency_ms: latest_local.latency_ms, cert_expiry_days: latest_local.metrics["cert_expiry_days"], timing: %{ dns_ms: latest_local.metrics["dns_ms"], tcp_ms: latest_local.metrics["tcp_ms"], ssl_ms: latest_local.metrics["ssl_ms"], ttfb_ms: latest_local.metrics["ttfb_ms"], download_ms: latest_local.metrics["download_ms"] }, redirects: latest_local.metrics["redirects"] }) assigns = Map.merge(assigns, %{samples: samples, local_id: local_id, result: result}) ~H"""
<%!-- Header --%>

<%= @config.name || @config.params[:url] || "HTTP Check" %>

<%= @config.params[:url] %>

<.status_chip status={@status} />
<%!-- Vital row --%>
<.stat label="Status" value={to_string(@result.status_code)} /> <.stat label="Latency" value={"#{@result.latency_ms} ms"} />

Cert Expiry

<%= @result.cert_expiry_days %>d

0} class="bg-base-200 rounded-lg px-3 py-2 flex-1 text-center">

Redirects

<%= @result.redirects %>

<%!-- Timing Breakdown --%>
DNS <%= @result.timing.dns_ms %>ms
TCP <%= @result.timing.tcp_ms %>ms
SSL <%= if @result.timing.ssl_ms, do: "#{@result.timing.ssl_ms}ms", else: "—" %>
TTFB <%= @result.timing.ttfb_ms %>ms
Download <%= @result.timing.download_ms %>ms
<%!-- Assertions Check --%>

Assertions Check

<%= for assertion <- @result.assertions do %> <% outcome = CodeNameRaven.Monitor.Assertion.evaluate(assertion, @result) %>
<%= assertion.type %><%= if assertion.target, do: " [#{assertion.target}]" %> <%= assertion.operator %> <%= inspect(assertion.value) %> <%= if outcome == :pass do %> ✓ Pass <% else %> ✗ Fail <% end %>
<% end %>
<%!-- Error --%>

<%= @last_error %>

<%!-- Footer --%>

Checked <%= CodeNameRaven.Timezone.format_datetime(@last_checked_at, __MODULE__) %>

""" end # --------------------------------------------------------------------------- # Private components # --------------------------------------------------------------------------- defp status_chip(%{status: :up} = assigns) do ~H""" Up """ end defp status_chip(%{status: :degraded} = assigns) do ~H""" Degraded """ end defp status_chip(%{status: :down} = assigns) do ~H""" Down """ end defp status_chip(assigns) do ~H""" Unknown """ end defp stat(assigns) do ~H"""

<%= @label %>

<%= @value %>

""" end end