This repository has been archived on 2026-10-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
karldns.de/router_dyndns/web/templates_public.py
T

177 lines
8.7 KiB
Python

from __future__ import annotations
from ..ddns_models import DdnsSettings
from ..ddns_service import DdnsService
from .html_utils import escape, trusted
from .templates import _message_band, _page, _top_nav
from .templates_public_sections import _credentials_panel, _custom_domain_flow, _linux_installer_section
def _render_public_home(
service: DdnsService,
settings: DdnsSettings,
created_account: dict[str, str] | None,
message: str | None,
challenge: dict[str, str | None] | None = None,
) -> str:
base_url = settings.public_base_url.rstrip("/")
suffix = escape(settings.hostname_suffix or "your DynDNS domain")
demo_update_url = escape(f"{base_url}/u/<secret>?myip=<ipaddr>")
created_html = _credentials_panel(service, created_account)
message_html = _message_band(message) if message and not challenge else ""
challenge_html = _custom_domain_flow(service, challenge, message)
description = "KarlDNS is a free self-hosted Dynamic DNS service for FRITZ!Box routers, custom domains, Linux cron jobs, and home networks."
return _page(
"KarlDNS",
trusted(f"""
<main id="main">
{_top_nav()}
<section class="hero-band">
<div class="container text-center d-grid justify-items-center gap-3">
<img class="hero-logo" src="/logo.webp?v=3" alt="" width="80" height="80" decoding="async" fetchpriority="high">
<h1>Dynamic DNS for home routers.</h1>
<div class="install-pill mx-auto">
<code>{demo_update_url}</code>
</div>
<p class="lead">KarlDNS gives you a private DynDNS update URL under {suffix}. Add it to your router, or use it from a script.</p>
<div class="d-flex gap-2 flex-wrap justify-content-center mt-2">
<a class="btn btn-primary rounded-pill" href="#create">Create hostname</a>
<a class="btn btn-outline-primary rounded-pill" href="#manual-updates">Manual updates</a>
</div>
</div>
</section>
{message_html}
{created_html}
<section class="section" id="create">
<div class="container narrow">
<div class="text-center mb-4">
<p class="eyebrow">Hostname</p>
<h2>No account needed.</h2>
<p class="section-copy mx-auto">Use a KarlDNS hostname now, or verify your own domain when you need it.</p>
</div>
<div class="row g-4 align-items-stretch">
<div class="col-lg-6">
<div class="card card-body h-100 gap-3">
<p class="eyebrow">KarlDNS domain</p>
<h3>Create a hostname</h3>
<p class="section-copy mb-0">You get a hostname, a private update URL, and router fields if your router needs them.</p>
<form method="post" action="/magic" class="d-grid gap-3" aria-label="Create a generated DynDNS hostname">
<button type="submit" class="btn btn-primary rounded-pill">Create hostname</button>
</form>
</div>
</div>
<div class="col-lg-6">
<div class="card card-body h-100 gap-3">
<p class="eyebrow">Use your own domain</p>
<h3>Verify your domain</h3>
<p class="section-copy mb-0">Add one TXT record first. KarlDNS only updates domains it can verify.</p>
<form method="post" action="/request-domain" class="d-grid gap-3" aria-label="Request a custom domain TXT challenge">
<label>Domain
<input class="form-control" name="domain" placeholder="example.com" autocomplete="off" autocapitalize="none" spellcheck="false" required>
</label>
<button type="submit" class="btn btn-primary rounded-pill">Create TXT record</button>
</form>
</div>
</div>
</div>
<div class="terminal-card mt-4" aria-hidden="true">
<code>$ curl -sS 'https://karldns.de/u/&lt;secret&gt;?myip=203.0.113.10'</code>
<code>good 203.0.113.10</code>
</div>
</div>
</section>
<section class="section" id="manual-updates">
<div class="container narrow text-center">
<h2>Update from a router or script.</h2>
<p class="lead mx-auto mt-3">Works with FRITZ!Box, curl, cron, Home Assistant, OpenWrt, pfSense, OPNsense, UniFi, MikroTik, and custom tools.</p>
<div class="terminal-card text-start mt-4">
<code># explicit IPv4</code>
<code>curl -sS 'https://karldns.de/u/&lt;secret&gt;?myip=203.0.113.10'</code>
<code># source IP fallback</code>
<code>curl -sS 'https://karldns.de/u/&lt;secret&gt;'</code>
</div>
</div>
</section>
{_linux_installer_section()}
{challenge_html}
</main>
"""),
description=description,
preload_logo=True,
canonical_url=f"{base_url}/",
image_url=f"{base_url}/social-preview.png",
image_alt="KarlDNS landing page with private Dynamic DNS update URL",
structured_data={
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "KarlDNS",
"applicationCategory": "NetworkingApplication",
"operatingSystem": "Linux",
"url": f"{base_url}/",
"description": description,
"image": f"{base_url}/social-preview.png",
"offers": {"@type": "Offer", "price": "0", "priceCurrency": "EUR"},
"featureList": [
"Dynamic DNS for FRITZ!Box routers",
"Accountless private update URLs",
"Custom domain TXT verification",
"Linux cron updater",
],
},
)
def _render_privacy_page(settings: DdnsSettings) -> str:
base_url = settings.public_base_url.rstrip("/")
description = "Privacy and legal information for KarlDNS, including stored hostnames, update links, DNS verification records, IP history, and cleanup behavior."
return _page(
"Legal and Privacy",
trusted(f"""
<main id="main">
{_top_nav()}
<section class="hero-band compact">
<div class="container text-center d-grid justify-items-center gap-3">
<p class="eyebrow">Legal and privacy</p>
<h1>Privacy basics.</h1>
<p class="lead">KarlDNS stores only the data needed to keep your hostname updated.</p>
</div>
</section>
<section class="section">
<div class="container narrow d-grid gap-4">
<div>
<h2>Data processed</h2>
<p class="section-copy">KarlDNS stores hostnames, update links, protected dashboard tokens, protected router passwords, TXT verification records, recent IP history, timestamps, and service events.</p>
</div>
<div>
<h2>Secrets</h2>
<p class="section-copy">Dashboard links, update URLs, router passwords, full update URLs, and claim links are secrets. Anyone with them may update or manage the hostname. Passwords and dashboard lookup values are not stored as readable text.</p>
</div>
<div>
<h2>Logs and retention</h2>
<p class="section-copy">KarlDNS keeps recent update events for troubleshooting. Cleanup removes abandoned setup records and unused hostnames. Server logs may also include IP addresses and request details.</p>
</div>
<div>
<h2>Contact</h2>
<p class="section-copy">For the public KarlDNS instance, contact Karl through <a href="https://karl.fail" rel="noopener">karl.fail</a> or <a href="https://karlcom.de" rel="noopener">karlcom.de</a>.</p>
</div>
</div>
</section>
</main>
"""),
description=description,
canonical_url=f"{base_url}/privacy",
image_url=f"{base_url}/social-preview.png",
image_alt="KarlDNS legal and privacy information",
structured_data={
"@context": "https://schema.org",
"@type": "WebPage",
"name": "KarlDNS Legal and Privacy",
"url": f"{base_url}/privacy",
"description": description,
"isPartOf": {
"@type": "WebSite",
"name": "KarlDNS",
"url": f"{base_url}/",
},
},
)