Make Ed25519 DKIM optional for Email DNS updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-18 14:26:05 +03:30
co-authored by Cursor
parent 5e7bdc0cf2
commit 50b716f040
3 changed files with 12 additions and 7 deletions
+1 -1
View File
@@ -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)
+7 -4
View File
@@ -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(', ')}` : '',
+4 -2
View File
@@ -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)