From 596b06f17166449c44e951e491cf1419ffe57883 Mon Sep 17 00:00:00 2001 From: Dmitrii Iurco Date: Fri, 24 Apr 2026 04:08:37 -0400 Subject: [PATCH] fix: apply_domain now removes all stale zone files when renaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the loop broke after processing the first zone file it found. If dev.zone already existed (created by apply_ip_range), it would be processed and the loop would stop — leaving any other zone files (e.g. cell.zone from an earlier domain) in place. get_dns_records() reads all .zone files so the stale zone appeared doubled in the UI. Fix: collect all non-local zone files first, write the target, then delete every file that is not the current domain's zone. Co-Authored-By: Claude Sonnet 4.6 --- api/network_manager.py | 49 ++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/api/network_manager.py b/api/network_manager.py index a28bc69..4bed0dd 100644 --- a/api/network_manager.py +++ b/api/network_manager.py @@ -419,30 +419,37 @@ class NetworkManager(BaseServiceManager): except Exception as e: warnings.append(f"Corefile domain update failed: {e}") - # 3. Update zone file: rename and rewrite $ORIGIN / SOA + # 3. Update zone file: rename and rewrite $ORIGIN / SOA, remove stale zones try: dns_data = os.path.join(self.data_dir, 'dns') if os.path.isdir(dns_data): - # Find existing primary zone file (anything not named 'local') - for fname in os.listdir(dns_data): - if fname.endswith('.zone') and 'local' not in fname: - src = os.path.join(dns_data, fname) - with open(src) as f: - zone_content = f.read() - # Detect old domain from $ORIGIN line - m = re.search(r'^\$ORIGIN\s+(\S+)', zone_content, re.MULTILINE) - old_origin = m.group(1).rstrip('.') if m else None - if old_origin and old_origin != domain: - zone_content = zone_content.replace( - f'{old_origin}.', f'{domain}.') - zone_content = re.sub( - r'^\$ORIGIN\s+\S+', f'$ORIGIN {domain}.', zone_content, flags=re.MULTILINE) - dst = os.path.join(dns_data, f'{domain}.zone') - with open(dst, 'w') as f: - f.write(zone_content) - if src != dst: - os.remove(src) - break + dst = os.path.join(dns_data, f'{domain}.zone') + # Find the best source: prefer a non-target zone (old domain) so we + # can migrate its content; fall back to the target zone itself. + zone_files = [ + os.path.join(dns_data, f) + for f in os.listdir(dns_data) + if f.endswith('.zone') and 'local' not in f + ] + src = next((p for p in zone_files if p != dst), dst) + if os.path.exists(src): + with open(src) as f: + zone_content = f.read() + m = re.search(r'^\$ORIGIN\s+(\S+)', zone_content, re.MULTILINE) + old_origin = m.group(1).rstrip('.') if m else None + if old_origin and old_origin != domain: + zone_content = zone_content.replace(f'{old_origin}.', f'{domain}.') + zone_content = re.sub( + r'^\$ORIGIN\s+\S+', f'$ORIGIN {domain}.', zone_content, flags=re.MULTILINE) + with open(dst, 'w') as f: + f.write(zone_content) + # Remove every zone file that is not the current domain's file + for zone_path in zone_files: + if zone_path != dst: + try: + os.remove(zone_path) + except OSError: + pass except Exception as e: warnings.append(f"zone file domain update failed: {e}")