Archived
Refactor UI assets and harden quality checks
This commit is contained in:
1 parent
23ab466c52
commit
4ecc8eb874
62 files changed
+3314
-3206
No files matched your search
+112
-49
@@ -31,6 +31,35 @@ class UpdateOutcome:
|
||||
source_ip: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class UpdateIpParams:
|
||||
myip: str | None = None
|
||||
ipaddr: str | None = None
|
||||
myipv6: str | None = None
|
||||
ip6addr: str | None = None
|
||||
|
||||
@property
|
||||
def initial_plan_args(self) -> tuple[str | None, str | None, str | None, str | None]:
|
||||
return self.myip, self.ipaddr, self.myipv6, self.ip6addr
|
||||
|
||||
def with_body_values(self, body_values: Mapping[str, str]) -> UpdateIpParams:
|
||||
return UpdateIpParams(
|
||||
myip=self.myip or body_values.get("myip"),
|
||||
ipaddr=self.ipaddr or body_values.get("ipaddr"),
|
||||
myipv6=self.myipv6 or body_values.get("myipv6"),
|
||||
ip6addr=self.ip6addr or body_values.get("ip6addr"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class InitialDnsPlan:
|
||||
ipv4: str | None
|
||||
ipv6: str | None
|
||||
update_ipv4: bool
|
||||
update_ipv6: bool
|
||||
detail: str
|
||||
|
||||
|
||||
class BadIpError(ValueError):
|
||||
"""Raised when a supplied DynDNS IP parameter is invalid."""
|
||||
|
||||
@@ -71,36 +100,21 @@ class DdnsService:
|
||||
self,
|
||||
request: Request,
|
||||
hostname: str,
|
||||
myip: str | None,
|
||||
ipaddr: str | None,
|
||||
myipv6: str | None,
|
||||
ip6addr: str | None,
|
||||
ip_params: UpdateIpParams,
|
||||
) -> bool:
|
||||
if not dns_backend_configured(self.settings):
|
||||
return False
|
||||
source_ip = client_ip(request, self.settings)
|
||||
source_fallback = source_ip if _is_global_ip(source_ip) else ""
|
||||
supplied_ip = any(value and value.strip() for value in (myip, ipaddr, myipv6, ip6addr))
|
||||
ipv4: str | None
|
||||
ipv6: str | None
|
||||
if not supplied_ip and not source_fallback:
|
||||
ipv4 = INITIAL_PLACEHOLDER_IPV4
|
||||
ipv6 = None
|
||||
update_ipv4 = True
|
||||
update_ipv6 = False
|
||||
detail = "initial placeholder dns registered"
|
||||
else:
|
||||
ipv4, ipv6, update_ipv4, update_ipv6 = parse_update_ips(
|
||||
source_fallback,
|
||||
myip,
|
||||
ipaddr,
|
||||
myipv6,
|
||||
ip6addr,
|
||||
)
|
||||
detail = "initial dns registered"
|
||||
plan = _initial_dns_plan(source_ip, *ip_params.initial_plan_args)
|
||||
lock = self._lock_for(hostname)
|
||||
with lock:
|
||||
planned = self.store.preview_update(hostname, ipv4, ipv6, update_ipv4=update_ipv4, update_ipv6=update_ipv6)
|
||||
planned = self.store.preview_update(
|
||||
hostname,
|
||||
plan.ipv4,
|
||||
plan.ipv6,
|
||||
update_ipv4=plan.update_ipv4,
|
||||
update_ipv6=plan.update_ipv6,
|
||||
)
|
||||
if planned.changed:
|
||||
try:
|
||||
publish_dns(planned, self.settings)
|
||||
@@ -113,7 +127,12 @@ class DdnsService:
|
||||
type(exc).__name__,
|
||||
)
|
||||
self.store.log_update_event(
|
||||
hostname, ipv4, ipv6, DNS_RECONCILE_FAILED, f"{type(exc).__name__}: {exc}", source_ip
|
||||
hostname,
|
||||
plan.ipv4,
|
||||
plan.ipv6,
|
||||
DNS_RECONCILE_FAILED,
|
||||
f"{type(exc).__name__}: {exc}",
|
||||
source_ip,
|
||||
)
|
||||
raise
|
||||
except DnsProviderError as exc:
|
||||
@@ -125,21 +144,23 @@ class DdnsService:
|
||||
type(exc).__name__,
|
||||
)
|
||||
self.store.log_update_event(
|
||||
hostname, ipv4, ipv6, DNS_FAILED, f"{type(exc).__name__}: {exc}", source_ip
|
||||
hostname,
|
||||
plan.ipv4,
|
||||
plan.ipv6,
|
||||
DNS_FAILED,
|
||||
f"{type(exc).__name__}: {exc}",
|
||||
source_ip,
|
||||
)
|
||||
raise
|
||||
result = self.store.commit_update_result(planned)
|
||||
self.store.log_update_event(hostname, result.ipv4, result.ipv6, UPDATED, detail, source_ip)
|
||||
self.store.log_update_event(hostname, result.ipv4, result.ipv6, UPDATED, plan.detail, source_ip)
|
||||
return True
|
||||
|
||||
def apply_update(
|
||||
self,
|
||||
request: Request,
|
||||
hostname: str,
|
||||
myip: str | None,
|
||||
ipaddr: str | None,
|
||||
myipv6: str | None,
|
||||
ip6addr: str | None,
|
||||
ip_params: UpdateIpParams,
|
||||
) -> UpdateOutcome:
|
||||
started_at = time.perf_counter()
|
||||
is_success = False
|
||||
@@ -150,10 +171,7 @@ class DdnsService:
|
||||
try:
|
||||
ipv4, ipv6, update_ipv4, update_ipv6 = parse_update_ips(
|
||||
source_ip,
|
||||
myip,
|
||||
ipaddr,
|
||||
myipv6,
|
||||
ip6addr,
|
||||
*ip_params.initial_plan_args,
|
||||
)
|
||||
lock = self._lock_for(hostname)
|
||||
with lock:
|
||||
@@ -248,13 +266,8 @@ class DdnsService:
|
||||
|
||||
|
||||
def normalize_ip(value: str | None, version: int | None = None) -> str | None:
|
||||
if not value or not value.strip():
|
||||
return None
|
||||
try:
|
||||
ip = ipaddress.ip_address(value.strip())
|
||||
except ValueError:
|
||||
return None
|
||||
if version and ip.version != version:
|
||||
ip = _optional_ip(value)
|
||||
if ip is None or (version and ip.version != version):
|
||||
return None
|
||||
return str(ip)
|
||||
|
||||
@@ -280,6 +293,42 @@ def parse_update_ips(
|
||||
return ipv4, ipv6, update_ipv4, update_ipv6
|
||||
|
||||
|
||||
def _initial_dns_plan(
|
||||
source_ip: str,
|
||||
myip: str | None,
|
||||
ipaddr: str | None,
|
||||
myipv6: str | None,
|
||||
ip6addr: str | None,
|
||||
) -> InitialDnsPlan:
|
||||
source_fallback = source_ip if _is_global_ip(source_ip) else ""
|
||||
if not _has_supplied_ip(myip, ipaddr, myipv6, ip6addr) and not source_fallback:
|
||||
return InitialDnsPlan(
|
||||
ipv4=INITIAL_PLACEHOLDER_IPV4,
|
||||
ipv6=None,
|
||||
update_ipv4=True,
|
||||
update_ipv6=False,
|
||||
detail="initial placeholder dns registered",
|
||||
)
|
||||
ipv4, ipv6, update_ipv4, update_ipv6 = parse_update_ips(
|
||||
source_fallback,
|
||||
myip,
|
||||
ipaddr,
|
||||
myipv6,
|
||||
ip6addr,
|
||||
)
|
||||
return InitialDnsPlan(
|
||||
ipv4=ipv4,
|
||||
ipv6=ipv6,
|
||||
update_ipv4=update_ipv4,
|
||||
update_ipv6=update_ipv6,
|
||||
detail="initial dns registered",
|
||||
)
|
||||
|
||||
|
||||
def _has_supplied_ip(*values: str | None) -> bool:
|
||||
return any(value and value.strip() for value in values)
|
||||
|
||||
|
||||
def _is_global_ip(value: str | None) -> bool:
|
||||
if not value:
|
||||
return False
|
||||
@@ -294,17 +343,31 @@ def _first_valid_ip(params: tuple[tuple[str, str | None], ...], version: int) ->
|
||||
for name, value in params:
|
||||
if value is None or not value.strip():
|
||||
continue
|
||||
try:
|
||||
ip = ipaddress.ip_address(value.strip())
|
||||
except ValueError as exc:
|
||||
raise BadIpError(f"{name} must be a valid IPv{version} address") from exc
|
||||
if ip.version != version:
|
||||
raise BadIpError(f"{name} must be an IPv{version} address")
|
||||
ip_value = _required_ip_param(name, value, version)
|
||||
if selected is None:
|
||||
selected = str(ip)
|
||||
selected = ip_value
|
||||
return selected
|
||||
|
||||
|
||||
def _optional_ip(value: str | None) -> ipaddress.IPv4Address | ipaddress.IPv6Address | None:
|
||||
if not value or not value.strip():
|
||||
return None
|
||||
try:
|
||||
return ipaddress.ip_address(value.strip())
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _required_ip_param(name: str, value: str, version: int) -> str:
|
||||
try:
|
||||
ip = ipaddress.ip_address(value.strip())
|
||||
except ValueError as exc:
|
||||
raise BadIpError(f"{name} must be a valid IPv{version} address") from exc
|
||||
if ip.version != version:
|
||||
raise BadIpError(f"{name} must be an IPv{version} address")
|
||||
return str(ip)
|
||||
|
||||
|
||||
def normalize_hostname(value: str, settings: DdnsSettings, expand_suffix: bool = True) -> str:
|
||||
hostname = value.strip().lower().rstrip(".")
|
||||
suffix = settings.hostname_suffix.strip().lower().strip(".")
|
||||
|
||||
Reference in new issue
Block a user