/** * DNS Provider Registry * Manages available DNS provider adapters. * Providers register themselves, and the active provider is selected by config. */ const path = require('path'); class DNSProviderRegistry { constructor() { this.providers = new Map(); // providerId -> adapter class this.instances = new Map(); // providerId -> adapter instance } /** Register a provider adapter class */ register(adapterClass) { const instance = new adapterClass({}, {}); const id = instance.providerId; if (this.providers.has(id)) { console.warn(`DNS provider "${id}" already registered, overwriting`); } this.providers.set(id, adapterClass); } /** Get list of all registered provider IDs */ getProviderIds() { return Array.from(this.providers.keys()); } /** Get metadata for all providers (without instantiating with real config) */ getProviderMeta() { return this.getProviderIds().map(id => { const Adapter = this.providers.get(id); const inst = new Adapter({}, {}); return { id: inst.providerId, displayName: inst.displayName, capabilities: inst.getCapabilities() }; }); } /** * Get or create an adapter instance for the given provider + config * @param {string} providerId - The provider to instantiate * @param {Object} config - Provider-specific configuration * @param {Object} ctx - Shared application context * @returns {BaseDNSProvider} The provider adapter instance */ getProvider(providerId, config, ctx) { // Re-create if config changed const cacheKey = providerId; const Adapter = this.providers.get(providerId); if (!Adapter) { throw new Error(`Unknown DNS provider: ${providerId}. Available: ${this.getProviderIds().join(', ')}`); } const instance = new Adapter(config, ctx); this.instances.set(cacheKey, instance); return instance; } /** Auto-discover and register all providers in this directory */ autoDiscover() { const fs = require('fs'); const dir = __dirname; const files = fs.readdirSync(dir).filter(f => f !== 'base.js' && f !== 'registry.js' && f.endsWith('.js') && !f.startsWith('.') ); for (const file of files) { try { const Loaded = require(path.join(dir, file)); // Support: module.exports = Class, module.exports = { Class }, or plain objects let cls = null; if (typeof Loaded === 'function') { cls = Loaded; } else if (typeof Loaded === 'object' && Loaded !== null) { // Try to find a class in the exported object cls = Object.values(Loaded).find(v => typeof v === 'function'); } if (cls) { // Verify it has providerId (on prototype or set in constructor) try { const test = new cls({}, {}); if (test.providerId && typeof test.getCapabilities === 'function') { this.register(cls); } } catch { // Not a valid provider adapter, skip } } } catch (err) { console.error(`Failed to load DNS provider from ${file}:`, err.message); } } } } // Singleton const registry = new DNSProviderRegistry(); registry.autoDiscover(); module.exports = registry;