import ipaddress from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.config import get_settings from app.database import get_postgres_session from app.deps.dns_setup_auth import require_dns_setup_auth from app.schemas import DnsSetupRequest, DnsSetupResponse from app.services.domain_provisioning import ( build_deployment_dns_records, get_domain_by_name, validate_domain, ) router = APIRouter(prefix="/dns-setup", tags=["dns-setup"]) def validate_mail_server_ip(value: str) -> str: candidate = value.strip() try: return str(ipaddress.ip_address(candidate)) except ValueError as error: raise ValueError("Enter a valid IPv4 or IPv6 address.") from error @router.post("/preview", response_model=DnsSetupResponse) async def preview_dns_setup( payload: DnsSetupRequest, _: None = Depends(require_dns_setup_auth), session: AsyncSession = Depends(get_postgres_session), ) -> DnsSetupResponse: settings = get_settings() try: domain_name = validate_domain(payload.domain) mail_server_ip = validate_mail_server_ip(payload.mail_server_ip) except ValueError as error: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(error), ) from error domain = await get_domain_by_name(session, domain_name) sections = build_deployment_dns_records( domain_name=domain_name, mail_server_ip=mail_server_ip, dkim_selector=settings.dkim_selector, dkim_public_key=domain.dkim_public_key if domain else None, app_public_host=settings.app_public_host, ) mail_host = f"mail.{domain_name}" return DnsSetupResponse( domain=domain_name, mail_host=mail_host, app_public_host=settings.app_public_host, mail_server_ip=mail_server_ip, is_platform_domain=domain_name == settings.platform_domain, sections=sections, notes=_build_notes( domain_name, settings.app_public_host, mail_server_ip, ), ) def _build_notes( domain_name: str, app_public_host: str, mail_server_ip: str, ) -> list[str]: return [ "Add these records in Namecheap Advanced DNS for the zone.", f"Point mail.{domain_name} and {app_public_host} A records to {mail_server_ip}.", f"Keep the root {domain_name} A record on your main website server if it hosts the marketing site.", f"Set {domain_name} MX to mail.{domain_name} (priority 10) so inbound mail reaches Postfix on this host.", "Postfix accepts inbound mail on :25 and delivers outbound mail for the app over the Docker network.", f"Users open the web UI at https://{app_public_host} (Caddy terminates TLS).", f"Ask your VPS provider to set reverse DNS (PTR) for {mail_server_ip} → mail.{domain_name}.", "SPF, DKIM, and DMARC authenticate outbound mail signed by Agmail (selector agmail).", ]