Diff -- /var/www/vhosts/3dshawn.com/site1/TEMPLATES/alerts.html
Diff

/var/www/vhosts/3dshawn.com/site1/TEMPLATES/alerts.html

added on local at 2026-07-12 00:35:28

Added
+208
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 4433ba3e7905
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1<div class="page-head">
2 <span class="page-eyebrow"><span class="dot"></span> Notify</span>
3 <h1 class="page-title">Alert rules</h1>
4 <p class="page-subtitle">Route file + schema changes to Slack, Discord, or any HTTP webhook. Rules fire in near-real-time (as the cron dispatcher runs every minute). Every attempt is logged.</p>
5</div>
6
7<div class="ds-guide">
8 <div class="dsg-icon">i</div>
9 <div>
10 <span class="dsg-title">How a rule fires:</span>
11 Two flavors. <strong>Per-row</strong> rules (default) fire on every change that matches the path glob + kind + status filter &mdash; good for "wake me on any deletion". <strong>Frequency</strong> rules (set threshold &gt; 0) fire once when a matching file crosses N changes in M minutes &mdash; good for "wake me when someone edits the payment gateway config 5+ times in an hour". Rate limit protects you from firehoses; every delivery attempt is logged whether it succeeded or failed.
12 </div>
13</div>
14
15<div class="dash-grid" style="grid-template-columns:1fr 1fr;margin-bottom:20px">
16
17 <!-- Rule form -->
18 <div class="module glow-teal">
19 <div class="module-head"><div class="left">
20 <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
21 <div class="module-title">[if:$is_edit]Edit rule #$edit_id[/if][if:!$is_edit]New rule[/if]</div>
22 <span class="module-subtitle">Combine a matcher (kind + path glob) with a delivery target. Save, then wait for the next capture tick.</span>
23 </div></div>
24 <div class="module-body">
25 <form method="post" action="/alerts.cgi" style="display:flex;flex-direction:column;gap:12px">
26 <input type="hidden" name="op" value="$form_op">
27 [if:$is_edit]<input type="hidden" name="alert_rule_id" value="$edit_id">[/if]
28
29 <label style="display:flex;flex-direction:column;gap:4px">
30 <span class="rb-label">Name</span>
31 <input type="text" name="rule_name" value="$rule_name" placeholder="e.g. Prod nginx config changes" required
32 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:inherit">
33 </label>
34
35 <div style="display:flex;gap:12px">
36 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
37 <span class="rb-label">Match kind</span>
38 <select name="match_kind" style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
39 <option value="either" $mk_either_sel>Either (file + schema)</option>
40 <option value="file" $mk_file_sel>File changes only</option>
41 <option value="schema" $mk_schema_sel>Schema DDL only</option>
42 </select>
43 </label>
44 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
45 <span class="rb-label">Path glob</span>
46 <input type="text" name="match_path_glob" value="$match_path_glob" placeholder="e.g. /etc/nginx/** or drift_sense.**"
47 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:12px">
48 </label>
49 </div>
50
51 <div style="display:flex;gap:12px">
52 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
53 <span class="rb-label">Status filter <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">(files only)</span></span>
54 <input type="text" name="match_status_list" value="$match_status_list" placeholder="added,modified,deleted"
55 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:12px">
56 </label>
57 <label style="flex:1;display:flex;align-items:center;gap:8px;margin-top:16px">
58 <input type="checkbox" name="exclude_ts_only" value="1" $exclude_ts_only_checked>
59 <span style="font-size:12.5px;color:var(--text-dim)">Exclude timestamp-only changes</span>
60 </label>
61 </div>
62
63 <hr style="border:none;border-top:1px solid var(--border);margin:4px 0">
64
65 <div style="display:flex;gap:12px">
66 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
67 <span class="rb-label">Delivery</span>
68 <select name="delivery_kind" style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
69 <option value="generic_webhook" $dk_generic_webhook_sel>Generic HTTP webhook</option>
70 <option value="slack" $dk_slack_sel>Slack incoming webhook</option>
71 <option value="discord" $dk_discord_sel>Discord webhook</option>
72 <option value="email" $dk_email_sel>Email (not yet implemented)</option>
73 </select>
74 </label>
75 <label style="flex:2;display:flex;flex-direction:column;gap:4px">
76 <span class="rb-label">Webhook URL</span>
77 <input type="text" name="delivery_url" value="$delivery_url" placeholder="https://hooks.slack.com/..."
78 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px;font-family:var(--mono);font-size:11.5px">
79 </label>
80 </div>
81
82 <div style="display:flex;gap:12px">
83 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
84 <span class="rb-label">Rate limit / min</span>
85 <input type="number" name="rate_limit_per_min" value="$rate_limit_per_min" min="0"
86 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
87 </label>
88 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
89 <span class="rb-label">Coalesce window (s)</span>
90 <input type="number" name="coalesce_window_s" value="$coalesce_window_s" min="0"
91 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
92 </label>
93 <label style="flex:1;display:flex;align-items:center;gap:8px;margin-top:16px">
94 <input type="checkbox" name="is_active" value="1" $is_active_checked>
95 <span style="font-size:12.5px;color:var(--text-dim)">Rule is active</span>
96 </label>
97 </div>
98
99 <hr style="border:none;border-top:1px solid var(--border);margin:4px 0">
100
101 <div class="rb-label" style="margin-bottom:-4px">Frequency mode <span class="dim" style="text-transform:none;letter-spacing:0;font-size:10.5px">&mdash; when threshold &gt; 0, this rule fires whenever a matching file crosses N changes in M minutes (instead of firing per row)</span></div>
102 <div style="display:flex;gap:12px">
103 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
104 <span class="rb-label">Threshold (changes)</span>
105 <input type="number" name="frequency_threshold" value="$frequency_threshold" min="0"
106 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
107 </label>
108 <label style="flex:1;display:flex;flex-direction:column;gap:4px">
109 <span class="rb-label">Window (minutes)</span>
110 <input type="number" name="frequency_window_min" value="$frequency_window_min" min="1"
111 style="background:var(--bg-1);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px 12px">
112 </label>
113 </div>
114
115 <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px">
116 [if:$is_edit]<a href="/alerts.cgi" class="tr-chip">Cancel</a>[/if]
117 <button type="submit" class="tr-chip is-active" style="border:none">[if:$is_edit]Save changes[/if][if:!$is_edit]Create rule[/if]</button>
118 </div>
119 </form>
120 </div>
121 </div>
122
123 <!-- Delivery history -->
124 <div class="module glow-emerald">
125 <div class="module-head"><div class="left">
126 <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg></div>
127 <div class="module-title">Recent deliveries</div>
128 <span class="module-subtitle">Last 30 fire attempts across every rule. Audit trail for compliance / debugging.</span>
129 </div></div>
130 <div class="module-body tight" style="max-height:520px;overflow:auto">
131 [if:$has_deliveries]
132 <table class="tbl">
133 <thead><tr>
134 <th style="width:80px">Status</th>
135 <th>Rule / target</th>
136 <th style="width:120px">Result</th>
137 <th style="width:150px">When</th>
138 </tr></thead>
139 <tbody>
140 [loop:@deliveries]
141 <tr>
142 <td><span class="pill $delivery_pill">$delivery_status</span></td>
143 <td class="mono" style="font-size:11.5px"><a href="$source_url" style="color:var(--accent-hi);text-decoration:none">$summary_h</a></td>
144 <td class="dim mono" style="font-size:11px">$err_h</td>
145 <td class="mono dim"><span class="ts" data-ts="$fired_epoch" data-fmt="relative">$fired_at</span></td>
146 </tr>
147 [/loop]
148 </tbody>
149 </table>
150 [/if]
151 [if:!$has_deliveries]
152 <div style="padding:36px 14px;text-align:center;color:var(--text-dim);font-size:13.5px">
153 No deliveries yet. Create a rule and wait for a change to fire it.
154 </div>
155 [/if]
156 </div>
157 </div>
158</div>
159
160<!-- Rule list -->
161<div class="module glow-sky">
162 <div class="module-head"><div class="left">
163 <div class="module-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg></div>
164 <div class="module-title">Rules</div>
165 <span class="module-subtitle">Click a row to edit. Deactivating a rule stops it firing but keeps its history.</span>
166 </div></div>
167 <div class="module-body tight">
168 [if:$has_rules]
169 <table class="tbl">
170 <thead><tr>
171 <th style="width:80px">Status</th>
172 <th>Name</th>
173 <th>Matches</th>
174 <th style="width:110px">Delivery</th>
175 <th>Target</th>
176 <th style="width:70px">Fires</th>
177 <th style="width:150px">Last fired</th>
178 <th style="width:60px">Actions</th>
179 </tr></thead>
180 <tbody>
181 [loop:@rules]
182 <tr>
183 <td><span class="pill $status_pill">$status_label</span></td>
184 <td><a href="$edit_url" style="color:var(--accent-hi);text-decoration:none">$rule_name</a></td>
185 <td class="mono dim" style="font-size:11.5px">$match_kind &middot; $match_glob_h</td>
186 <td><span class="pill $kind_pill">$delivery_kind</span></td>
187 <td class="mono dim" style="font-size:11px">$url_h</td>
188 <td class="mono">$fire_count</td>
189 <td class="mono dim"><span class="ts" data-ts="$last_fired_epoch" data-fmt="relative">$last_fired_h</span></td>
190 <td>
191 <form method="post" action="/alerts.cgi" style="display:inline">
192 <input type="hidden" name="op" value="toggle">
193 <input type="hidden" name="alert_rule_id" value="$alert_rule_id">
194 <button type="submit" class="tr-chip" title="Toggle active" style="padding:3px 8px;font-size:11px">⏻</button>
195 </form>
196 </td>
197 </tr>
198 [/loop]
199 </tbody>
200 </table>
201 [/if]
202 [if:!$has_rules]
203 <div style="padding:36px 14px;text-align:center;color:var(--text-dim);font-size:13.5px">
204 No rules yet. Use the form above to create your first.
205 </div>
206 [/if]
207 </div>
208</div>
Keyboard: j next diff k previous diff g top G bottom r restore c compile-check ? help