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
+36 -12
View File
@@ -358,8 +358,8 @@ def get_config():
try:
service_configs = config_manager.get_all_configs()
config = {
'cell_name': os.environ.get('CELL_NAME', 'personal-internet-cell'),
'domain': os.environ.get('CELL_DOMAIN', 'cell.local'),
'cell_name': os.environ.get('CELL_NAME', 'mycell'),
'domain': os.environ.get('CELL_DOMAIN', 'cell'),
'ip_range': os.environ.get('CELL_IP_RANGE', '172.20.0.0/16'),
'wireguard_port': int(os.environ.get('WG_PORT', '51820')),
}
@@ -836,19 +836,28 @@ def update_peer_ip():
@app.route('/api/wireguard/peers/status', methods=['POST'])
def get_peer_status():
"""Get WireGuard peer status."""
"""Get live WireGuard status for a single peer."""
try:
data = request.get_json(silent=True)
if data is None or 'public_key' not in data:
return jsonify({"error": "Missing public key"}), 400
public_key = data['public_key']
data = request.get_json(silent=True) or {}
public_key = data.get('public_key', '')
if not public_key:
return jsonify({"error": "Missing public_key"}), 400
status = wireguard_manager.get_peer_status(public_key)
return jsonify(status)
except Exception as e:
logger.error(f"Error getting peer status: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/wireguard/peers/statuses', methods=['GET'])
def get_all_peer_statuses():
"""Get live WireGuard status for all peers (keyed by public_key)."""
try:
statuses = wireguard_manager.get_all_peer_statuses()
return jsonify(statuses)
except Exception as e:
logger.error(f"Error getting peer statuses: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/wireguard/network/setup', methods=['POST'])
def setup_network():
"""Setup network configuration for internet access."""
@@ -917,17 +926,23 @@ def get_server_config():
def refresh_external_ip():
try:
ip = wireguard_manager.get_external_ip(force_refresh=True)
port_open = wireguard_manager.check_port_open()
return jsonify({
'external_ip': ip,
'port': wireguard_manager.DEFAULT_PORT if hasattr(wireguard_manager, 'DEFAULT_PORT') else 51820,
'port_open': port_open,
'endpoint': f'{ip}:{51820}' if ip else None,
'port': 51820,
'endpoint': f'{ip}:51820' if ip else None,
})
except Exception as e:
logger.error(f"Error refreshing external IP: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/wireguard/check-port', methods=['POST'])
def check_wireguard_port():
try:
port_open = wireguard_manager.check_port_open()
return jsonify({'port_open': port_open, 'port': 51820})
except Exception as e:
return jsonify({"error": str(e)}), 500
# Peer Registry API
@app.route('/api/peers', methods=['GET'])
def get_peers():
@@ -1369,6 +1384,15 @@ def get_routing_status():
logger.error(f"Error getting routing status: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/routing/setup', methods=['POST'])
def setup_routing():
"""Apply/verify routing setup (WireGuard handles NAT via PostUp rules)."""
try:
status = routing_manager.get_status()
return jsonify({'success': True, 'message': 'Routing managed by WireGuard PostUp rules', **status})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/routing/nat', methods=['POST'])
def add_nat_rule():
"""Add NAT rule.