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/routers/public.py
T

98 lines
3.5 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse, Response
from starlette.concurrency import run_in_threadpool
from ..ddns_security import client_ip
from ..dependencies import ServiceDep, SettingsDep
from ..web.forms import first_form_value, request_form
from ..web.templates_public import _render_privacy_page, _render_public_home
router = APIRouter()
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
def index(settings: SettingsDep, service: ServiceDep) -> str:
return _render_public_home(service, settings, None, None)
@router.get("/magic", include_in_schema=False)
def magic_form() -> RedirectResponse:
return RedirectResponse("/#create", status_code=303)
@router.post("/magic", response_class=HTMLResponse, include_in_schema=False)
async def magic_account(request: Request, settings: SettingsDep, service: ServiceDep) -> str:
await request_form(request, settings)
account = await run_in_threadpool(service.create_managed_account, None)
return _render_public_home(service, settings, account, None)
@router.post("/accounts", response_class=HTMLResponse, include_in_schema=False)
async def self_service_account(request: Request, settings: SettingsDep, service: ServiceDep) -> str:
form = await request_form(request, settings)
mode = first_form_value(form, "mode")
if mode and mode != "managed":
raise HTTPException(status_code=400, detail="custom hostnames are not supported; create a KarlDNS hostname and use a CNAME")
if first_form_value(form, "hostname") or first_form_value(form, "claim_secret"):
raise HTTPException(status_code=400, detail="custom hostnames are not supported; create a KarlDNS hostname and use a CNAME")
account = await run_in_threadpool(service.create_managed_account, None)
return _render_public_home(service, settings, account, None)
@router.get("/healthz", response_class=PlainTextResponse)
def healthz() -> str:
return "ok"
@router.get("/ip", response_class=PlainTextResponse)
def visible_ip(request: Request, settings: SettingsDep) -> str:
return client_ip(request, settings)
@router.get("/privacy", response_class=HTMLResponse, include_in_schema=False)
def privacy(settings: SettingsDep) -> str:
return _render_privacy_page(settings)
@router.get("/robots.txt", response_class=PlainTextResponse, include_in_schema=False)
def robots_txt(settings: SettingsDep) -> str:
base_url = settings.public_base_url.rstrip("/")
admin_path = settings.admin_path.rstrip("/")
return "\n".join(
[
"User-agent: *",
f"Disallow: {admin_path}/",
"Disallow: /m/",
"Disallow: /u/",
"Disallow: /nic/",
"Disallow: /api/",
"Disallow: /docs",
"Disallow: /redoc",
"Disallow: /openapi.json",
f"Sitemap: {base_url}/sitemap.xml",
"",
]
)
@router.get("/sitemap.xml", include_in_schema=False)
def sitemap_xml(settings: SettingsDep) -> Response:
base_url = settings.public_base_url.rstrip("/")
body = f"""<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>{base_url}/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>{base_url}/privacy</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
</urlset>
"""
return Response(body, media_type="application/xml")