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:
@@ -316,7 +316,7 @@ module.exports = function({ docker, caddy, credentialManager, servicesStateManag
|
||||
let dnsWarning = null;
|
||||
if (config.createDns && !isSubdirectoryMode) {
|
||||
try {
|
||||
await ctx.dns.createRecord(config.subdomain, config.ip);
|
||||
await ctx.dns.universalCreateRecord(config.subdomain, config.ip);
|
||||
log.info('deploy', 'DNS record created', { domain: ctx.buildDomain(config.subdomain), ip: config.ip });
|
||||
} catch (dnsError) {
|
||||
await logError('app-deploy-dns', dnsError, { appId, subdomain: config.subdomain, ip: config.ip });
|
||||
|
||||
@@ -71,18 +71,13 @@ module.exports = function({
|
||||
if (shouldDeleteContainer && subdomain && ctx.dns.getToken()) {
|
||||
try {
|
||||
const domain = ctx.buildDomain(subdomain);
|
||||
const getResult = await ctx.dns.call(ctx.siteConfig.dnsServerIp, '/api/zones/records/get', {
|
||||
token: ctx.dns.getToken(), domain, zone: ctx.siteConfig.tld.replace(/^\./, ''), listZone: 'true'
|
||||
});
|
||||
const resolveResult = await ctx.dns.universalResolveRecord(domain, 'A');
|
||||
let recordIp = ip || 'localhost';
|
||||
if (getResult.status === 'ok' && getResult.response?.records) {
|
||||
const aRecord = getResult.response.records.find(r => r.type === 'A');
|
||||
if (aRecord && aRecord.rData?.ipAddress) recordIp = aRecord.rData.ipAddress;
|
||||
if (resolveResult) {
|
||||
recordIp = resolveResult;
|
||||
}
|
||||
const dnsResult = await ctx.dns.call(ctx.siteConfig.dnsServerIp, '/api/zones/records/delete', {
|
||||
token: ctx.dns.getToken(), domain, type: 'A', ipAddress: recordIp
|
||||
});
|
||||
results.dns = dnsResult.status === 'ok' ? 'deleted' : (dnsResult.errorMessage || 'failed');
|
||||
await ctx.dns.universalDeleteRecord(domain, recordIp);
|
||||
results.dns = 'deleted';
|
||||
log.info('dns', 'DNS record removal', { result: results.dns });
|
||||
} catch (error) {
|
||||
results.dns = error.message;
|
||||
|
||||
@@ -458,7 +458,7 @@ module.exports = function({ docker, caddy, servicesStateManager, asyncHandler, e
|
||||
// DNS record
|
||||
if (manifest.config.createDns && manifest.caddy.routingMode !== 'subdirectory') {
|
||||
try {
|
||||
await ctx.dns.createRecord(manifest.config.subdomain, manifest.config.ip);
|
||||
await ctx.dns.universalCreateRecord(manifest.config.subdomain, manifest.config.ip);
|
||||
log.info('restore', 'DNS record recreated', { subdomain: manifest.config.subdomain });
|
||||
} catch (e) {
|
||||
log.warn('restore', `DNS recreation failed: ${e.message}`);
|
||||
|
||||
@@ -107,10 +107,8 @@ module.exports = function({
|
||||
if (oldSubdomain && ctx.dns.getToken()) {
|
||||
try {
|
||||
const oldDomain = oldSubdomain.includes('.') ? oldSubdomain : ctx.buildDomain(oldSubdomain);
|
||||
const result = await ctx.dns.call(ctx.siteConfig.dnsServerIp, '/api/zones/records/delete', {
|
||||
token: ctx.dns.getToken(), domain: oldDomain, type: 'A', ipAddress: ip || 'localhost'
|
||||
});
|
||||
results.oldDns = result.status === 'ok' ? 'deleted' : result.errorMessage;
|
||||
await ctx.dns.universalDeleteRecord(oldDomain, ip || 'localhost');
|
||||
results.oldDns = 'deleted';
|
||||
log.info('dns', 'Old DNS record deleted', { domain: oldDomain });
|
||||
} catch (error) {
|
||||
results.oldDns = `failed: ${error.message}`;
|
||||
@@ -120,7 +118,7 @@ module.exports = function({
|
||||
|
||||
if (newSubdomain && ctx.dns.getToken()) {
|
||||
try {
|
||||
await ctx.dns.createRecord(newSubdomain, ip || 'localhost');
|
||||
await ctx.dns.universalCreateRecord(newSubdomain, ip || 'localhost');
|
||||
results.newDns = 'created';
|
||||
log.info('dns', 'New DNS record created', { domain: ctx.buildDomain(newSubdomain) });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user