@serve.zone/dcrouter 13.12.0 → 13.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -176,6 +176,15 @@ let OpsViewDomains = (() => {
176
176
  });
177
177
  },
178
178
  },
179
+ {
180
+ name: 'Migrate',
181
+ iconName: 'lucide:arrow-right-left',
182
+ type: ['inRow', 'contextmenu'],
183
+ actionFunc: async (actionData) => {
184
+ const domain = actionData.item;
185
+ await this.showMigrateDialog(domain);
186
+ },
187
+ },
179
188
  {
180
189
  name: 'Delete',
181
190
  iconName: 'lucide:trash2',
@@ -327,6 +336,93 @@ let OpsViewDomains = (() => {
327
336
  ],
328
337
  });
329
338
  }
339
+ async showMigrateDialog(domain) {
340
+ const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
341
+ const providers = this.domainsState.providers;
342
+ // Build target options based on current source
343
+ const targetOptions = [];
344
+ if (domain.source === 'provider') {
345
+ targetOptions.push({ option: 'DcRouter (authoritative)', key: 'dcrouter' });
346
+ }
347
+ // Add all providers (except the current one if already provider-managed)
348
+ for (const p of providers) {
349
+ if (domain.source === 'provider' && domain.providerId === p.id)
350
+ continue;
351
+ targetOptions.push({ option: `${p.name} (${p.type})`, key: `provider:${p.id}` });
352
+ }
353
+ if (domain.source === 'dcrouter') {
354
+ targetOptions.unshift({ option: 'DcRouter (authoritative)', key: 'dcrouter' });
355
+ }
356
+ if (targetOptions.length === 0) {
357
+ DeesToast.show({
358
+ message: 'No migration targets available. Add a DNS provider first.',
359
+ type: 'warning',
360
+ duration: 3000,
361
+ });
362
+ return;
363
+ }
364
+ const currentLabel = domain.source === 'dcrouter'
365
+ ? 'DcRouter (authoritative)'
366
+ : providers.find((p) => p.id === domain.providerId)?.name || 'Provider';
367
+ DeesModal.createAndShow({
368
+ heading: `Migrate: ${domain.name}`,
369
+ content: html `
370
+ <dees-form>
371
+ <dees-input-text
372
+ .key=${'currentSource'}
373
+ .label=${'Current source'}
374
+ .value=${currentLabel}
375
+ .disabled=${true}
376
+ ></dees-input-text>
377
+ <dees-input-dropdown
378
+ .key=${'target'}
379
+ .label=${'Migrate to'}
380
+ .description=${'Select the target DNS management'}
381
+ .options=${targetOptions}
382
+ .required=${true}
383
+ ></dees-input-dropdown>
384
+ <dees-input-checkbox
385
+ .key=${'deleteExisting'}
386
+ .label=${'Delete existing records at provider first'}
387
+ .description=${'Removes all records at the provider before pushing migrated records'}
388
+ .value=${true}
389
+ ></dees-input-checkbox>
390
+ </dees-form>
391
+ `,
392
+ menuOptions: [
393
+ { name: 'Cancel', action: async (m) => m.destroy() },
394
+ {
395
+ name: 'Migrate',
396
+ action: async (m) => {
397
+ const form = m.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
398
+ if (!form)
399
+ return;
400
+ const data = await form.collectFormData();
401
+ const targetKey = typeof data.target === 'object' ? data.target.key : data.target;
402
+ if (!targetKey)
403
+ return;
404
+ let targetSource;
405
+ let targetProviderId;
406
+ if (targetKey === 'dcrouter') {
407
+ targetSource = 'dcrouter';
408
+ }
409
+ else {
410
+ targetSource = 'provider';
411
+ targetProviderId = targetKey.replace('provider:', '');
412
+ }
413
+ await appstate.domainsStatePart.dispatchAction(appstate.migrateDomainAction, {
414
+ id: domain.id,
415
+ targetSource,
416
+ targetProviderId,
417
+ deleteExistingProviderRecords: targetSource === 'provider' ? Boolean(data.deleteExisting) : false,
418
+ });
419
+ DeesToast.show({ message: `Domain ${domain.name} migrated successfully`, type: 'success', duration: 3000 });
420
+ m.destroy();
421
+ },
422
+ },
423
+ ],
424
+ });
425
+ }
330
426
  async deleteDomain(domain) {
331
427
  const { DeesModal } = await import('@design.estate/dees-catalog');
332
428
  DeesModal.createAndShow({
@@ -359,4 +455,4 @@ let OpsViewDomains = (() => {
359
455
  return OpsViewDomains = _classThis;
360
456
  })();
361
457
  export { OpsViewDomains };
362
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctZG9tYWlucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy9kb21haW5zL29wcy12aWV3LWRvbWFpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sRUFDTCxXQUFXLEVBQ1gsSUFBSSxFQUNKLGFBQWEsRUFFYixHQUFHLEVBQ0gsS0FBSyxFQUNMLFVBQVUsR0FDWCxNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0lBUy9CLGNBQWM7NEJBRDFCLGFBQWEsQ0FBQyxrQkFBa0IsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzhCQUFuQixTQUFRLFdBQVc7Ozs7d0NBQzVDLEtBQUssRUFBRTtZQUNSLHlMQUFTLFlBQVksNkJBQVosWUFBWSxtR0FBaUU7WUFGeEYsNktBMFRDOzs7O1FBeFRDLHFGQUFnRCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFHLEVBQUM7UUFBdEYsSUFBUyxZQUFZLGtEQUFpRTtRQUF0RixJQUFTLFlBQVksd0RBQWlFO1FBRXRGO1lBQ0UsS0FBSyxFQUFFLENBQUM7O1lBQ1IsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUNwRSxJQUFJLENBQUMsWUFBWSxHQUFHLFFBQVEsQ0FBQztZQUMvQixDQUFDLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFD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
458
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctZG9tYWlucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy9kb21haW5zL29wcy12aWV3LWRvbWFpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sRUFDTCxXQUFXLEVBQ1gsSUFBSSxFQUNKLGFBQWEsRUFFYixHQUFHLEVBQ0gsS0FBSyxFQUNMLFVBQVUsR0FDWCxNQUFNLDZCQUE2QixDQUFDO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0lBUy9CLGNBQWM7NEJBRDFCLGFBQWEsQ0FBQyxrQkFBa0IsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzhCQUFuQixTQUFRLFdBQVc7Ozs7d0NBQzVDLEtBQUssRUFBRTtZQUNSLHlMQUFTLFlBQVksNkJBQVosWUFBWSxtR0FBaUU7WUFGeEYsNktBNlpDOzs7O1FBM1pDLHFGQUFnRCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFHLEVBQUM7UUFBdEYsSUFBUyxZQUFZLGtEQUFpRTtRQUF0RixJQUFTLFlBQVksd0RBQWlFO1FBRXRGO1lBQ0UsS0FBSyxFQUFFLENBQUM7O1lBQ1IsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO2dCQUNwRSxJQUFJLENBQUMsWUFBWSxHQUFHLFFBQVEsQ0FBQztZQUMvQixDQUFDLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFD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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "13.12.0",
4
+ "version": "13.13.0",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "exports": {
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '13.12.0',
6
+ version: '13.13.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -930,15 +930,16 @@ export class DcRouter {
930
930
  }
931
931
 
932
932
  // Configure DNS-01 challenge if any DnsProviderDoc exists in the DB AND
933
- // ACME is enabled. The DnsManager dispatches each challenge to the right
934
- // provider client based on the FQDN being certificated.
933
+ // ACME is enabled. The DnsManager dispatches each challenge through the
934
+ // unified createRecord()/deleteRecord() path works for both dcrouter-hosted
935
+ // zones and provider-managed zones. Only domains under management get certs.
935
936
  let challengeHandlers: any[] = [];
936
937
  if (
937
938
  acmeConfig &&
938
939
  this.dnsManager &&
939
- (await this.dnsManager.hasAcmeCapableProvider())
940
+ (await this.dnsManager.hasAnyManagedDomain())
940
941
  ) {
941
- logger.log('info', 'Configuring DNS-01 challenge for ACME via DnsManager (DB providers)');
942
+ logger.log('info', 'Configuring DNS-01 challenge for ACME via DnsManager (managed domains)');
942
943
  const convenientDnsProvider = this.dnsManager.buildAcmeConvenientDnsProvider();
943
944
  const dns01Handler = new plugins.smartacme.handlers.Dns01Handler(convenientDnsProvider);
944
945
  challengeHandlers.push(dns01Handler);
@@ -296,70 +296,99 @@ export class DnsManager {
296
296
  }
297
297
 
298
298
  /**
299
- * True if any cloudflare provider exists in the DB. Used by setupSmartProxy()
300
- * to decide whether to wire SmartAcme with a DNS-01 handler.
299
+ * Find the DomainDoc that covers a given FQDN, regardless of source
300
+ * (dcrouter-hosted or provider-managed). Uses longest-suffix match.
301
301
  */
302
- public async hasAcmeCapableProvider(): Promise<boolean> {
303
- const providers = await DnsProviderDoc.findAll();
304
- return providers.length > 0;
302
+ public async findDomainForFqdn(fqdn: string): Promise<DomainDoc | null> {
303
+ const lower = fqdn.toLowerCase().replace(/^\*\./, '').replace(/\.$/, '');
304
+ const allDomains = await DomainDoc.findAll();
305
+ // Sort by name length descending for longest-match-wins
306
+ allDomains.sort((a, b) => b.name.length - a.name.length);
307
+ for (const domain of allDomains) {
308
+ if (lower === domain.name || lower.endsWith(`.${domain.name}`)) {
309
+ return domain;
310
+ }
311
+ }
312
+ return null;
313
+ }
314
+
315
+ /**
316
+ * Delete all DNS records matching a name and type under a domain.
317
+ * Used for ACME challenge cleanup (may have multiple TXT records at the same name).
318
+ */
319
+ public async deleteRecordsByNameAndType(
320
+ domainId: string,
321
+ name: string,
322
+ type: TDnsRecordType,
323
+ ): Promise<void> {
324
+ const records = await DnsRecordDoc.findByDomainId(domainId);
325
+ for (const rec of records) {
326
+ if (rec.name.toLowerCase() === name.toLowerCase() && rec.type === type) {
327
+ await this.deleteRecord(rec.id);
328
+ }
329
+ }
330
+ }
331
+
332
+ /**
333
+ * True if any domain is under management (dcrouter-hosted or provider-managed).
334
+ * Used by setupSmartProxy() to decide whether to wire SmartAcme with a DNS-01 handler.
335
+ */
336
+ public async hasAnyManagedDomain(): Promise<boolean> {
337
+ const domains = await DomainDoc.findAll();
338
+ return domains.length > 0;
305
339
  }
306
340
 
307
341
  /**
308
- * Build an IConvenientDnsProvider that dispatches each ACME challenge to
309
- * the right provider client (whichever provider type owns the parent zone),
310
- * based on the challenge's hostName. Provider-agnostic — uses the IDnsProviderClient
311
- * interface, so any registered provider implementation works.
312
- * Returned object plugs directly into smartacme's Dns01Handler.
342
+ * Build an IConvenientDnsProvider that routes ACME DNS-01 challenges through
343
+ * the DnsManager abstraction. Challenges are dispatched via createRecord() /
344
+ * deleteRecord(), which transparently handle both dcrouter-hosted zones
345
+ * (embedded DnsServer) and provider-managed zones (e.g. Cloudflare API).
346
+ *
347
+ * Only domains under management (with a DomainDoc in DB) are supported —
348
+ * this acts as the management gate for certificate issuance.
313
349
  */
314
350
  public buildAcmeConvenientDnsProvider(): plugins.tsclass.network.IConvenientDnsProvider {
315
351
  const self = this;
316
352
  const adapter = {
317
353
  async acmeSetDnsChallenge(dnsChallenge: { hostName: string; challenge: string }) {
318
- const client = await self.getProviderClientForDomain(dnsChallenge.hostName);
319
- if (!client) {
354
+ const domainDoc = await self.findDomainForFqdn(dnsChallenge.hostName);
355
+ if (!domainDoc) {
320
356
  throw new Error(
321
- `DnsManager: no DNS provider configured for ${dnsChallenge.hostName}. ` +
322
- 'Add one in the Domains > Providers UI before issuing certificates.',
357
+ `DnsManager: no managed domain found for ${dnsChallenge.hostName}. ` +
358
+ 'Add the domain in Domains before issuing certificates.',
323
359
  );
324
360
  }
325
- // Clean any leftover challenge records first to avoid duplicates.
361
+ // Clean leftover challenge records first to avoid duplicates.
326
362
  try {
327
- const existing = await client.listRecords(dnsChallenge.hostName);
328
- for (const r of existing) {
329
- if (r.type === 'TXT' && r.name === dnsChallenge.hostName) {
330
- await client.deleteRecord(dnsChallenge.hostName, r.providerRecordId).catch(() => {});
331
- }
332
- }
363
+ await self.deleteRecordsByNameAndType(domainDoc.id, dnsChallenge.hostName, 'TXT');
333
364
  } catch (err: unknown) {
334
365
  logger.log('warn', `DnsManager: failed to clean existing TXT for ${dnsChallenge.hostName}: ${(err as Error).message}`);
335
366
  }
336
- await client.createRecord(dnsChallenge.hostName, {
367
+ // Create the challenge TXT record via the unified path
368
+ await self.createRecord({
369
+ domainId: domainDoc.id,
337
370
  name: dnsChallenge.hostName,
338
371
  type: 'TXT',
339
372
  value: dnsChallenge.challenge,
340
373
  ttl: 120,
374
+ createdBy: 'acme-dns01',
341
375
  });
342
376
  },
343
377
  async acmeRemoveDnsChallenge(dnsChallenge: { hostName: string; challenge: string }) {
344
- const client = await self.getProviderClientForDomain(dnsChallenge.hostName);
345
- if (!client) {
378
+ const domainDoc = await self.findDomainForFqdn(dnsChallenge.hostName);
379
+ if (!domainDoc) {
346
380
  // The domain may have been removed; nothing to clean up.
347
381
  return;
348
382
  }
349
383
  try {
350
- const existing = await client.listRecords(dnsChallenge.hostName);
351
- for (const r of existing) {
352
- if (r.type === 'TXT' && r.name === dnsChallenge.hostName) {
353
- await client.deleteRecord(dnsChallenge.hostName, r.providerRecordId);
354
- }
355
- }
384
+ await self.deleteRecordsByNameAndType(domainDoc.id, dnsChallenge.hostName, 'TXT');
356
385
  } catch (err: unknown) {
357
386
  logger.log('warn', `DnsManager: failed to remove TXT for ${dnsChallenge.hostName}: ${(err as Error).message}`);
358
387
  }
359
388
  },
360
389
  async isDomainSupported(domain: string): Promise<boolean> {
361
- const client = await self.getProviderClientForDomain(domain);
362
- return !!client;
390
+ const domainDoc = await self.findDomainForFqdn(domain);
391
+ return !!domainDoc;
363
392
  },
364
393
  };
365
394
  return { convenience: adapter } as plugins.tsclass.network.IConvenientDnsProvider;
@@ -642,6 +671,151 @@ export class DnsManager {
642
671
  return await DnsRecordDoc.findById(id);
643
672
  }
644
673
 
674
+ // ==========================================================================
675
+ // Domain migration
676
+ // ==========================================================================
677
+
678
+ /**
679
+ * Migrate a domain between dcrouter-hosted and provider-managed.
680
+ * Transfers all records to the target and updates domain metadata.
681
+ */
682
+ public async migrateDomain(args: {
683
+ id: string;
684
+ targetSource: 'dcrouter' | 'provider';
685
+ targetProviderId?: string;
686
+ deleteExistingProviderRecords?: boolean;
687
+ }): Promise<{ success: boolean; recordsMigrated?: number; message?: string }> {
688
+ const domain = await DomainDoc.findById(args.id);
689
+ if (!domain) return { success: false, message: 'Domain not found' };
690
+
691
+ if (domain.source === args.targetSource && domain.providerId === args.targetProviderId) {
692
+ return { success: false, message: 'Domain is already in the target configuration' };
693
+ }
694
+
695
+ const records = await DnsRecordDoc.findByDomainId(domain.id);
696
+
697
+ if (args.targetSource === 'provider') {
698
+ return this.migrateToDnsProvider(domain, records, args.targetProviderId!, args.deleteExistingProviderRecords ?? false);
699
+ } else {
700
+ return this.migrateToDcrouter(domain, records);
701
+ }
702
+ }
703
+
704
+ /**
705
+ * Migrate domain from dcrouter-hosted (or another provider) to an external DNS provider.
706
+ */
707
+ private async migrateToDnsProvider(
708
+ domain: DomainDoc,
709
+ records: DnsRecordDoc[],
710
+ targetProviderId: string,
711
+ deleteExistingProviderRecords: boolean,
712
+ ): Promise<{ success: boolean; recordsMigrated?: number; message?: string }> {
713
+ // Validate the target provider exists
714
+ const client = await this.getProviderClientById(targetProviderId);
715
+ if (!client) {
716
+ return { success: false, message: 'Target DNS provider not found' };
717
+ }
718
+
719
+ // Find the zone at the provider
720
+ const providerDomains = await client.listDomains();
721
+ const zone = providerDomains.find(
722
+ (z) => z.name.toLowerCase() === domain.name.toLowerCase(),
723
+ );
724
+ if (!zone) {
725
+ return { success: false, message: `Zone "${domain.name}" not found at the target provider` };
726
+ }
727
+
728
+ // Optionally delete existing records at the provider
729
+ if (deleteExistingProviderRecords) {
730
+ try {
731
+ const existingProviderRecords = await client.listRecords(domain.name);
732
+ for (const pr of existingProviderRecords) {
733
+ await client.deleteRecord(domain.name, pr.providerRecordId).catch(() => {});
734
+ }
735
+ logger.log('info', `Deleted ${existingProviderRecords.length} existing records at provider for ${domain.name}`);
736
+ } catch (err: unknown) {
737
+ logger.log('warn', `Failed to clean existing provider records for ${domain.name}: ${(err as Error).message}`);
738
+ }
739
+ }
740
+
741
+ // Push each local record to the provider
742
+ let migrated = 0;
743
+ for (const rec of records) {
744
+ try {
745
+ const providerRecord = await client.createRecord(domain.name, {
746
+ name: rec.name,
747
+ type: rec.type as any,
748
+ value: rec.value,
749
+ ttl: rec.ttl,
750
+ });
751
+ // Unregister from embedded DnsServer if it was dcrouter-hosted
752
+ if (domain.source === 'dcrouter') {
753
+ this.unregisterRecordFromDnsServer(rec);
754
+ }
755
+ // Update the record doc to synced
756
+ rec.source = 'synced' as TDnsRecordSource;
757
+ rec.providerRecordId = providerRecord.providerRecordId;
758
+ await rec.save();
759
+ migrated++;
760
+ } catch (err: unknown) {
761
+ logger.log('warn', `Failed to migrate record ${rec.name} ${rec.type} to provider: ${(err as Error).message}`);
762
+ }
763
+ }
764
+
765
+ // Update domain metadata
766
+ domain.source = 'provider';
767
+ domain.authoritative = false;
768
+ domain.providerId = targetProviderId;
769
+ domain.externalZoneId = zone.externalId;
770
+ domain.nameservers = zone.nameservers;
771
+ domain.lastSyncedAt = Date.now();
772
+ domain.updatedAt = Date.now();
773
+ await domain.save();
774
+
775
+ logger.log('info', `Domain ${domain.name} migrated to provider (${migrated} records)`);
776
+ return { success: true, recordsMigrated: migrated };
777
+ }
778
+
779
+ /**
780
+ * Migrate domain from provider-managed to dcrouter-hosted (authoritative).
781
+ */
782
+ private async migrateToDcrouter(
783
+ domain: DomainDoc,
784
+ records: DnsRecordDoc[],
785
+ ): Promise<{ success: boolean; recordsMigrated?: number; message?: string }> {
786
+ // Register each record with the embedded DnsServer
787
+ let migrated = 0;
788
+ for (const rec of records) {
789
+ try {
790
+ this.registerRecordWithDnsServer(rec);
791
+ // Update the record doc to local
792
+ rec.source = 'local' as TDnsRecordSource;
793
+ rec.providerRecordId = undefined;
794
+ await rec.save();
795
+ migrated++;
796
+ } catch (err: unknown) {
797
+ logger.log('warn', `Failed to register record ${rec.name} ${rec.type} with DnsServer: ${(err as Error).message}`);
798
+ }
799
+ }
800
+
801
+ // Update domain metadata
802
+ domain.source = 'dcrouter';
803
+ domain.authoritative = true;
804
+ domain.providerId = undefined;
805
+ domain.externalZoneId = undefined;
806
+ domain.nameservers = undefined;
807
+ domain.lastSyncedAt = undefined;
808
+ domain.updatedAt = Date.now();
809
+ await domain.save();
810
+
811
+ logger.log('info', `Domain ${domain.name} migrated to dcrouter (${migrated} records)`);
812
+ return { success: true, recordsMigrated: migrated };
813
+ }
814
+
815
+ // ==========================================================================
816
+ // Record CRUD
817
+ // ==========================================================================
818
+
645
819
  public async createRecord(args: {
646
820
  domainId: string;
647
821
  name: string;
@@ -759,14 +933,24 @@ export class DnsManager {
759
933
  }
760
934
  }
761
935
  }
762
- // For local records: smartdns has no unregister API in the pinned version,
763
- // so the record stays served until the next restart. The DB delete still
764
- // takes effect on restart, the record will not be re-registered.
936
+ // For dcrouter-hosted records: unregister the handler from the embedded DnsServer
937
+ // so the record stops being served immediately (not just after restart).
938
+ if (domain.source === 'dcrouter' && this.dnsServer) {
939
+ this.unregisterRecordFromDnsServer(doc);
940
+ }
765
941
 
766
942
  await doc.delete();
767
943
  return { success: true };
768
944
  }
769
945
 
946
+ /**
947
+ * Unregister a record's handler from the embedded DnsServer.
948
+ */
949
+ public unregisterRecordFromDnsServer(rec: DnsRecordDoc): void {
950
+ if (!this.dnsServer) return;
951
+ this.dnsServer.unregisterHandler(rec.name, [rec.type]);
952
+ }
953
+
770
954
  // ==========================================================================
771
955
  // Internal helpers
772
956
  // ==========================================================================
@@ -127,7 +127,7 @@ export class ConfigHandler {
127
127
  // (replaces the legacy `dnsChallenge.cloudflareApiKey` constructor field).
128
128
  let dnsChallengeEnabled = false;
129
129
  try {
130
- dnsChallengeEnabled = (await dcRouter.dnsManager?.hasAcmeCapableProvider()) ?? false;
130
+ dnsChallengeEnabled = (await dcRouter.dnsManager?.hasAnyManagedDomain()) ?? false;
131
131
  } catch {
132
132
  dnsChallengeEnabled = false;
133
133
  }
@@ -157,5 +157,23 @@ export class DomainHandler {
157
157
  },
158
158
  ),
159
159
  );
160
+
161
+ // Migrate domain between dcrouter-hosted and provider-managed
162
+ this.typedrouter.addTypedHandler(
163
+ new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_MigrateDomain>(
164
+ 'migrateDomain',
165
+ async (dataArg) => {
166
+ await this.requireAuth(dataArg, 'domains:write');
167
+ const dnsManager = this.opsServerRef.dcRouterRef.dnsManager;
168
+ if (!dnsManager) return { success: false, message: 'DnsManager not initialized' };
169
+ return await dnsManager.migrateDomain({
170
+ id: dataArg.id,
171
+ targetSource: dataArg.targetSource,
172
+ targetProviderId: dataArg.targetProviderId,
173
+ deleteExistingProviderRecords: dataArg.deleteExistingProviderRecords,
174
+ });
175
+ },
176
+ ),
177
+ );
160
178
  }
161
179
  }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '13.12.0',
6
+ version: '13.13.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -1887,6 +1887,32 @@ export const syncDomainAction = domainsStatePart.createAction<{ id: string }>(
1887
1887
  },
1888
1888
  );
1889
1889
 
1890
+ export const migrateDomainAction = domainsStatePart.createAction<{
1891
+ id: string;
1892
+ targetSource: interfaces.data.TDomainSource;
1893
+ targetProviderId?: string;
1894
+ deleteExistingProviderRecords?: boolean;
1895
+ }>(
1896
+ async (statePartArg, dataArg, actionContext): Promise<IDomainsState> => {
1897
+ const context = getActionContext();
1898
+ try {
1899
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
1900
+ interfaces.requests.IReq_MigrateDomain
1901
+ >('/typedrequest', 'migrateDomain');
1902
+ const response = await request.fire({ identity: context.identity!, ...dataArg });
1903
+ if (!response.success) {
1904
+ return { ...statePartArg.getState()!, error: response.message || 'Migration failed' };
1905
+ }
1906
+ return await actionContext!.dispatch(fetchDomainsAndProvidersAction, null);
1907
+ } catch (error: unknown) {
1908
+ return {
1909
+ ...statePartArg.getState()!,
1910
+ error: error instanceof Error ? error.message : 'Migration failed',
1911
+ };
1912
+ }
1913
+ },
1914
+ );
1915
+
1890
1916
  export const createDnsRecordAction = domainsStatePart.createAction<{
1891
1917
  domainId: string;
1892
1918
  name: string;