fix: WireGuard routing, DNS, service access, and UI improvements

- Fix CoreDNS not loading .cell zones (wrong Corefile path, now uses -conf flag)
- Fix WireGuard server address conflict (172.20.0.1/16 overlapped with Docker
  network; changed to 10.0.0.1/24 to eliminate duplicate routes)
- Add SERVERMODE=true and sysctls to WireGuard docker-compose for server mode
- Fix DNS zone file parser to handle 4-field records (name IN type value)
- Add get_dns_records() to NetworkManager; mount data/dns into API container
- Fix peer config endpoint: look up IP/key from registry, use real endpoint
- Add bulk peer statuses endpoint keyed by public_key
- Normalize snake_case API fields to camelCase in WireGuard UI
- Add port check endpoint (checks via live handshake, not unreliable TCP probe)
- Add Caddy virtual hosts for ui/calendar/files/mail .cell domains (HTTP only)
- Fix cell config domain default from cell.local to cell
- Fix Routing Network Config tab (was calling hardcoded localhost:3000)
- Fix DNS records display (record.value not record.ip)
- Move service access guide to top of Dashboard with login hints
- Add /api/routing/setup endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 12:43:23 -04:00
parent bd67764bf4
commit e79ee08c63
14 changed files with 422 additions and 306 deletions
+25 -6
View File
@@ -118,6 +118,20 @@ class NetworkManager(BaseServiceManager):
logger.error(f"Failed to remove DNS record: {e}")
return False
def get_dns_records(self, zone: str = 'cell') -> List[Dict]:
"""Get all DNS records across all zones"""
all_records = []
try:
for fname in os.listdir(self.dns_zones_dir):
if fname.endswith('.zone'):
z = fname[:-5]
for rec in self._load_dns_records(z):
rec['zone'] = z
all_records.append(rec)
except Exception as e:
logger.error(f"Failed to list DNS records: {e}")
return all_records
def _load_dns_records(self, zone: str) -> List[Dict]:
"""Load DNS records from zone file"""
zone_file = os.path.join(self.dns_zones_dir, f'{zone}.zone')
@@ -131,12 +145,17 @@ class NetworkManager(BaseServiceManager):
lines = f.readlines()
for line in lines:
line = line.strip()
if line and not line.startswith(';') and not line.startswith('$'):
parts = line.split()
if len(parts) >= 5:
record_type = parts[3]
if record_type in ('A', 'CNAME'):
line = line.strip().split(';')[0].strip() # strip inline comments
if not line or line.startswith('$'):
continue
parts = line.split()
# Support both: name IN type value (4 parts)
# and name TTL IN type value (5 parts)
if len(parts) == 4 and parts[1] in ('IN',) and parts[2] in ('A', 'CNAME', 'MX', 'TXT'):
records.append({'name': parts[0], 'ttl': '300', 'type': parts[2], 'value': parts[3]})
elif len(parts) >= 5:
record_type = parts[3]
if record_type in ('A', 'CNAME'):
records.append({
'name': parts[0],
'ttl': parts[1],