from __future__ import annotations import json from .html_utils import SafeHtml, attr, escape, join_html, trusted def _svg_icon(name: str, css_class: str = "copy-icon") -> SafeHtml: if name == "check": path = '' elif name == "more": path = ( '' ) else: path = ( '' ) return trusted( f'' ) def _top_nav(label: str | None = None, links: SafeHtml | None = None) -> SafeHtml: account_html = "" if label: account_html = f'{escape(label)}' if links is None: links = trusted('DocsAPI') theme_toggle = """ """ return trusted( f""" """ ) def _copy_row(label: str, value: str, sensitivity: str | None = None) -> SafeHtml: safe_label = escape(label) safe_value = escape(value) badge = "" is_secret = bool(sensitivity and sensitivity.lower() == "secret") if sensitivity: safe_sensitivity = escape(sensitivity) badge_class = "text-bg-warning" if is_secret else "text-bg-secondary" badge = f'{safe_sensitivity}' value_field = ( f'' if is_secret else f'{safe_value}' ) reveal_button = ( f'' if is_secret else "" ) return trusted( f"""
{safe_label}{badge} {value_field} {reveal_button}
""" ) def _copy_button(label: str, value: str, *, style: str = "outline-primary") -> SafeHtml: safe_label = escape(label) safe_value = attr(value) safe_style = attr(style) return trusted( f'' ) def _copy_icon_button(label: str, value: str) -> SafeHtml: safe_label = escape(label) safe_value = attr(value) return trusted( f'' ) def _copy_menu(label: str, items: list[tuple[str, str]]) -> SafeHtml: safe_label = escape(label) buttons = [] for item_label, item_value in items: buttons.append( f'' ) return trusted( f"""
{_svg_icon("more", "more-icon")}
""" ) def _code_window(commands: str, *, copy_label: str = "code", class_name: str = "") -> SafeHtml: safe_class = f" {attr(class_name)}" if class_name else "" code_lines = "\n".join(f"{escape(line)}" for line in commands.splitlines()) return trusted( f"""
{_copy_icon_button(copy_label, commands)} {code_lines}
""" ) def _manual_update_commands(update_url: str) -> str: update_base = update_url.split("?", 1)[0] return "\n".join( [ f"curl -sS '{update_base}?myip=203.0.113.10'", f"curl -sS '{update_base}?myipv6=2001:db8::10'", f"curl -sS '{update_base}' # use request IP", ] ) def _manual_update_panel(commands: str) -> SafeHtml: return trusted( f"""

Manual updates

FRITZ!Box fills in the IP placeholders automatically. Scripts can use curl. If no IP is sent, KarlDNS uses the request IP.

{_code_window(commands, copy_label="manual update commands", class_name="manual-code")}
""" ) def _fritzbox_fields( update_url: str, hostname: str, username: str, password: str, management_url: str | None = None, ) -> str: lines = [ f"Update-URL: {update_url}", f"Domainnamen: {hostname}", f"CNAME target: {hostname}", f"Benutzername: {username}", f"Kennwort: {password}", ] if management_url: lines.append(f"Private dashboard: {management_url}") return "\n".join(lines) def _secret_notice() -> SafeHtml: return trusted( """
Public: Hostname and CNAME target can be shared. Secret: Update URLs, router password, and dashboard link can update or manage this hostname. Secrets are hidden on screen. Copy still copies the real value.
""" ) def _one_time_secret_notice() -> SafeHtml: return trusted( """ """ ) def _message_band(message: str | None) -> SafeHtml: if not message: return trusted("") return trusted( f"""
{escape(message)}
""" ) def _site_footer() -> SafeHtml: return trusted( """ """ ) def _page( title: str, body: SafeHtml, extra_styles: SafeHtml | None = None, extra_scripts: SafeHtml | None = None, description: str = "KarlDNS is a small self-hosted Dynamic DNS service for FRITZ!Box routers, Linux cron jobs, and home networks.", preload_logo: bool = False, canonical_url: str | None = None, image_url: str | None = None, image_alt: str | None = None, structured_data: dict[str, object] | None = None, ) -> str: if not isinstance(body, SafeHtml): raise TypeError("body must be a SafeHtml fragment built by a template helper") extra_styles_html = extra_styles or trusted("") extra_scripts_html = extra_scripts or trusted("") preload_logo_html = trusted('') if preload_logo else trusted("") canonical_html = trusted(f'') if canonical_url else trusted("") social_url = canonical_url or "" social_image = image_url or "" social_html = trusted("") if social_url: social_card = "summary_large_image" if social_image else "summary" social_image_alt = image_alt or title social_image_html = f""" """ if social_image else "" social_html = trusted(f""" {social_image_html} """) structured_data_html = trusted("") if structured_data: structured_data_html = trusted( '" ) return f""" {canonical_html} {social_html} {escape(title)} {preload_logo_html} {extra_scripts_html} {extra_styles_html} {structured_data_html} {body}{_site_footer()} """ __all__ = [ "SafeHtml", "_copy_button", "_copy_icon_button", "_copy_menu", "_copy_row", "_code_window", "_fritzbox_fields", "_manual_update_commands", "_manual_update_panel", "_message_band", "_one_time_secret_notice", "_page", "_secret_notice", "_top_nav", "join_html", "trusted", ]