mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-03 02:26:43 +09:00
69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
"""Provider-agnostic DNS primitives shared by both Cloudflare code paths.
|
|
|
|
A single :class:`~app.services.dns.cloudflare.CloudflareClient` is driven from
|
|
*both* layers that talk to Cloudflare:
|
|
|
|
* ``DNSProviderService`` — credentials in the ``DNSProviderConfig`` table
|
|
(email DKIM/SPF/DMARC + WordPress custom-domain auto-DNS), and
|
|
* ``DNSZoneService`` — the ``/dns`` Zones page and Dynamic DNS.
|
|
|
|
Before this, each maintained its *own* Cloudflare request/payload code, so
|
|
wire-format fixes (e.g. CAA's structured ``data`` object) had to be made twice
|
|
and drifted apart. These dataclasses are the normalized inputs both callers
|
|
build, so the API specifics live in exactly one place.
|
|
"""
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class DnsCredential:
|
|
"""Normalized DNS-provider credential, decoupled from where it's stored."""
|
|
|
|
provider: str
|
|
token: Optional[str] = None # bearer token / primary API key
|
|
email: Optional[str] = None # set => Cloudflare global-key auth mode
|
|
secret: Optional[str] = None # second secret (Route53 / GoDaddy)
|
|
|
|
@classmethod
|
|
def from_provider_config(cls, config) -> 'DnsCredential':
|
|
"""Build from a ``DNSProviderConfig`` row, decrypting secrets at point of
|
|
use (never read ``config.api_key`` directly — it's encrypted at rest)."""
|
|
from app.services.dns_provider_service import DNSProviderService
|
|
creds = DNSProviderService.decrypted_credentials(config)
|
|
return cls(
|
|
provider=config.provider,
|
|
token=creds.get('api_key'),
|
|
email=creds.get('api_email'),
|
|
secret=creds.get('api_secret'),
|
|
)
|
|
|
|
@classmethod
|
|
def cloudflare_token(cls, token: str) -> 'DnsCredential':
|
|
"""Build a scoped-token Cloudflare credential (legacy zone-config path)."""
|
|
return cls(provider='cloudflare', token=token)
|
|
|
|
|
|
@dataclass
|
|
class DnsRecordSpec:
|
|
"""A DNS record to create/update, independent of the provider wire format."""
|
|
|
|
record_type: str
|
|
name: str # FQDN (e.g. "www.example.com" or the apex)
|
|
content: str
|
|
ttl: int = 3600
|
|
priority: Optional[int] = None # MX / SRV
|
|
proxied: Optional[bool] = False # Cloudflare orange-cloud; None = legacy unknown
|
|
|
|
@classmethod
|
|
def from_record(cls, record) -> 'DnsRecordSpec':
|
|
"""Build from a ``DNSRecord`` ORM row."""
|
|
return cls(
|
|
record_type=record.record_type,
|
|
name=record.name,
|
|
content=record.content,
|
|
ttl=record.ttl or 3600,
|
|
priority=record.priority,
|
|
proxied=bool(record.proxied),
|
|
)
|