feat: external IP detection, port status, fix peer config generation
- WireGuardManager: get_external_ip() (cached 1h), check_port_open(), get_server_config() returning public_key + detected endpoint - API: /api/wireguard/server-config returns real external IP; /api/wireguard/refresh-ip forces re-detection; /api/wireguard/peers/config now looks up peer IP + private key from registry and uses real server endpoint automatically - Fix doubled port in Endpoint (178.x:51820:51820 → 178.x:51820) - Fix Address=/32 when peer_ip already has mask - WebUI nginx: proxy /api/ and /health to cell-api (fixes localhost:3000 hardcode — UI now works from any machine) - api.js: baseURL='' so all calls go through nginx proxy - WireGuard page: show Server Endpoint card with external IP, endpoint, public key, and Refresh IP button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,21 @@ server {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Proxy API and health calls to the backend container
|
||||
location /api/ {
|
||||
proxy_pass http://cell-api:3000/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
location /health {
|
||||
proxy_pass http://cell-api:3000/health;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# Handle client-side routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Shield, Key, Users, Activity, Wifi, Download, Copy, RefreshCw, Play, Pause, AlertCircle, Eye } from 'lucide-react';
|
||||
import { Shield, Key, Users, Activity, Wifi, Download, Copy, RefreshCw, Play, Pause, AlertCircle, Eye, Globe, CheckCircle, XCircle } from 'lucide-react';
|
||||
import { wireguardAPI, peerAPI } from '../services/api';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
function WireGuard() {
|
||||
const [status, setStatus] = useState(null);
|
||||
const [serverConfig, setServerConfig] = useState(null);
|
||||
const [isRefreshingIp, setIsRefreshingIp] = useState(false);
|
||||
const [peers, setPeers] = useState([]);
|
||||
const [totalPeers, setTotalPeers] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -19,15 +21,30 @@ function WireGuard() {
|
||||
fetchWireGuardData();
|
||||
}, []);
|
||||
|
||||
const refreshExternalIp = async () => {
|
||||
setIsRefreshingIp(true);
|
||||
try {
|
||||
const response = await fetch('/api/wireguard/refresh-ip', { method: 'POST' });
|
||||
const data = await response.json();
|
||||
setServerConfig(prev => ({ ...prev, ...data }));
|
||||
} catch (e) {
|
||||
console.error('Failed to refresh IP:', e);
|
||||
} finally {
|
||||
setIsRefreshingIp(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchWireGuardData = async () => {
|
||||
try {
|
||||
const [statusResponse, peersResponse, wireguardResponse] = await Promise.all([
|
||||
const [statusResponse, peersResponse, wireguardResponse, serverConfigResponse] = await Promise.all([
|
||||
wireguardAPI.getStatus(),
|
||||
peerAPI.getPeers(),
|
||||
wireguardAPI.getPeers()
|
||||
wireguardAPI.getPeers(),
|
||||
fetch('/api/wireguard/server-config').then(r => r.json()).catch(() => null),
|
||||
]);
|
||||
|
||||
|
||||
setStatus(statusResponse.data);
|
||||
if (serverConfigResponse) setServerConfig(serverConfigResponse);
|
||||
|
||||
// Merge peer registry data with WireGuard data (same as Peers page)
|
||||
const peersData = peersResponse.data || [];
|
||||
@@ -160,25 +177,18 @@ function WireGuard() {
|
||||
};
|
||||
|
||||
const getServerConfig = async () => {
|
||||
if (serverConfig?.public_key) return serverConfig;
|
||||
try {
|
||||
// Try to get server configuration from API
|
||||
const response = await fetch('/api/wireguard/server-config');
|
||||
if (response.ok) {
|
||||
const config = await response.json();
|
||||
return {
|
||||
public_key: config.public_key || "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
||||
endpoint: config.endpoint || "YOUR_SERVER_IP:51820"
|
||||
};
|
||||
setServerConfig(config);
|
||||
return config;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Could not get server config:', error);
|
||||
}
|
||||
|
||||
// Return default values
|
||||
return {
|
||||
public_key: "SERVER_PUBLIC_KEY_PLACEHOLDER",
|
||||
endpoint: "YOUR_SERVER_IP:51820"
|
||||
};
|
||||
return { public_key: '', endpoint: '<SERVER_IP>:51820' };
|
||||
};
|
||||
|
||||
const generateWireGuardConfig = (peer) => {
|
||||
@@ -354,6 +364,50 @@ PersistentKeepalive = ${peer.persistent_keepalive || 25}`;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* External IP & Port Status */}
|
||||
<div className="card mb-8">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center">
|
||||
<Globe className="h-5 w-5 text-gray-500 mr-2" />
|
||||
<h2 className="text-lg font-semibold text-gray-900">Server Endpoint</h2>
|
||||
</div>
|
||||
<button
|
||||
onClick={refreshExternalIp}
|
||||
disabled={isRefreshingIp}
|
||||
className="btn btn-secondary flex items-center text-sm"
|
||||
>
|
||||
<RefreshCw className={`h-3 w-3 mr-1 ${isRefreshingIp ? 'animate-spin' : ''}`} />
|
||||
Refresh IP
|
||||
</button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">External IP</p>
|
||||
<p className="font-mono font-semibold text-gray-900">
|
||||
{serverConfig?.external_ip || <span className="text-yellow-600">Detecting…</span>}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">WireGuard Endpoint</p>
|
||||
<p className="font-mono font-semibold text-gray-900">
|
||||
{serverConfig?.endpoint || `<SERVER_IP>:${serverConfig?.port || 51820}`}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 mb-1">Server Public Key</p>
|
||||
<p className="font-mono text-xs text-gray-700 break-all">
|
||||
{serverConfig?.public_key || '—'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{serverConfig && !serverConfig.external_ip && (
|
||||
<div className="mt-3 flex items-center text-yellow-700 bg-yellow-50 rounded p-2 text-sm">
|
||||
<AlertCircle className="h-4 w-4 mr-2 flex-shrink-0" />
|
||||
External IP could not be detected. Check internet connectivity, then click Refresh IP.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Traffic Stats */}
|
||||
{status?.total_traffic && (
|
||||
<div className="card mb-8">
|
||||
|
||||
@@ -2,7 +2,7 @@ import axios from 'axios';
|
||||
|
||||
// Create axios instance with base configuration
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',
|
||||
baseURL: import.meta.env.VITE_API_URL || '',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
Reference in New Issue
Block a user