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:
+33
-109
@@ -95,7 +95,7 @@ function Routing() {
|
||||
setNetworkLoading(true);
|
||||
setNetworkError(null);
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/wireguard/network/status');
|
||||
const response = await fetch('/api/routing/status');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setNetworkStatus(data);
|
||||
@@ -114,11 +114,9 @@ function Routing() {
|
||||
setIsSettingUp(true);
|
||||
setNetworkError(null);
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/wireguard/network/setup', {
|
||||
const response = await fetch('/api/routing/setup', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
@@ -404,125 +402,51 @@ function Routing() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{networkError && (
|
||||
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg">
|
||||
<p className="text-red-800">{networkError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkLoading ? (
|
||||
<div className="flex justify-center items-center py-8">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||
</div>
|
||||
) : networkStatus ? (
|
||||
<div className="space-y-6">
|
||||
{/* Network Status Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="bg-white p-4 rounded-lg border border-gray-200">
|
||||
<div className="flex items-center">
|
||||
<div className={`w-3 h-3 rounded-full mr-3 ${networkStatus.ip_forwarding ? 'bg-green-400' : 'bg-red-400'}`}></div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">IP Forwarding</p>
|
||||
<p className="text-xs text-gray-500">{networkStatus.ip_forwarding ? 'Enabled' : 'Disabled'}</p>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{/* Status cards */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[
|
||||
{ label: 'Routing', value: networkStatus.status === 'online' ? 'Online' : 'Offline', ok: networkStatus.running },
|
||||
{ label: 'NAT Rules', value: networkStatus.nat_rules_count ?? 0, ok: true },
|
||||
{ label: 'Firewall Rules', value: networkStatus.firewall_rules_count ?? 0, ok: true },
|
||||
{ label: 'Peer Routes', value: networkStatus.peer_routes_count ?? 0, ok: true },
|
||||
].map(item => (
|
||||
<div key={item.label} className="bg-white p-4 rounded-lg border border-gray-200">
|
||||
<p className="text-xs text-gray-500">{item.label}</p>
|
||||
<p className={`text-lg font-semibold mt-1 ${item.ok ? 'text-gray-900' : 'text-red-600'}`}>{item.value}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-4 rounded-lg border border-gray-200">
|
||||
<div className="flex items-center">
|
||||
<div className={`w-3 h-3 rounded-full mr-3 ${networkStatus.interface_status ? 'bg-green-400' : 'bg-red-400'}`}></div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">WireGuard Interface</p>
|
||||
<p className="text-xs text-gray-500">{networkStatus.interface_status ? 'Up' : 'Down'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-4 rounded-lg border border-gray-200">
|
||||
<div className="flex items-center">
|
||||
<div className={`w-3 h-3 rounded-full mr-3 ${networkStatus.nat_rules ? 'bg-green-400' : 'bg-red-400'}`}></div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">NAT Rules</p>
|
||||
<p className="text-xs text-gray-500">{networkStatus.nat_rules ? 'Configured' : 'Missing'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-4 rounded-lg border border-gray-200">
|
||||
<div className="flex items-center">
|
||||
<div className={`w-3 h-3 rounded-full mr-3 ${networkStatus.forwarding_rules ? 'bg-green-400' : 'bg-red-400'}`}></div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">Forwarding Rules</p>
|
||||
<p className="text-xs text-gray-500">{networkStatus.forwarding_rules ? 'Configured' : 'Missing'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Configuration Details */}
|
||||
<div className="bg-gray-50 p-4 rounded-lg">
|
||||
<h4 className="text-md font-medium text-gray-900 mb-3">Configuration Details</h4>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Last Updated:</span>
|
||||
<span className="text-gray-900">{new Date(networkStatus.timestamp).toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">IP Forwarding:</span>
|
||||
<span className={`font-medium ${networkStatus.ip_forwarding ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{networkStatus.ip_forwarding ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">WireGuard Interface:</span>
|
||||
<span className={`font-medium ${networkStatus.interface_status ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{networkStatus.interface_status ? 'Up (wg0)' : 'Down'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">NAT Translation:</span>
|
||||
<span className={`font-medium ${networkStatus.nat_rules ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{networkStatus.nat_rules ? 'Active' : 'Not Configured'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">Traffic Forwarding:</span>
|
||||
<span className={`font-medium ${networkStatus.forwarding_rules ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{networkStatus.forwarding_rules ? 'Allowed' : 'Blocked'}
|
||||
</span>
|
||||
{/* Routing table */}
|
||||
{networkStatus.routing_status?.routing_table?.length > 0 && (
|
||||
<div className="bg-gray-50 rounded-lg p-4">
|
||||
<h4 className="text-sm font-medium text-gray-700 mb-2">Active Routes</h4>
|
||||
<div className="space-y-1 font-mono text-xs text-gray-600">
|
||||
{networkStatus.routing_status.routing_table.map((r, i) => (
|
||||
<div key={i} className="flex gap-4">
|
||||
<span className="text-gray-900 w-40 truncate">{r.parsed?.destination || r.route}</span>
|
||||
<span className="text-gray-500">via {r.parsed?.dev || '—'}</span>
|
||||
{r.parsed?.via && <span className="text-gray-400">{r.parsed.via}</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="bg-blue-50 p-4 rounded-lg">
|
||||
<h4 className="text-md font-medium text-blue-900 mb-2">Quick Actions</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
className="btn btn-sm btn-outline"
|
||||
onClick={fetchNetworkStatus}
|
||||
>
|
||||
Refresh Status
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={setupNetworkConfiguration}
|
||||
disabled={isSettingUp}
|
||||
>
|
||||
{isSettingUp ? 'Setting up...' : 'Setup Network'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button className="btn btn-secondary text-sm" onClick={fetchNetworkStatus}>Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-8">
|
||||
<p className="text-gray-500">Failed to load network status</p>
|
||||
<button
|
||||
className="btn btn-primary mt-2"
|
||||
onClick={fetchNetworkStatus}
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
<p className="text-gray-500">Could not load network status</p>
|
||||
<button className="btn btn-primary mt-2" onClick={fetchNetworkStatus}>Retry</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user