first commit
This commit is contained in:
commit
cce9a8c9ba
73 changed files with 8665 additions and 0 deletions
81
backend/app/routers/dns_setup.py
Normal file
81
backend/app/routers/dns_setup.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
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).",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue