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

90 lines
3.6 KiB
Python

from __future__ import annotations
import sqlite3
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
from starlette.concurrency import run_in_threadpool
from ..ddns_security import client_ip
from ..ddns_service import normalize_domain
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("/request-domain", response_class=HTMLResponse, include_in_schema=False)
async def request_domain(request: Request, settings: SettingsDep, service: ServiceDep) -> str:
form = await request_form(request, settings)
try:
challenge = await run_in_threadpool(
service.create_domain_challenge,
first_form_value(form, "domain"),
)
except sqlite3.IntegrityError as exc:
raise HTTPException(status_code=409, detail="domain is already verified") from exc
return _render_public_home(service, settings, None, None, challenge)
@router.post("/verify-domain", response_class=HTMLResponse, include_in_schema=False)
async def verify_domain(request: Request, settings: SettingsDep, service: ServiceDep) -> str:
form = await request_form(request, settings)
domain = normalize_domain(first_form_value(form, "domain"))
claim_secret = first_form_value(form, "claim_secret")
found, challenge = await run_in_threadpool(service.verify_domain, domain, claim_secret)
if not found:
message = "TXT record not found yet. DNS propagation can take a few minutes."
return _render_public_home(service, settings, None, message, challenge)
return _render_public_home(service, settings, None, "Domain verified. You can now create a hostname under it.", challenge)
@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 == "managed":
account = await run_in_threadpool(service.create_managed_account, None)
else:
claim_secret = first_form_value(form, "claim_secret")
account = await run_in_threadpool(
service.create_custom_account,
first_form_value(form, "hostname"),
claim_secret,
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() -> str:
return _render_privacy_page()