feat: Add new updated version
All checks were successful
Build Docker / BuildImage (push) Successful in 1m0s

This commit is contained in:
2025-11-21 15:58:21 +11:00
parent f936973b8d
commit ff3f40beaf
9 changed files with 1049 additions and 426 deletions

View File

@@ -4,17 +4,63 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Status | HNS DoH</title>
<link rel="icon" href="/assets/img/HNS.png" type="image/png">
<link rel="stylesheet" href="/assets/css/404.css">
<title>Page Not Found - HNSDoH Status</title>
<link rel="stylesheet" href="/assets/style.css">
<link rel="icon" type="image/png" href="/favicon.png">
<style>
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 60vh;
text-align: center;
}
.error-code {
font-size: 6rem;
font-weight: 800;
color: var(--bg-card-hover);
line-height: 1;
margin-bottom: 1rem;
}
.error-title {
font-size: 2rem;
font-weight: 700;
margin-bottom: 1rem;
}
.error-msg {
color: var(--text-muted);
margin-bottom: 2rem;
max-width: 400px;
}
.btn {
display: inline-block;
background-color: var(--accent);
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
transition: opacity 0.2s;
}
.btn:hover {
opacity: 0.9;
text-decoration: none;
}
</style>
</head>
<body>
<div class="spacer"></div>
<div class="centre">
<h1>404 | Page not found</h1>
<p>Sorry, the page you are looking for does not exist.</p>
<p><a href="/">Go back to the homepage</a></p>
<div class="container">
<div class="error-container">
<div class="error-code">404</div>
<div class="error-title">Page Not Found</div>
<div class="error-msg">The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.</div>
<a href="/" class="btn">Return Home</a>
</div>
<div class="footer">
<p>Powered by <a href="https://nathan.woodburn.au">Nathan.Woodburn/</a></p>
</div>
</div>
</body>

View File

