2026-02-14 09:53:14 +01:00
import apps from './apps.js' ;
import assert from 'node:assert' ;
import BoxError from './boxerror.js' ;
import constants from './constants.js' ;
2026-02-14 15:43:24 +01:00
import dashboard from './dashboard.js' ;
2026-03-12 22:55:28 +05:30
import logger from './logger.js' ;
2026-02-14 15:43:24 +01:00
import domains from './domains.js' ;
import ipaddr from './ipaddr.js' ;
import mail from './mail.js' ;
import mailServer from './mailserver.js' ;
import network from './network.js' ;
2026-02-14 09:53:14 +01:00
import promiseRetry from './promise-retry.js' ;
import safe from 'safetydance' ;
import tasks from './tasks.js' ;
import tld from 'tldjs' ;
2026-02-14 15:43:24 +01:00
import dnsBunny from './dns/bunny.js' ;
import dnsCloudflare from './dns/cloudflare.js' ;
import dnsDesec from './dns/desec.js' ;
import dnsDnsimple from './dns/dnsimple.js' ;
import dnsRoute53 from './dns/route53.js' ;
import dnsGcdns from './dns/gcdns.js' ;
import dnsDigitalocean from './dns/digitalocean.js' ;
import dnsGandi from './dns/gandi.js' ;
import dnsGodaddy from './dns/godaddy.js' ;
import dnsInwx from './dns/inwx.js' ;
import dnsLinode from './dns/linode.js' ;
import dnsVultr from './dns/vultr.js' ;
import dnsNamecom from './dns/namecom.js' ;
import dnsNamecheap from './dns/namecheap.js' ;
import dnsNetcup from './dns/netcup.js' ;
import dnsHetzner from './dns/hetzner.js' ;
import dnsHetznercloud from './dns/hetznercloud.js' ;
import dnsNoop from './dns/noop.js' ;
import dnsManual from './dns/manual.js' ;
import dnsOvh from './dns/ovh.js' ;
import dnsPorkbun from './dns/porkbun.js' ;
import dnsWildcard from './dns/wildcard.js' ;
2026-02-14 09:53:14 +01:00
2026-03-12 23:23:23 +05:30
const { log } = logger ( 'dns' ) ;
2026-02-14 09:53:14 +01:00
2021-08-13 17:22:28 -07:00
2026-02-14 09:53:14 +01:00
const DNS _PROVIDERS = {
bunny : dnsBunny , cloudflare : dnsCloudflare , desec : dnsDesec , dnsimple : dnsDnsimple ,
route53 : dnsRoute53 , gcdns : dnsGcdns , digitalocean : dnsDigitalocean , gandi : dnsGandi ,
godaddy : dnsGodaddy , inwx : dnsInwx , linode : dnsLinode , vultr : dnsVultr ,
namecom : dnsNamecom , namecheap : dnsNamecheap , netcup : dnsNetcup , hetzner : dnsHetzner ,
hetznercloud : dnsHetznercloud , noop : dnsNoop , manual : dnsManual , ovh : dnsOvh ,
porkbun : dnsPorkbun , wildcard : dnsWildcard
} ;
2021-08-13 17:22:28 -07:00
// choose which subdomain backend we use for test purpose we use route53
function api ( provider ) {
assert . strictEqual ( typeof provider , 'string' ) ;
2026-02-14 09:53:14 +01:00
return DNS _PROVIDERS [ provider ] || null ;
2021-08-13 17:22:28 -07:00
}
2022-11-28 21:23:06 +01:00
function fqdn ( subdomain , domain ) {
2022-02-03 16:15:14 -08:00
assert . strictEqual ( typeof subdomain , 'string' ) ;
2022-11-28 21:23:06 +01:00
assert . strictEqual ( typeof domain , 'string' ) ;
2021-08-13 17:22:28 -07:00
2022-11-28 21:23:06 +01:00
return subdomain + ( subdomain ? '.' : '' ) + domain ;
2021-08-13 17:22:28 -07:00
}
// Hostname validation comes from RFC 1123 (section 2.1)
// Domain name validation comes from RFC 2181 (Name syntax)
// https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names
// We are validating the validity of the location-fqdn as host name (and not dns name)
2022-11-28 21:23:06 +01:00
function validateHostname ( subdomain , domain ) {
2022-02-03 16:15:14 -08:00
assert . strictEqual ( typeof subdomain , 'string' ) ;
2022-11-28 21:23:06 +01:00
assert . strictEqual ( typeof domain , 'string' ) ;
2021-08-13 17:22:28 -07:00
2022-11-28 21:23:06 +01:00
const hostname = fqdn ( subdomain , domain ) ;
2021-08-13 17:22:28 -07:00
// workaround https://github.com/oncletom/tld.js/issues/73
2022-04-14 17:41:41 -05:00
const tmp = hostname . replace ( '_' , '-' ) ;
2025-02-05 10:51:05 +01:00
if ( ! tld . isValid ( tmp ) ) return new BoxError ( BoxError . BAD _FIELD , 'Hostname is not a valid domain name' ) ;
2021-08-13 17:22:28 -07:00
2022-02-07 13:19:59 -08:00
if ( hostname . length > 253 ) return new BoxError ( BoxError . BAD _FIELD , 'Hostname length exceeds 253 characters' ) ;
2021-08-13 17:22:28 -07:00
2022-02-03 16:15:14 -08:00
if ( subdomain ) {
2021-08-13 17:22:28 -07:00
// label validation
2022-02-07 13:19:59 -08:00
if ( subdomain . split ( '.' ) . some ( function ( p ) { return p . length > 63 || p . length < 1 ; } ) ) return new BoxError ( BoxError . BAD _FIELD , 'Invalid subdomain length' ) ;
if ( subdomain . match ( /^[A-Za-z0-9-.]+$/ ) === null ) return new BoxError ( BoxError . BAD _FIELD , 'Subdomain can only contain alphanumeric, hyphen and dot' ) ;
if ( /^[-.]/ . test ( subdomain ) ) return new BoxError ( BoxError . BAD _FIELD , 'Subdomain cannot start or end with hyphen or dot' ) ;
2021-08-13 17:22:28 -07:00
}
return null ;
}
// returns the 'name' that needs to be inserted into zone
// eslint-disable-next-line no-unused-vars
2022-02-03 16:15:14 -08:00
function getName ( domain , subdomain , type ) {
2021-08-13 17:22:28 -07:00
const part = domain . domain . slice ( 0 , - domain . zoneName . length - 1 ) ;
2022-02-03 16:15:14 -08:00
if ( subdomain === '' ) return part ;
2021-08-13 17:22:28 -07:00
2022-02-03 16:15:14 -08:00
return part ? ` ${ subdomain } . ${ part } ` : subdomain ;
2021-08-13 17:22:28 -07:00
}
2022-02-03 16:15:14 -08:00
async function getDnsRecords ( subdomain , domain , type ) {
assert . strictEqual ( typeof subdomain , 'string' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof domain , 'string' ) ;
assert . strictEqual ( typeof type , 'string' ) ;
2021-08-27 09:52:24 -07:00
const domainObject = await domains . get ( domain ) ;
2022-02-04 09:37:02 -08:00
return await api ( domainObject . provider ) . get ( domainObject , subdomain , type ) ;
2021-08-13 17:22:28 -07:00
}
2022-02-03 16:15:14 -08:00
async function checkDnsRecords ( subdomain , domain ) {
assert . strictEqual ( typeof subdomain , 'string' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof domain , 'string' ) ;
2022-03-29 13:45:29 -07:00
const cnameRecords = await getDnsRecords ( subdomain , domain , 'CNAME' ) ;
if ( cnameRecords . length !== 0 ) return { needsOverwrite : true } ;
2023-08-03 13:38:42 +05:30
const ipv4 = await network . getIPv4 ( ) ;
2024-04-25 14:48:17 +02:00
if ( ipv4 ) {
const ipv4Records = await getDnsRecords ( subdomain , domain , 'A' ) ;
2022-01-06 17:02:16 -08:00
2024-04-25 14:48:17 +02:00
// if empty OR exactly one record with the ip, we don't need to overwrite
if ( ipv4Records . length !== 0 && ( ipv4Records . length !== 1 || ipv4Records [ 0 ] !== ipv4 ) ) return { needsOverwrite : true } ;
}
2021-08-13 17:22:28 -07:00
2023-08-03 13:38:42 +05:30
const ipv6 = await network . getIPv6 ( ) ;
2022-02-15 12:31:55 -08:00
if ( ipv6 ) {
2022-02-03 16:15:14 -08:00
const ipv6Records = await getDnsRecords ( subdomain , domain , 'AAAA' ) ;
2021-08-13 17:22:28 -07:00
2022-01-06 17:02:16 -08:00
// if empty OR exactly one record with the ip, we don't need to overwrite
2025-05-06 16:16:33 +02:00
if ( ipv6Records . length !== 0 && ( ipv6Records . length !== 1 || ! ipaddr . isEqual ( ipv6Records [ 0 ] , ipv6 ) ) ) return { needsOverwrite : true } ;
2022-01-06 17:02:16 -08:00
}
2021-08-13 17:22:28 -07:00
2022-01-06 17:02:16 -08:00
return { needsOverwrite : false } ; // one record exists and in sync
2021-08-13 17:22:28 -07:00
}
// note: for TXT records the values must be quoted
2022-02-03 16:15:14 -08:00
async function upsertDnsRecords ( subdomain , domain , type , values ) {
assert . strictEqual ( typeof subdomain , 'string' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof domain , 'string' ) ;
assert . strictEqual ( typeof type , 'string' ) ;
assert ( Array . isArray ( values ) ) ;
2026-03-12 22:55:28 +05:30
log ( ` upsertDnsRecords: subdomain: ${ subdomain } domain: ${ domain } type: ${ type } values: ${ JSON . stringify ( values ) } ` ) ;
2021-08-13 17:22:28 -07:00
2021-08-27 09:52:24 -07:00
const domainObject = await domains . get ( domain ) ;
2022-02-04 09:37:02 -08:00
await api ( domainObject . provider ) . upsert ( domainObject , subdomain , type , values ) ;
2021-08-13 17:22:28 -07:00
}
2022-02-03 16:15:14 -08:00
async function removeDnsRecords ( subdomain , domain , type , values ) {
assert . strictEqual ( typeof subdomain , 'string' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof domain , 'string' ) ;
assert . strictEqual ( typeof type , 'string' ) ;
assert ( Array . isArray ( values ) ) ;
2026-03-12 22:55:28 +05:30
log ( ` removeDnsRecords: subdomain: ${ subdomain } domain: ${ domain } type: ${ type } values: ${ JSON . stringify ( values ) } ` ) ;
2021-08-13 17:22:28 -07:00
2021-08-27 09:52:24 -07:00
const domainObject = await domains . get ( domain ) ;
2022-02-04 09:37:02 -08:00
const [ error ] = await safe ( api ( domainObject . provider ) . del ( domainObject , subdomain , type , values ) ) ;
if ( error && error . reason !== BoxError . NOT _FOUND ) throw error ; // this is never returned afaict
2021-08-13 17:22:28 -07:00
}
2022-02-03 16:15:14 -08:00
async function waitForDnsRecord ( subdomain , domain , type , value , options ) {
assert . strictEqual ( typeof subdomain , 'string' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof domain , 'string' ) ;
2022-01-06 22:07:26 -08:00
assert ( type === 'A' || type === 'AAAA' || type === 'TXT' ) ;
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof value , 'string' ) ;
assert ( options && typeof options === 'object' ) ; // { interval: 5000, times: 50000 }
2021-08-27 09:52:24 -07:00
const domainObject = await domains . get ( domain ) ;
2021-08-13 17:22:28 -07:00
2021-08-27 09:52:24 -07:00
// linode DNS takes ~15mins
if ( ! options . interval ) options . interval = domainObject . provider === 'linode' ? 20000 : 5000 ;
2021-08-13 17:22:28 -07:00
2022-02-03 16:15:14 -08:00
await api ( domainObject . provider ) . wait ( domainObject , subdomain , type , value , options ) ;
2021-08-13 17:22:28 -07:00
}
2023-08-14 09:40:31 +05:30
async function waitForLocations ( locations , progressCallback ) {
assert ( Array . isArray ( locations ) ) ;
assert . strictEqual ( typeof progressCallback , 'function' ) ;
if ( constants . TEST ) return ;
const ipv4 = await network . getIPv4 ( ) ;
const ipv6 = await network . getIPv6 ( ) ;
for ( const location of locations ) {
const { subdomain , domain } = location ;
2023-08-21 14:40:57 +05:30
progressCallback ( { message : ` Waiting for propagation of ${ fqdn ( subdomain , domain ) } ` } ) ;
2023-08-14 09:40:31 +05:30
2024-04-25 14:48:17 +02:00
if ( ipv4 ) {
const [ error ] = await safe ( waitForDnsRecord ( subdomain , domain , 'A' , ipv4 , { times : 240 } ) ) ;
2024-10-30 16:21:21 +01:00
if ( error ) throw new BoxError ( BoxError . DNS _ERROR , ` DNS A Record of ${ fqdn ( subdomain , domain ) } is not synced yet: ${ error . message } ` ) ;
2024-04-25 14:48:17 +02:00
}
2023-08-14 09:40:31 +05:30
if ( ipv6 ) {
const [ error ] = await safe ( waitForDnsRecord ( subdomain , domain , 'AAAA' , ipv6 , { times : 240 } ) ) ;
2024-10-30 16:21:21 +01:00
if ( error ) throw new BoxError ( BoxError . DNS _ERROR , ` DNS AAAA Record of ${ fqdn ( subdomain , domain ) } is not synced yet: ${ error . message } ` ) ;
2023-08-14 09:40:31 +05:30
}
}
}
2026-02-18 08:18:37 +01:00
function makeWildcard ( recordFqdn ) {
assert . strictEqual ( typeof recordFqdn , 'string' ) ;
2021-08-13 17:22:28 -07:00
2022-07-13 09:26:27 +05:30
// if the fqdn is like *.example.com, this function will do nothing
2026-02-18 08:18:37 +01:00
const parts = recordFqdn . split ( '.' ) ;
2021-08-13 17:22:28 -07:00
parts [ 0 ] = '*' ;
return parts . join ( '.' ) ;
}
2022-01-06 16:34:33 -08:00
async function registerLocation ( location , options , recordType , recordValue ) {
2024-04-27 18:43:31 +02:00
assert . strictEqual ( typeof location , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof recordType , 'string' ) ;
assert . strictEqual ( typeof recordValue , 'string' ) ;
2022-01-06 16:34:33 -08:00
const overwriteDns = options . overwriteDns || false ;
// get the current record before updating it
const [ getError , values ] = await safe ( getDnsRecords ( location . subdomain , location . domain , recordType ) ) ;
if ( getError ) {
2022-03-29 13:45:29 -07:00
const retryable = getError . reason !== BoxError . ACCESS _DENIED && getError . reason !== BoxError . NOT _FOUND ; // NOT_FOUND is when zone is not found
2026-03-12 22:55:28 +05:30
log ( ` registerLocation: Get error. retryable: ${ retryable } . ${ getError . message } ` ) ;
2025-12-09 14:36:36 +01:00
throw new BoxError ( getError . reason , ` ${ location . domain } : ${ getError . message } ` , { domain : location , retryable } ) ;
2022-01-06 16:34:33 -08:00
}
if ( values . length === 1 && values [ 0 ] === recordValue ) return ; // up-to-date
// refuse to update any existing DNS record for custom domains that we did not create
if ( values . length !== 0 && ! overwriteDns ) throw new BoxError ( BoxError . ALREADY _EXISTS , ` DNS ${ recordType } record already exists ` , { domain : location , retryable : false } ) ;
const [ upsertError ] = await safe ( upsertDnsRecords ( location . subdomain , location . domain , recordType , [ recordValue ] ) ) ;
if ( upsertError ) {
const retryable = upsertError . reason === BoxError . BUSY || upsertError . reason === BoxError . EXTERNAL _ERROR ;
2026-03-12 22:55:28 +05:30
log ( ` registerLocation: Upsert error. retryable: ${ retryable } . ${ upsertError . message } ` ) ;
2025-12-09 14:36:36 +01:00
throw new BoxError ( BoxError . EXTERNAL _ERROR , ` ${ location . domain } : ${ getError . message } ` , { domain : location , retryable } ) ;
2022-01-06 16:34:33 -08:00
}
}
2021-08-27 09:52:24 -07:00
async function registerLocations ( locations , options , progressCallback ) {
2021-08-13 17:22:28 -07:00
assert ( Array . isArray ( locations ) ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof progressCallback , 'function' ) ;
2026-03-12 22:55:28 +05:30
log ( ` registerLocations: Will register ${ JSON . stringify ( locations ) } with options ${ JSON . stringify ( options ) } ` ) ;
2021-08-13 17:22:28 -07:00
2023-08-03 13:38:42 +05:30
const ipv4 = await network . getIPv4 ( ) ;
const ipv6 = await network . getIPv6 ( ) ;
2021-08-27 09:52:24 -07:00
for ( const location of locations ) {
2023-08-22 16:28:48 +05:30
progressCallback ( { message : ` Registering location ${ fqdn ( location . subdomain , location . domain ) } ` } ) ;
2021-08-27 09:52:24 -07:00
2026-03-12 23:11:16 +05:30
await promiseRetry ( { times : 200 , interval : 5000 , log , retry : ( error ) => error . retryable } , async function ( ) {
2022-03-29 13:45:29 -07:00
// cname records cannot co-exist with other records
const [ getError , values ] = await safe ( getDnsRecords ( location . subdomain , location . domain , 'CNAME' ) ) ;
if ( ! getError && values . length === 1 ) {
if ( ! options . overwriteDns ) throw new BoxError ( BoxError . ALREADY _EXISTS , 'DNS CNAME record already exists' , { domain : location , retryable : false } ) ;
2026-03-12 22:55:28 +05:30
log ( ` registerLocations: removing CNAME record of ${ fqdn ( location . subdomain , location . domain ) } ` ) ;
2022-03-29 13:45:29 -07:00
await removeDnsRecords ( location . subdomain , location . domain , 'CNAME' , values ) ;
}
2024-04-27 18:43:31 +02:00
if ( ipv4 ) {
await registerLocation ( location , options , 'A' , ipv4 ) ;
} else {
2026-02-18 08:18:37 +01:00
const [ aError , aValues ] = await safe ( getDnsRecords ( location . subdomain , location . domain , 'A' ) ) ;
2026-03-18 14:26:35 +05:30
if ( ! aError && aValues . length ) await safe ( removeDnsRecords ( location . subdomain , location . domain , 'A' , aValues ) , { debug : log } ) ;
2024-04-27 18:43:31 +02:00
}
if ( ipv6 ) {
await registerLocation ( location , options , 'AAAA' , ipv6 ) ;
} else {
2026-02-18 08:18:37 +01:00
const [ aaaaError , aaaaValues ] = await safe ( getDnsRecords ( location . subdomain , location . domain , 'AAAA' ) ) ;
2026-03-18 14:26:35 +05:30
if ( ! aaaaError && aaaaValues . length ) await safe ( removeDnsRecords ( location . subdomain , location . domain , 'AAAA' , aaaaValues ) , { debug : log } ) ;
2024-04-27 18:43:31 +02:00
}
2022-01-06 16:34:33 -08:00
} ) ;
}
}
2021-08-27 09:52:24 -07:00
2022-01-06 16:34:33 -08:00
async function unregisterLocation ( location , recordType , recordValue ) {
2024-04-27 18:43:31 +02:00
assert . strictEqual ( typeof location , 'object' ) ;
assert . strictEqual ( typeof recordType , 'string' ) ;
assert . strictEqual ( typeof recordValue , 'string' ) ;
2022-01-06 16:34:33 -08:00
const [ error ] = await safe ( removeDnsRecords ( location . subdomain , location . domain , recordType , [ recordValue ] ) ) ;
if ( ! error || error . reason === BoxError . NOT _FOUND ) return ;
2021-08-27 09:52:24 -07:00
2022-01-06 16:34:33 -08:00
const retryable = error . reason === BoxError . BUSY || error . reason === BoxError . EXTERNAL _ERROR ;
2026-03-12 22:55:28 +05:30
log ( ` unregisterLocation: Error unregistering location ${ recordType } . retryable: ${ retryable } . ${ error . message } ` ) ;
2021-08-27 09:52:24 -07:00
2025-12-09 14:36:36 +01:00
throw new BoxError ( BoxError . EXTERNAL _ERROR , ` ${ location . domain } : ${ error . message } ` , { domain : location , retryable } ) ;
2021-08-13 17:22:28 -07:00
}
2021-08-27 09:52:24 -07:00
async function unregisterLocations ( locations , progressCallback ) {
2021-08-13 17:22:28 -07:00
assert ( Array . isArray ( locations ) ) ;
assert . strictEqual ( typeof progressCallback , 'function' ) ;
2023-08-03 13:38:42 +05:30
const ipv4 = await network . getIPv4 ( ) ;
const ipv6 = await network . getIPv6 ( ) ;
2021-08-27 09:52:24 -07:00
for ( const location of locations ) {
2022-01-06 16:34:33 -08:00
progressCallback ( { message : ` Unregistering location: ${ location . subdomain ? ( location . subdomain + '.' ) : '' } ${ location . domain } ` } ) ;
2021-08-27 09:52:24 -07:00
2026-03-12 23:11:16 +05:30
await promiseRetry ( { times : 30 , interval : 5000 , log , retry : ( error ) => error . retryable } , async function ( ) {
2024-04-25 14:48:17 +02:00
if ( ipv4 ) await unregisterLocation ( location , 'A' , ipv4 ) ;
2022-02-15 12:31:55 -08:00
if ( ipv6 ) await unregisterLocation ( location , 'AAAA' , ipv6 ) ;
2021-08-27 09:52:24 -07:00
} ) ;
}
2021-08-13 17:22:28 -07:00
}
2021-08-27 09:52:24 -07:00
async function syncDnsRecords ( options , progressCallback ) {
2021-08-13 17:22:28 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof progressCallback , 'function' ) ;
2024-12-16 09:55:54 +01:00
const errors = [ ] ;
if ( options . domain && options . type === 'mail' ) {
const [ error ] = await safe ( mail . setDnsRecords ( options . domain ) ) ;
if ( error ) errors . push ( { domain : options . domain , error : error . message } ) ;
return errors ;
}
2021-08-13 17:22:28 -07:00
2021-09-05 12:10:37 +02:00
let allDomains = await domains . list ( ) ;
2021-08-13 17:22:28 -07:00
2021-09-05 12:10:37 +02:00
if ( options . domain ) allDomains = allDomains . filter ( d => d . domain === options . domain ) ;
2021-08-13 17:22:28 -07:00
2023-08-04 21:37:38 +05:30
const { domain : mailDomain , fqdn : mailFqdn , subdomain : mailSubdomain } = await mailServer . getLocation ( ) ;
2024-04-27 11:10:24 +02:00
const dashboardLocation = await dashboard . getLocation ( ) ;
2021-08-13 17:22:28 -07:00
2024-12-16 09:55:54 +01:00
const allApps = await apps . list ( ) ;
2024-04-27 18:43:31 +02:00
let progress = 1 ;
2021-08-13 17:22:28 -07:00
2021-08-27 09:52:24 -07:00
// we sync by domain only to get some nice progress
2021-09-05 12:10:37 +02:00
for ( const domain of allDomains ) {
2021-08-27 09:52:24 -07:00
progressCallback ( { percent : progress , message : ` Updating DNS of ${ domain . domain } ` } ) ;
2026-03-04 16:37:30 +05:30
progress = Math . min ( progress + Math . round ( 90 / ( 1 + allDomains . length ) ) , 95 ) ;
2021-08-13 17:22:28 -07:00
2021-08-27 09:52:24 -07:00
let locations = [ ] ;
2024-04-27 11:10:24 +02:00
if ( domain . domain === dashboardLocation . domain ) locations . push ( { subdomain : dashboardLocation . subdomain , domain : dashboardLocation . domain } ) ;
if ( domain . domain === mailDomain && mailFqdn !== dashboardLocation . fqdn ) locations . push ( { subdomain : mailSubdomain , domain : mailDomain } ) ;
2021-08-13 17:22:28 -07:00
2022-01-06 17:19:08 -08:00
for ( const app of allApps ) {
2023-02-08 23:16:48 +01:00
const appLocations = [ { subdomain : app . subdomain , domain : app . domain } ]
. concat ( app . secondaryDomains )
. concat ( app . redirectDomains )
. concat ( app . aliasDomains ) ;
2021-08-27 09:52:24 -07:00
locations = locations . concat ( appLocations . filter ( al => al . domain === domain . domain ) ) ;
2022-01-06 17:19:08 -08:00
}
2021-08-27 09:52:24 -07:00
try {
await registerLocations ( locations , { overwriteDns : true } , progressCallback ) ;
progressCallback ( { message : ` Updating mail DNS of ${ domain . domain } ` } ) ;
await mail . setDnsRecords ( domain . domain ) ;
} catch ( error ) {
errors . push ( { domain : domain . domain , message : error . message } ) ;
}
}
2026-03-04 16:37:30 +05:30
progressCallback ( { percent : 100 , message : 'Done' } ) ;
2021-08-27 09:52:24 -07:00
return { errors } ;
2021-08-13 17:22:28 -07:00
}
2023-08-04 12:53:11 +05:30
async function startSyncDnsRecords ( options ) {
assert . strictEqual ( typeof options , 'object' ) ;
const taskId = await tasks . add ( tasks . TASK _SYNC _DNS _RECORDS , [ options ] ) ;
2026-03-18 14:26:35 +05:30
safe ( tasks . startTask ( taskId , { } ) , { debug : log } ) ; // background
2023-08-04 12:53:11 +05:30
return taskId ;
}
2026-02-14 15:43:24 +01:00
export default {
fqdn ,
getName ,
getDnsRecords ,
upsertDnsRecords ,
removeDnsRecords ,
waitForDnsRecord ,
waitForLocations ,
validateHostname ,
makeWildcard ,
registerLocations ,
unregisterLocations ,
checkDnsRecords ,
syncDnsRecords ,
startSyncDnsRecords
} ;