company flows

This commit is contained in:
debu-2 2026-10-04 15:35:17 +05:30
parent c04a6ce6fe
commit a3a6464a99
15 changed files with 963 additions and 60 deletions

View file

@ -12,6 +12,7 @@ from app.routers.auth import get_current_account
from app.schemas import (
DnsVerificationResponse,
DomainCreate,
DomainNameCreate,
MailDomainRead,
SmtpCredentialCreate,
SmtpCredentialCreated,
@ -20,11 +21,14 @@ from app.schemas import (
from app.services.dns_verification import verify_company_dns
from app.services.domain_policy import assert_company_domain_allowed
from app.services.domain_provisioning import (
account_is_domain_owner,
activate_company_domain,
create_smtp_credential,
ensure_ownership_dns_record,
ensure_platform_domain,
get_account_company_domain,
get_owned_domain,
list_owned_domains,
provision_mail_domain,
validate_domain,
)
@ -32,6 +36,56 @@ from app.services.domain_provisioning import (
router = APIRouter(prefix="/domains", tags=["domains"])
async def require_domain_owner(
account: Account,
session: AsyncSession,
) -> None:
if account.account_type != "company":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Company accounts only.",
)
if not await account_is_domain_owner(session, account.id, account.company_domain):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only the domain owner can manage domains.",
)
async def verify_owned_domain_dns(
session: AsyncSession,
domain: MailDomain,
) -> DnsVerificationResponse:
settings = get_settings()
if domain.status == "active":
return DnsVerificationResponse(
domain=domain.domain_name,
status=domain.status,
verified=True,
checks=[],
)
verification_token = await ensure_ownership_dns_record(session, domain)
checks = verify_company_dns(
domain_name=domain.domain_name,
shared_mail_host=settings.smtp_public_host,
dkim_selector=domain.dkim_selector,
dkim_public_key=domain.dkim_public_key,
platform_spf_include=settings.platform_spf_include,
verification_token=verification_token,
)
verified = all(check["verified"] for check in checks)
if verified:
await activate_company_domain(session, domain)
await session.commit()
return DnsVerificationResponse(
domain=domain.domain_name,
status=domain.status if not verified else "active",
verified=verified,
checks=checks,
)
async def get_domain_or_404(
domain_id: UUID,
session: AsyncSession,
@ -104,25 +158,86 @@ async def verify_my_company_dns(
checks=[],
)
verification_token = await ensure_ownership_dns_record(session, domain)
checks = verify_company_dns(
domain_name=domain.domain_name,
shared_mail_host=settings.smtp_public_host,
dkim_selector=domain.dkim_selector,
dkim_public_key=domain.dkim_public_key,
platform_spf_include=settings.platform_spf_include,
verification_token=verification_token,
return await verify_owned_domain_dns(session, domain)
@router.get("/me/domains", response_model=list[MailDomainRead])
async def list_my_domains(
account: Account = Depends(get_current_account),
session: AsyncSession = Depends(get_postgres_session),
) -> list[MailDomain]:
await require_domain_owner(account, session)
return await list_owned_domains(session, account.id)
@router.post("/me/domains", response_model=MailDomainRead, status_code=status.HTTP_201_CREATED)
async def add_my_domain(
payload: DomainNameCreate,
account: Account = Depends(get_current_account),
session: AsyncSession = Depends(get_postgres_session),
) -> MailDomain:
settings = get_settings()
await require_domain_owner(account, session)
try:
normalized = assert_company_domain_allowed(
payload.domain_name,
settings.platform_domain,
)
except ValueError as error:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=str(error),
) from error
existing = await session.scalar(
select(MailDomain).where(MailDomain.domain_name == normalized)
)
verified = all(check["verified"] for check in checks)
if verified:
await activate_company_domain(session, domain)
await session.commit()
return DnsVerificationResponse(
domain=domain.domain_name,
status=domain.status if not verified else "active",
verified=verified,
checks=checks,
if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="That domain is already registered.",
)
domain = await provision_mail_domain(
session,
domain_name=normalized,
domain_type="company",
account_id=account.id,
)
await session.commit()
return await get_owned_domain(session, domain.id, account.id)
@router.get("/me/domains/{domain_id}", response_model=MailDomainRead)
async def get_my_domain(
domain_id: UUID,
account: Account = Depends(get_current_account),
session: AsyncSession = Depends(get_postgres_session),
) -> MailDomain:
await require_domain_owner(account, session)
domain = await get_owned_domain(session, domain_id, account.id)
if domain is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Domain not found.",
)
return domain
@router.post("/me/domains/{domain_id}/verify-dns", response_model=DnsVerificationResponse)
async def verify_my_domain_dns(
domain_id: UUID,
account: Account = Depends(get_current_account),
session: AsyncSession = Depends(get_postgres_session),
) -> DnsVerificationResponse:
await require_domain_owner(account, session)
domain = await get_owned_domain(session, domain_id, account.id)
if domain is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Domain not found.",
)
return await verify_owned_domain_dns(session, domain)
@router.get("/platform", response_model=MailDomainRead)