Files
FabledSteward/steward/templates/monitors/_fields.html
T
bvandeusenandClaude Opus 5.5 6788ce60f1
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 53s
CI / unit (push) Successful in 1m19s
CI / publish (push) Successful in 1m20s
feat(monitors): a DNS monitor can name a resolver; the host page puts Ansible above Docker (#5214)
DNS monitors take an optional Resolver, a server's IP. Set, the check asks
that server directly for A and AAAA over UDP and records how long it took
to answer; empty keeps today's lookup through the container's resolver.
"Does my local DNS still answer for public names" and "does 1.1.1.1" are
now separate monitors. No new dependency.

On a host's page the Ansible card moves above the Docker list, which can
run to dozens of containers and hid it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:14:11 -04:00

79 lines
4.4 KiB
HTML

{# Shared type-specific monitor form fields. Every field is rendered; a tiny
script (mtoggle) shows only the ones relevant to the selected type. #}
{% macro type_fields(config={}) %}
{# What each type checks, shown under the row for the selected type. #}
<p class="field-hint mfield" data-types="icmp" style="grid-column:1/-1;margin:0;">
<strong>Ping</strong> sends an ICMP echo to the target, a hostname or IP. Up when it answers; the round trip is the latency.</p>
<p class="field-hint mfield" data-types="tcp" style="grid-column:1/-1;margin:0;">
<strong>TCP</strong> opens a connection to the target on the port below (22 for SSH, 443 for HTTPS, …). Up when the port accepts; the connect time is the latency. Nothing is sent.</p>
<p class="field-hint mfield" data-types="http" style="grid-column:1/-1;margin:0;">
<strong>HTTP</strong> requests the target URL (http:// or https://). Up when the status matches, and the page contains the text below if you set one. For https, the certificate's expiry is tracked too.</p>
<p class="field-hint mfield" data-types="dns" style="grid-column:1/-1;margin:0;">
<strong>DNS</strong> looks the target name up (google.com, nas.lan, …). Up when it resolves, and to the expected IP if you set one. With no resolver it asks the way Steward's container does (its resolv.conf, so usually your local DNS) and records no response time; name a resolver (1.1.1.1, your router, …) to ask that server directly and time its answer.</p>
{# tcp #}
<div class="mfield" data-types="tcp">
<label class="field-label">Port</label>
<input type="number" name="port" value="{{ config.get('port', 80) }}" min="1" max="65535" class="input">
</div>
{# dns #}
<div class="mfield" data-types="dns">
<label class="field-label">Expected IP <span style="color:var(--text-dim);">(optional)</span></label>
<input type="text" name="expected_ip" value="{{ config.get('expected_ip') or '' }}"
placeholder="e.g. 192.0.2.10" autocomplete="off" class="input">
</div>
<div class="mfield" data-types="dns">
<label class="field-label">Resolver <span style="color:var(--text-dim);">(optional)</span></label>
<input type="text" name="resolver" value="{{ config.get('resolver') or '' }}"
placeholder="the container's own" autocomplete="off" class="input"
title="A DNS server's IP address to ask directly. Empty asks the way Steward's container does.">
</div>
{# http #}
<div class="mfield" data-types="http">
<label class="field-label">Method</label>
<select name="method" class="input">
{% for opt in ["GET", "HEAD", "POST"] %}
<option value="{{ opt }}" {% if config.get('method', 'GET') == opt %}selected{% endif %}>{{ opt }}</option>
{% endfor %}
</select>
</div>
<div class="mfield" data-types="http">
<label class="field-label">Expected status</label>
<input type="number" name="expected_status" value="{{ config.get('expected_status', 200) }}"
min="100" max="599" class="input">
</div>
<div class="mfield" data-types="http">
<label class="field-label">Timeout (s)</label>
<input type="number" name="timeout_seconds" value="{{ config.get('timeout_seconds', 10) }}"
min="1" max="60" class="input">
</div>
<div class="mfield" data-types="http">
<label class="field-label">Content match <span style="color:var(--text-dim);">(substring)</span></label>
<input type="text" name="content_match" value="{{ config.get('content_match', '') }}"
placeholder="optional" autocomplete="off" class="input">
</div>
<div class="mfield" data-types="http" style="display:flex;flex-direction:column;gap:0.3rem;justify-content:flex-end;">
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.82rem;cursor:pointer;">
<input type="checkbox" name="follow_redirects" {% if config.get('follow_redirects', True) %}checked{% endif %}> Follow redirects
</label>
<label style="display:flex;align-items:center;gap:0.4rem;font-size:0.82rem;cursor:pointer;">
<input type="checkbox" name="verify_ssl" {% if config.get('verify_ssl', True) %}checked{% endif %}> Verify SSL
</label>
</div>
{% endmacro %}
{# Show only the fields whose data-types matches the active monitor type. #}
{% macro toggle_script() %}
<script>
function mtoggle(t) {
document.querySelectorAll('.mfield').forEach(function (el) {
var types = (el.getAttribute('data-types') || '').split(',');
el.style.display = types.indexOf(t) !== -1 ? '' : 'none';
});
}
</script>
{% endmacro %}