feat: DNS provider abstraction — Technitium, Cloudflare, RFC 2136, Manual
- dns-providers/: adapter base class + registry with auto-discovery - technitium.js: wraps existing Technitium API calls into adapter interface - cloudflare.js: Cloudflare API v4 adapter (zones, records, credentials) - rfc2136.js: RFC 2136 dynamic DNS via nsupdate (BIND, PowerDNS, etc.) - manual.js: no-op adapter for external DNS management with instructions - provider-dns.js: provider-aware DNS context, resolves active adapter from config - Universal helper methods: universalCreateRecord/Delete/ResolveRecord - All 7 route files updated to use universal methods instead of raw dns.call() - Setup wizard: provider dropdown (Technitium, Cloudflare, RFC 2136, Manual) - DNS template selector: added Cloudflare and External/Manual options - Config schema: validates dns.provider field - Capability gating on Technitium-specific endpoints (logs, restart, update) - Backward compatible: no provider set = auto-detect (technitium if dns.ip exists)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Base DNS Provider Adapter
|
||||
* All DNS provider adapters must extend this class and implement the required methods.
|
||||
*
|
||||
* Each adapter handles the specifics of talking to a particular DNS provider's API.
|
||||
* The routes layer calls these methods generically — no provider-specific logic in routes.
|
||||
*/
|
||||
class BaseDNSProvider {
|
||||
constructor(config, ctx) {
|
||||
this.config = config; // Provider-specific config (api token, server url, etc.)
|
||||
this.ctx = ctx; // Shared app context (log, credentialManager, fetchT, etc.)
|
||||
this.providerId = 'base';
|
||||
this.displayName = 'Base DNS Provider';
|
||||
}
|
||||
|
||||
/** Check if this provider supports a given capability */
|
||||
supportsCapability(cap) {
|
||||
// Capabilities: 'create-record', 'delete-record', 'resolve', 'list-records',
|
||||
// 'logs', 'restart', 'update-check', 'credentials', 'zones'
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Authenticate and return a token/session */
|
||||
async authenticate() { throw new Error('Not implemented'); }
|
||||
|
||||
/** Create a DNS record */
|
||||
async createRecord({ domain, zone, type, value, ttl, overwrite }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Delete a DNS record */
|
||||
async deleteRecord({ domain, type, value }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Resolve/query existing records for a domain */
|
||||
async resolveRecords({ domain, zone, type }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** List all records in a zone */
|
||||
async listRecords({ zone }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Get DNS query logs */
|
||||
async getLogs({ limit, server }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Restart the DNS server */
|
||||
async restartServer({ server }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Check for DNS server updates */
|
||||
async checkUpdate({ server }) { throw new Error('Not implemented'); }
|
||||
|
||||
/** Get provider status info */
|
||||
async getStatus() {
|
||||
return {
|
||||
providerId: this.providerId,
|
||||
displayName: this.displayName,
|
||||
capabilities: this.getCapabilities(),
|
||||
authenticated: false
|
||||
};
|
||||
}
|
||||
|
||||
/** Get list of supported capabilities */
|
||||
getCapabilities() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** Validate provider-specific config */
|
||||
validateConfig() { return { valid: true, errors: [] }; }
|
||||
|
||||
/** Clean up resources on shutdown */
|
||||
async shutdown() {}
|
||||
}
|
||||
|
||||
module.exports = BaseDNSProvider;
|
||||
Reference in New Issue
Block a user