Skip to main content

max / makenotwork

13.4 KB · 349 lines History Blame Raw
1 {% extends "base.html" %}
2
3 {% block title %}System Health - Makenot.work{% endblock %}
4 {% block body_attrs %} class="health-page"{% endblock %}
5
6 {% block head %}
7 {% endblock %}
8
9 {% block content %}
10 {% include "partials/site_header.html" %}
11
12 <div class="health-container">
13 <header>
14 <h1 class="page-title">System Health</h1>
15 <p class="subtitle">Service status and diagnostics</p>
16 </header>
17
18 <div class="summary-bar">
19 <div class="summary-item">
20 <span class="status-indicator {{ overall_status_class }}"></span>
21 <span>{{ overall_status }}</span>
22 </div>
23 <div class="summary-item">
24 Uptime: {{ uptime }}
25 </div>
26 <div class="summary-item">
27 Version: {{ version }}
28 </div>
29 <div class="summary-item">
30 Checked in {{ check_duration_ms }}ms
31 </div>
32 </div>
33
34 <div class="health-grid">
35 <div class="health-card">
36 <h3>
37 <span class="status-indicator {{ db_status_class }}"></span>
38 Database
39 </h3>
40 <dl>
41 <dt>Status</dt>
42 <dd>{{ db_status }}</dd>
43 <dt>Pool</dt>
44 <dd>{{ db_active_connections }}/{{ db_pool_max }} ({{ db_pool_utilization }})</dd>
45 <dt>Users</dt>
46 <dd>{{ user_count }}</dd>
47 <dt>Projects</dt>
48 <dd>{{ project_count }}</dd>
49 <dt>Items</dt>
50 <dd>{{ item_count }}</dd>
51 <dt>Transactions</dt>
52 <dd>{{ transaction_count }}</dd>
53 <dt>Blog Posts</dt>
54 <dd>{{ blog_post_count }}</dd>
55 </dl>
56 </div>
57
58 <div class="health-card">
59 <h3>
60 <span class="status-indicator {{ session_status_class }}"></span>
61 Sessions
62 </h3>
63 <dl>
64 <dt>Status</dt>
65 <dd>{{ session_status }}</dd>
66 <dt>Store</dt>
67 <dd>PostgreSQL</dd>
68 <dt>Active Sessions</dt>
69 <dd>{{ active_sessions }}</dd>
70 </dl>
71 </div>
72
73 <div class="health-card">
74 <h3>
75 <span class="status-indicator {{ storage_status_class }}"></span>
76 Storage (S3)
77 </h3>
78 <dl>
79 <dt>Status</dt>
80 <dd>{{ storage_status }}</dd>
81 {% if storage_configured %}
82 <dt>Bucket</dt>
83 <dd>{{ storage_bucket }}</dd>
84 <dt>Region</dt>
85 <dd>{{ storage_region }}</dd>
86 {% endif %}
87 </dl>
88 </div>
89
90 <div class="health-card">
91 <h3>
92 <span class="status-indicator {{ stripe_status_class }}"></span>
93 Payments (Stripe)
94 </h3>
95 <dl>
96 <dt>Status</dt>
97 <dd>{{ stripe_status }}</dd>
98 {% if stripe_configured %}
99 <dt>Mode</dt>
100 <dd>{{ stripe_mode }}</dd>
101 <dt>Connected Creators</dt>
102 <dd>{{ connected_creators }}</dd>
103 {% endif %}
104 </dl>
105 </div>
106
107 <div class="health-card">
108 <h3>
109 <span class="status-indicator {{ email_status_class }}"></span>
110 Email
111 </h3>
112 <dl>
113 <dt>Status</dt>
114 <dd>{{ email_status }}</dd>
115 <dt>Provider</dt>
116 <dd>{{ email_provider }}</dd>
117 </dl>
118 </div>
119
120 <div class="health-card">
121 <h3>
122 <span class="status-indicator {{ synckit_status_class }}"></span>
123 SyncKit
124 </h3>
125 <dl>
126 <dt>Status</dt>
127 <dd>{{ synckit_status }}</dd>
128 {% if synckit_configured %}
129 <dt>Registered Apps</dt>
130 <dd>{{ synckit_app_count }}</dd>
131 <dt>Connected Devices</dt>
132 <dd>{{ synckit_device_count }}</dd>
133 <dt>Sync Log Entries</dt>
134 <dd>{{ synckit_log_entries }}</dd>
135 {% endif %}
136 </dl>
137 </div>
138
139 <div class="health-card">
140 <h3>
141 <span class="status-indicator status-ok"></span>
142 Security &amp; Monitoring
143 </h3>
144 <dl>
145 <dt>Admin</dt>
146 <dd>{{ admin_status }}</dd>
147 <dt>2FA</dt>
148 <dd>Available</dd>
149 <dt>CSRF</dt>
150 <dd>Active</dd>
151 </dl>
152 </div>
153
154 <div class="health-card">
155 <h3>
156 <span class="status-indicator status-ok"></span>
157 Server
158 </h3>
159 <dl>
160 <dt>Environment</dt>
161 <dd>{{ environment }}</dd>
162 <dt>Host</dt>
163 <dd>{{ host }}</dd>
164 <dt>Started</dt>
165 <dd>{{ started_at }}</dd>
166 </dl>
167 </div>
168 </div>
169
170 <h2 class="section-title">Privacy &amp; Compliance</h2>
171
172 <div class="health-grid">
173 {% for job in privacy_jobs %}
174 <div class="health-card">
175 <h3>
176 <span class="status-indicator {{ job.status_class }}"></span>
177 {{ job.description }}
178 </h3>
179 <dl>
180 <dt>Last ran</dt>
181 <dd>{{ job.last_ran }}</dd>
182 <dt>Rows affected</dt>
183 <dd>{{ job.rows_affected }}</dd>
184 </dl>
185 </div>
186 {% endfor %}
187 </div>
188
189 <h2 class="section-title">Uptime Monitoring</h2>
190
191 <div class="health-grid">
192 <div class="health-card">
193 <h3>
194 <span class="status-indicator {% if monitor_enabled %}status-ok{% else %}status-warn{% endif %}"></span>
195 Monitor
196 </h3>
197 <dl>
198 <dt>Status</dt>
199 <dd>{% if monitor_enabled %}Active{% else %}Disabled{% endif %}</dd>
200 <dt>Check Interval</dt>
201 <dd>{{ monitor_interval_secs }}s</dd>
202 <dt>Alerts</dt>
203 <dd>{% if alerts_configured %}Configured{% else %}Not configured{% endif %}</dd>
204 </dl>
205 </div>
206
207 <div class="health-card">
208 <h3>
209 <span class="status-indicator status-ok"></span>
210 Uptime
211 </h3>
212 <dl>
213 <dt>Last 24h</dt>
214 <dd>{% match uptime_24h %}{% when Some with (v) %}{{ v }}%{% when None %}No data{% endmatch %}</dd>
215 <dt>Last 7d</dt>
216 <dd>{% match uptime_7d %}{% when Some with (v) %}{{ v }}%{% when None %}No data{% endmatch %}</dd>
217 <dt>Last Incident</dt>
218 <dd>{% match last_incident %}{% when Some with (v) %}{{ v }}{% when None %}None recorded{% endmatch %}</dd>
219 </dl>
220 </div>
221
222 <div class="health-card">
223 <h3>
224 <span class="status-indicator {% if pom_available %}{{ pom_status_class.as_deref().unwrap_or("status-ok") }}{% else %}status-warn{% endif %}"></span>
225 External Monitor (PoM)
226 </h3>
227 <dl>
228 {% if pom_available %}
229 <dt>Status</dt>
230 <dd>{% match pom_status %}{% when Some with (v) %}{{ v }}{% when None %}-{% endmatch %}</dd>
231 <dt>Response Time</dt>
232 <dd>{% match pom_response_time_ms %}{% when Some with (v) %}{{ v }}ms{% when None %}-{% endmatch %}</dd>
233 <dt>Last Check</dt>
234 <dd>{% match pom_checked_at %}{% when Some with (v) %}{{ v }}{% when None %}-{% endmatch %}</dd>
235 <dt>Uptime 24h</dt>
236 <dd>{% match pom_uptime_24h %}{% when Some with (v) %}{{ v }}%{% when None %}No data{% endmatch %}</dd>
237 <dt>Uptime 7d</dt>
238 <dd>{% match pom_uptime_7d %}{% when Some with (v) %}{{ v }}%{% when None %}No data{% endmatch %}</dd>
239 {% match pom_avg_latency %}{% when Some with (v) %}
240 <dt>Avg Latency</dt>
241 <dd>{{ v }}</dd>
242 {% when None %}{% endmatch %}
243 {% match pom_p95_latency %}{% when Some with (v) %}
244 <dt>p95 Latency</dt>
245 <dd>{{ v }}</dd>
246 {% when None %}{% endmatch %}
247 {% if pom_routes_total > 0 %}
248 <dt>Routes</dt>
249 <dd>{{ pom_routes_ok }}/{{ pom_routes_total }} OK{% if !pom_routes_failed.is_empty() %} (FAIL: {{ pom_routes_failed.join(", ") }}){% endif %}</dd>
250 {% endif %}
251 {% if pom_incident_active %}
252 <dt>Incident</dt>
253 <dd class="health-error-text">{% match pom_incident_status %}{% when Some with (v) %}{{ v }}{% when None %}-{% endmatch %} since {% match pom_incident_since %}{% when Some with (v) %}{{ v }}{% when None %}-{% endmatch %}</dd>
254 {% endif %}
255 {% else %}
256 <dt>Status</dt>
257 <dd>Unavailable</dd>
258 {% endif %}
259 </dl>
260 </div>
261 </div>
262
263 {% if !recent_snapshots.is_empty() %}
264 <details class="check-list">
265 <summary>Recent Checks ({{ recent_snapshots.len() }})</summary>
266 <ul class="test-list">
267 {% for snap in recent_snapshots %}
268 <li>
269 <span class="test-name">
270 <span class="status-indicator health-indicator-dot {{ snap.status_class }}"></span>
271 {{ snap.status }} at {{ snap.checked_at }}
272 </span>
273 <span>({{ snap.duration_ms }}ms)</span>
274 </li>
275 {% endfor %}
276 </ul>
277 </details>
278 {% endif %}
279
280 {% if !pom_recent.is_empty() %}
281 <details class="check-list">
282 <summary>Recent External Checks ({{ pom_recent.len() }})</summary>
283 <ul class="test-list">
284 {% for snap in pom_recent %}
285 <li>
286 <span class="test-name">
287 <span class="status-indicator health-indicator-dot {{ snap.status_class }}"></span>
288 {{ snap.status }} at {{ snap.checked_at }}
289 </span>
290 <span>({{ snap.response_time_ms }}ms)</span>
291 </li>
292 {% endfor %}
293 </ul>
294 </details>
295 {% endif %}
296
297 {% if !pom_recent_incidents.is_empty() %}
298 <details class="check-list">
299 <summary>Recent Incidents ({{ pom_recent_incidents.len() }})</summary>
300 <ul class="test-list">
301 {% for inc in pom_recent_incidents %}
302 <li>
303 <span class="test-name">
304 <span class="status-indicator health-indicator-dot status-error"></span>
305 {{ inc.to_status }} at {{ inc.started_at }}
306 </span>
307 <span>({{ inc.duration }})</span>
308 </li>
309 {% endfor %}
310 </ul>
311 </details>
312 {% endif %}
313
314 <h2 class="section-title">Endpoint Tests</h2>
315
316 <details class="check-list">
317 <summary>Public Endpoints ({{ public_tests.len() }})</summary>
318 <ul class="test-list">
319 {% for test in public_tests %}
320 <li>
321 <span class="test-name">{{ test.name }}</span>
322 <span class="{% if test.passed %}test-pass{% else %}test-fail{% endif %}">
323 {% if test.passed %}PASS{% else %}FAIL{% endif %}
324 ({{ test.latency_ms }}ms)
325 </span>
326 </li>
327 {% endfor %}
328 </ul>
329 </details>
330
331 <details class="check-list">
332 <summary>Database Queries ({{ db_tests.len() }})</summary>
333 <ul class="test-list">
334 {% for test in db_tests %}
335 <li>
336 <span class="test-name">{{ test.name }}</span>
337 <span class="{% if test.passed %}test-pass{% else %}test-fail{% endif %}">
338 {% if test.passed %}PASS{% else %}FAIL{% endif %}
339 ({{ test.latency_ms }}ms)
340 </span>
341 </li>
342 {% endfor %}
343 </ul>
344 </details>
345
346 <p class="timestamp">Generated at {{ generated_at }}</p>
347 </div>
348 {% endblock %}
349