From 50b716f04055ee83bf70a40a66a2ca8847fbd1c7 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Fri, 18 Sep 2026 14:26:05 +0330 Subject: [PATCH] Make Ed25519 DKIM optional for Email DNS updates. Co-authored-by: Cursor --- docs/PROJECT_CONTEXT.md | 2 +- src/domain-admin/domain-admin.service.ts | 11 +++++++---- src/domain-admin/dto/apply-mail-dns.dto.ts | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index ac9c65a..433cc83 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -659,7 +659,7 @@ See `.env.example` for the full list. Key groups: - Multi-tenant auth (register, login, passwordless OTP login, reset password via SMS, profile) - Super admin: users, businesses, domains, system business categories - Super admin add/update domain upserts tenant DNS via ArvanCloud or Cloudflare (`@` ANAME/CNAME, `www`/`business`/`customer`/`api` CNAMEs; Cloudflare DNS-only) -- Super admin Websites ⋯ **Email DNS** upserts Stalwart mail records (mail A `185.214.101.41`, MX `mail.meshkee.com`, SPF `v=spf1 mx -all`, two DKIM TXT, autoconfig/autodiscover) on Arvan or Cloudflare; removes extra MX, `dkim._domainkey`, and `_dmarc` +- Super admin Websites ⋯ **Email DNS** upserts Stalwart mail records (mail A `185.214.101.41`, MX `mail.meshkee.com`, SPF `v=spf1 mx -all`, RSA DKIM required, Ed25519 DKIM optional, autoconfig/autodiscover) on Arvan or Cloudflare; removes extra MX, `dkim._domainkey`, and `_dmarc` - Super admin: selective migrate-from-old + purge-data (portfolio categories + portfolios; oversized images resized to max 1280×1280; purge removes portfolios + images) - Business team management - Media upload (S3 + Sharp) diff --git a/src/domain-admin/domain-admin.service.ts b/src/domain-admin/domain-admin.service.ts index ad20e83..b932e8a 100644 --- a/src/domain-admin/domain-admin.service.ts +++ b/src/domain-admin/domain-admin.service.ts @@ -939,22 +939,25 @@ export class DomainAdminService { let ed25519; let rsa; try { - ed25519 = parseDkimPaste(dto.ed25519Key, 'ed25519', dto.ed25519Selector); + if (dto.ed25519Key?.trim()) { + ed25519 = parseDkimPaste(dto.ed25519Key, 'ed25519', dto.ed25519Selector); + } rsa = parseDkimPaste(dto.rsaKey, 'rsa', dto.rsaSelector); } catch (err) { throw new BadRequestException(err instanceof Error ? err.message : 'Invalid DKIM key'); } - if (ed25519.selector === rsa.selector) { + if (ed25519 && ed25519.selector === rsa.selector) { throw new BadRequestException('Ed25519 and RSA DKIM selectors must be different.'); } + const keys = ed25519 ? [ed25519, rsa] : [rsa]; const provider = dto.dnsProvider === 'cloudflare' ? 'cloudflare' : 'arvan'; const host = domain.host.trim().toLowerCase(); try { const result = provider === 'cloudflare' - ? await this.cloudflareDns.ensureMailRecords(host, [ed25519, rsa]) - : await this.arvanDns.ensureMailRecords(host, [ed25519, rsa]); + ? await this.cloudflareDns.ensureMailRecords(host, keys) + : await this.arvanDns.ensureMailRecords(host, keys); const parts = [ result.created.length ? `created ${result.created.join(', ')}` : '', diff --git a/src/domain-admin/dto/apply-mail-dns.dto.ts b/src/domain-admin/dto/apply-mail-dns.dto.ts index 5e04983..b262775 100644 --- a/src/domain-admin/dto/apply-mail-dns.dto.ts +++ b/src/domain-admin/dto/apply-mail-dns.dto.ts @@ -1,14 +1,16 @@ -import { IsIn, IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; +import { IsIn, IsOptional, IsString, MaxLength, MinLength, ValidateIf } from 'class-validator'; import { PARKED_DNS_PROVIDERS } from '../parked-host.util'; export class ApplyMailDnsDto { @IsIn(PARKED_DNS_PROVIDERS) dnsProvider!: (typeof PARKED_DNS_PROVIDERS)[number]; + /** Optional — omit or leave empty when only publishing RSA from Stalwart. */ + @ValidateIf((_, value) => value != null && String(value).trim() !== '') @IsString() @MinLength(20) @MaxLength(8000) - ed25519Key!: string; + ed25519Key?: string; @IsString() @MinLength(20)