@@ -4,41 +4,100 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API | HNS DoH</title>
<link rel="icon" href="/assets/img/HNS.png" type="image/png">
<link rel="stylesheet" href="/assets/css/api.css">
<title>API Documentation - HNSDoH Status</title>
<link rel="stylesheet" href="/assets/style.css">
<link rel="icon" type="image/png" href="/favicon.png">
<style>
.endpoint-card {
background-color: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.endpoint-method {
display: inline-block;
background-color: rgba(59, 130, 246, 0.15);
color: var(--accent);
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-weight: 700;
font-size: 0.75rem;
margin-right: 0.75rem;
}
.endpoint-route {
font-family: monospace;
font-size: 1.1rem;
color: var(--text-main);
}
.endpoint-desc {
margin-top: 0.75rem;
color: var(--text-muted);
}
.param-table {
width: 100%;
margin-top: 1rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
overflow: hidden;
}
.param-table th { background-color: rgba(0,0,0,0.2); }
.param-table td, .param-table th { padding: 0.75rem; border-bottom: 1px solid var(--border); }
.param-table tr:last-child td { border-bottom: none; }
.back-link { display: inline-block; margin-bottom: 1rem; }
</style>
</head>
<body>
<div class="spacer"></div>
<div class="centre">
{% if endpoints %}
<h1 style="font-size: xx-large;">API Info</h1>
<p>Available endpoints:</p>
<ul style="width: fit-content;margin: auto;">
{% for endpoint in endpoints %}
<li class="top">
<strong>{{ endpoint.route }}</strong> - {{ endpoint.description }}
{% if endpoint.parameters %}
<ul>
<li><em>Parameters:</em></li>
{% for param in endpoint.parameters %}
<li>
<strong>{{ param.name }}:</strong>
({{ param.type }}) - {{ param.description }}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
<div class="container">
<header>
<div>
<h1>API Documentation</h1>
<div class="meta">Programmatic access to status data</div>
</div>
<div>
<a href="/" class="back-link">← Back to Dashboard</a>
</div>
</header>
{% else %}
<h1>404 | Page not found</h1>
<p>Sorry, the page you are looking for does not exist.</p>
<p><a href="/">Go back to the homepage</a></p>
{% endif %}
<div class="endpoints-list">
{% for endpoint in endpoints %}
<div class="endpoint-card">
<div>
<span class="endpoint-method">GET</span>
<a href="{{ endpoint.route }}" class="endpoint-route">{{ endpoint.route }}</a>
</div>
<div class="endpoint-desc">{{ endpoint.description }}</div>
{% if endpoint.parameters %}
<div class="param-table">
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for param in endpoint.parameters %}
<tr>
<td><code>{{ param.name }}</code></td>
<td><code>{{ param.type }}</code></td>
<td>{{ param.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="footer">
<p>Powered by <a href="https://nathan.woodburn.au">Nathan.Woodburn/</a></p>
</div>
</div>
</body>

133
templates/assets/style.css Normal file
View File

@@ -0,0 +1,133 @@
:root {
--bg-body: #0f172a;
--bg-card: #1e293b;
--bg-card-hover: #334155;
--text-main: #f1f5f9;
--text-muted: #94a3b8;
--border: #334155;
--accent: #3b82f6;
--success: #22c55e;
--error: #ef4444;
--warning: #f59e0b;
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: system-ui, -apple-system, sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
line-height: 1.5;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
margin-bottom: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border);
padding-bottom: 1rem;
}
h1 { font-size: 1.5rem; font-weight: 700; }
h2 { font-size: 1.25rem; margin-bottom: 1rem; color: var(--text-main); }
.meta { color: var(--text-muted); font-size: 0.875rem; }
/* Alerts */
.alerts-section { margin-bottom: 2rem; }
.alert-box {
padding: 1rem;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
font-weight: 500;
}
.alert-box.error { background-color: rgba(239, 68, 68, 0.1); border: 1px solid var(--error); color: #fca5a5; }
.alert-box.warning { background-color: rgba(245, 158, 11, 0.1); border: 1px solid var(--warning); color: #fcd34d; }
/* Grid */
.node-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.card {
background-color: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: var(--shadow);
transition: transform 0.2s, border-color 0.2s;
}
.card:hover {
transform: translateY(-2px);
border-color: var(--text-muted);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border);
}
.node-name { font-weight: 600; font-size: 1.125rem; }
.node-location { font-size: 0.875rem; color: var(--text-muted); }
.node-ip { font-family: monospace; font-size: 0.75rem; color: var(--text-muted); background: rgba(0,0,0,0.2); padding: 2px 6px; border-radius: 4px; }
.status-list { display: flex; flex-direction: column; gap: 0.75rem; }
.status-item { display: flex; justify-content: space-between; align-items: center; font-size: 0.875rem; }
.badge {
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.badge.up { background-color: rgba(34, 197, 94, 0.15); color: var(--success); }
.badge.down { background-color: rgba(239, 68, 68, 0.15); color: var(--error); }
.cert-info { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.25rem; text-align: right; }
/* History Table */
.table-container {
background-color: var(--bg-card);
border-radius: 0.75rem;
border: 1px solid var(--border);
overflow-x: auto;
}
table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
th { background-color: rgba(0,0,0,0.2); font-weight: 600; color: var(--text-muted); }
tr:last-child td { border-bottom: none; }
tr:hover td { background-color: var(--bg-card-hover); }
.uptime-bar {
height: 6px;
background-color: var(--bg-body);
border-radius: 3px;
overflow: hidden;
width: 100px;
margin-top: 4px;
}
.uptime-fill { height: 100%; background-color: var(--success); }
.uptime-fill.warn { background-color: var(--warning); }
.uptime-fill.bad { background-color: var(--error); }
.footer { margin-top: 4rem; text-align: center; color: var(--text-muted); font-size: 0.875rem; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

View File

@@ -1,174 +1,128 @@
<!DOCTYPE html>
<html data-bs-theme="dark" lang="en-au">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Status | HNS DoH</title>
<meta name="twitter:image" content="https://status.hnsdoh.com/assets/img/HNS.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:description" content="Access Handshake Domains with DNS over HTTPS">
<meta property="og:title" content="Status | HNS DoH">
<meta name="description" content="Access Handshake Domains with DNS over HTTPS">
<meta property="og:type" content="website">
<meta property="og:description" content="Access Handshake Domains with DNS over HTTPS">
<meta name="twitter:title" content="Status | HNS DoH">
<meta property="og:image" content="https://status.hnsdoh.com/assets/img/HNS.png">
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "WebSite",
"name": "Status | HNS DoH",
"url": "https://status.hnsdoh.com"
}
</script>
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNSW.png" media="(prefers-color-scheme: dark)">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="icon" type="image/png" sizes="670x700" href="assets/img/HNS.png">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/bs-theme-overrides.css">
<link rel="stylesheet" href="assets/css/Navbar-Right-Links-Dark-icons.css">
<link rel="stylesheet" href="assets/css/Team-images.css">
<link rel="stylesheet" href="assets/css/index.css">
<link rel="manifest" href="manifest.json">
<script async src="https://umami.woodburn.au/script.js"
data-website-id="7e0ed7e4-3858-4124-a574-b57ac05aaad1"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HNSDoH Status</title>
<link rel="stylesheet" href="/assets/style.css">
<link rel="icon" type="image/png" href="/favicon.png">
</head>
<body>
<header>
<nav class="navbar navbar-expand-md fixed-top bg-dark py-3" data-bs-theme="dark">
<div class="container-fluid"><a class="navbar-brand d-flex align-items-center" href="https://welcome.hnsdoh.com"><span
class="bs-icon-sm bs-icon-rounded bs-icon-primary d-flex justify-content-center align-items-center me-2 bs-icon"><img
src="assets/img/HNSW.png" width="20px"></span><span>HNS DoH</span></a></div>
</nav>
</header>
<div style="margin: 100px;"></div>
<section id="intro">
<div class="text-center">
<h1 class="text-center" style="font-size: 60px;">HNS DoH Status</h1>
<div class="errors">
<!-- Check if errors is empty -->
{% if alerts %}
<div class="alert alert-danger" role="alert">
<h4 class="alert-heading">Alert</h4>
{% for alert in alerts %}
<p>{{ alert }}</p>
{% endfor %}
</div>
{% endif %}
</div>
<div class="warnings">
<!-- Check if warnings is empty -->
{% if warnings %}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Warning</h4>
{% for warning in warnings %}
<p>{{ warning }}</p>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</section>
<section id="status"></section>
<div class="text-center" style="width: fit-content;margin: auto;max-width: 100%;">
<span style="font-size: smaller;margin-bottom: 10px;">Last check: {{last_check}}</span>
<div class="spacer"></div>
<div class="node" style="display: block;">
<div class="container">
<header>
<div>
<h2>Overall Stats</h2>
<h1>HNSDoH Status</h1>
<div class="meta">Handshake DNS over HTTPS/TLS</div>
</div>
<div class="node-info">
<p>Plain DNS: {{history.overall.plain_dns.percentage}}% uptime (last down
{{history.overall.plain_dns.last_down}})</p>
<p>DNS over HTTPS: {{history.overall.doh.percentage}}% uptime (last down
{{history.overall.doh.last_down}})</p>
<p>DNS over TLS: {{history.overall.dot.percentage}}% uptime (last down
{{history.overall.dot.last_down}})</p>
<div class="meta">
Last checked: {{ last_check }}
</div>
</header>
{% if alerts or warnings %}
<div class="alerts-section">
{% for alert in alerts %}
<div class="alert-box error">{{ alert }}</div>
{% endfor %}
{% for warning in warnings %}
<div class="alert-box warning">{{ warning }}</div>
{% endfor %}
</div>
<div class="spacer"></div>
{% for node in nodes %}
<div class="node {{node.class}}">
<div>
<h2>{{node.location}}</h2>
</div>
<div class="node-info">
<h5>Current Status</h5>
<p>Plain DNS: {{node.plain_dns}}</p>
<p>DNS over HTTPS: {{node.doh}}</p>
<p>DNS over TLS: {{node.dot}}</p>
<p>Certificate: {% if node.cert.valid %} Valid {% else %} Invalid {% endif %} (expires
{{node.cert.expires}})</p>
</div>
<div class="node-info">
<h5>Stats</h5>
<p>Plain DNS: {{history.nodes[node.ip].plain_dns.percentage}}% uptime (last down
{{history.nodes[node.ip].plain_dns.last_down}})</p>
<p>DNS over HTTPS: {{history.nodes[node.ip].doh.percentage}}% uptime (last down
{{history.nodes[node.ip].doh.last_down}})</p>
<p>DNS over TLS: {{history.nodes[node.ip].dot.percentage}}% uptime (last down
{{history.nodes[node.ip].dot.last_down}})</p>
</div>
<div class="node-info">
<p style="font-weight: bold;">{{node.name}}: {{node.ip}}</p>
{% endif %}
<h2>Node Status</h2>
<div class="node-grid">
{% for node in nodes %}
<div class="card">
<div class="card-header">
<div>
<div class="node-name">{{ node.name }}</div>
<div class="node-location">{{ node.location }}</div>
</div>
<div class="node-ip">{{ node.ip }}</div>
</div>
<div class="status-list">
<div class="status-item">
<span>Plain DNS</span>
<span class="badge {{ 'up' if node.plain_dns else 'down' }}">
{{ 'UP' if node.plain_dns else 'DOWN' }}
</span>
</div>
<div class="status-item">
<span>DoH (443)</span>
<span class="badge {{ 'up' if node.doh else 'down' }}">
{{ 'UP' if node.doh else 'DOWN' }}
</span>
</div>
<div class="status-item">
<span>DoT (853)</span>
<span class="badge {{ 'up' if node.dot else 'down' }}">
{{ 'UP' if node.dot else 'DOWN' }}
</span>
</div>
<div class="status-item">
<span>Certificate</span>
<div style="text-align: right;">
<span class="badge {{ 'up' if node.cert.valid else 'down' }}">
{{ 'VALID' if node.cert.valid else 'INVALID' }}
</span>
{% if node.cert.valid %}
<div class="cert-info">Exp: {{ node.cert.expires }}</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
<h2>30-Day History</h2>
<div class="table-container">
<table>
<thead>
<tr>
<th>Node</th>
<th style="text-align: center;">Plain DNS</th>
<th style="text-align: center;">DoH</th>
<th style="text-align: center;">DoT</th>
</tr>
</thead>
<tbody>
{% for node in history.nodes %}
<tr>
<td>
<div class="node-name">{{ node.name }}</div>
<div class="node-location">{{ node.location }}</div>
</td>
{% for type in ['plain_dns', 'doh', 'dot'] %}
<td style="text-align: center;">
<div>{{ node[type].percentage }}%</div>
<div class="uptime-bar" style="margin: 4px auto 0;">
<div class="uptime-fill {% if node[type].percentage < 90 %}warn{% endif %} {% if node[type].percentage < 70 %}bad{% endif %}"
style="width: {{ node[type].percentage }}%"></div>
</div>
{% if node[type].last_down != 'never' %}
<div class="cert-info" style="text-align: center;">Last outage: {{ node[type].last_down }}</div>
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="footer">
<p>Powered by <a href="https://nathan.woodburn.au">Nathan.Woodburn/</a></p>
<p><a href="/api">API Access</a></p>
</div>
</div>
</section>
<section id="setup"
style="min-height: 400px;padding-top: 10vh;text-align: center;margin-right: 10%;margin-left: 10%;"
data-bs-target="#navcol-5" data-bs-smooth-scroll="true">
<h3 class="display-1">Setup</h3>
<ul class="list-group">
<li class="list-group-item">
<div>
<h5 class="display-5">DNS over HTTPS</h5>
<p>DNS over HTTPS is supported by most browsers. To add HNSDoH to your revolvers add this URL to
your Secure DNS setting<br><code>https://hnsdoh.com/dns-query</code></p>
</div>
</li>
<li class="list-group-item">
<div>
<h5 class="display-5">DNS over TLS</h5>
<p>DNS over TLS is the best option for mobile phones. Simply set Private DNS to the
hostname&nbsp;<br><code>hnsdoh.com</code></p>
</div>
</li>
<li class="list-group-item">
<div>
<h5 class="display-5">Plain DNS</h5>
<p>As a last resort you can use any of plain DNS below (best to chose 2 IPs from different
people)<br><br>- 194.50.5.27 (powered by Nathan.Woodburn/)<br>-&nbsp;139.177.195.185 (powered by
HNS Canada)<br>-&nbsp;172.233.46.92 (powered by Nathan.Woodburn/)<br>-&nbsp;172.105.120.203 (powered
by Nathan.Woodburn/)<br>-&nbsp;18.169.98.42 (powered by Easy HNS)<br><br>Alternative Providers (Not
running the HNSDoH software configuration)<br><br>- 194.50.5.26 (powered by
Nathan.Woodburn/)<br>- 194.50.5.28 (powered by Nathan.Woodburn/)<br>-&nbsp;139.144.68.241
(powered by HNS DNS)<br>- 139.144.68.242 (powered by HNS DNS)<br>- 2a01:7e01:e002:c300::
(powered by HNS DNS)<br>- 2a01:7e01:e002:c500:: (powered by HNS DNS)</p>
</div>
</li>
</ul>
</section>
<footer class="text-center bg-dark">
<div class="container text-white py-4 py-lg-5">
<p class="text-muted mb-0">Copyright © 2024 HNSDoH</p>
</div>
</footer>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
</body>
</html>