refactor: remove duplicate network diagnostics from Settings, add link to Server page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
76269a50fa
commit
38b29dd2fd
@ -40,7 +40,7 @@
|
||||
|
||||
- [x] **UI-02** — Fix Web5.vue top bar: use proper glass sub-card pattern. In `neode-ui/src/views/Web5.vue` lines 10-119, the 5 quick-action cards inside the `.glass-card` container use `bg-white/5 rounded-lg`. This is the correct pattern for info sub-cards inside a glass container per CLAUDE.md CSS hierarchy (`bg-white/5` = "Simple read-only info rows"). However, verify alignment with the Server.vue quick-actions bar (lines 10-96) which uses the identical pattern. Confirm both pages are visually consistent. If Web5 cards lack `data-controller-container` and `tabindex="0"` attributes, add them for keyboard/gamepad navigation parity. **Acceptance**: Web5 and Server quick-action bars visually match. No animation changes. Deploy and verify.
|
||||
|
||||
- [ ] **UI-03** — Remove duplicate network diagnostics from Settings.vue. Settings.vue contains a "Network Diagnostics" section that duplicates functionality available on the Server.vue (Network) page. Remove the entire Network Diagnostics section from Settings.vue. Add a small link/button in Settings that says "Network Diagnostics" and routes to `/dashboard/server` instead. Keep the "Network Diagnostics" section only in Server.vue. **Acceptance**: Settings no longer shows duplicate network info; link navigates to Server page. Deploy and verify.
|
||||
- [x] **UI-03** — Remove duplicate network diagnostics from Settings.vue. Settings.vue contains a "Network Diagnostics" section that duplicates functionality available on the Server.vue (Network) page. Remove the entire Network Diagnostics section from Settings.vue. Add a small link/button in Settings that says "Network Diagnostics" and routes to `/dashboard/server` instead. Keep the "Network Diagnostics" section only in Server.vue. **Acceptance**: Settings no longer shows duplicate network info; link navigates to Server page. Deploy and verify.
|
||||
|
||||
- [ ] **UI-04** — Server.vue: wire real RPC data to Local Network card. The Local Network card in `neode-ui/src/views/Server.vue` lines 100-159 shows hardcoded values ("2 configured", "12 active", "5 rules"). Replace with data from RPC calls: `network.diagnostics` for connectivity info and `router.list-forwards` for port forwarding count. Add `onMounted` lifecycle hook to fetch data. Show skeleton loading states while fetching. **Acceptance**: Network card shows real data from backend (or graceful "N/A" if RPC unavailable). Deploy and verify.
|
||||
|
||||
|
||||
@ -542,111 +542,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Network & Connectivity Section -->
|
||||
<!-- Network Diagnostics Link -->
|
||||
<div class="glass-card px-6 py-6 mb-6">
|
||||
<div class="mb-4">
|
||||
<h2 class="text-xl font-semibold text-white/96">Network</h2>
|
||||
<p class="text-sm text-white/60 mt-1">Network connectivity, UPnP, and diagnostics</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading -->
|
||||
<div v-if="networkLoading" class="py-4 text-center">
|
||||
<p class="text-white/50 text-sm">Running diagnostics...</p>
|
||||
</div>
|
||||
|
||||
<template v-else-if="networkDiag">
|
||||
<!-- Status Grid -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
|
||||
<div class="bg-white/5 rounded-lg p-3">
|
||||
<div class="text-xs text-white/50 mb-1">WAN IP</div>
|
||||
<span class="text-sm text-white font-mono">{{ networkDiag.wan_ip || 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="bg-white/5 rounded-lg p-3">
|
||||
<div class="text-xs text-white/50 mb-1">NAT Type</div>
|
||||
<span class="text-sm text-white">{{ networkDiag.nat_type }}</span>
|
||||
</div>
|
||||
<div class="bg-white/5 rounded-lg p-3">
|
||||
<div class="text-xs text-white/50 mb-1">UPnP</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full" :class="networkDiag.upnp_available ? 'bg-green-400' : 'bg-red-400'"></div>
|
||||
<span class="text-sm text-white">{{ networkDiag.upnp_available ? 'Available' : 'Unavailable' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-white/5 rounded-lg p-3">
|
||||
<div class="text-xs text-white/50 mb-1">Tor</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full" :class="networkDiag.tor_connected ? 'bg-green-400' : 'bg-red-400'"></div>
|
||||
<span class="text-sm text-white">{{ networkDiag.tor_connected ? 'Connected' : 'Offline' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-white/96">Network</h2>
|
||||
<p class="text-sm text-white/60 mt-1">Network connectivity, UPnP, and diagnostics</p>
|
||||
</div>
|
||||
|
||||
<!-- Recommendations -->
|
||||
<div v-if="networkDiag.recommendations?.length" class="mb-4">
|
||||
<div class="text-xs text-white/50 mb-2">Recommendations</div>
|
||||
<div class="space-y-1">
|
||||
<div v-for="(rec, idx) in networkDiag.recommendations" :key="idx" class="flex items-start gap-2 text-xs text-yellow-400/80 bg-yellow-500/10 rounded-lg px-3 py-2">
|
||||
<svg class="w-3 h-3 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
|
||||
<span>{{ rec }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Port Forwards -->
|
||||
<div class="border-t border-white/10 pt-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="text-sm font-semibold text-white">Port Forwards</h3>
|
||||
<button @click="showAddForwardModal = true" class="glass-button glass-button-sm px-3 rounded-lg text-xs">Add</button>
|
||||
</div>
|
||||
<div v-if="portForwards.length" class="space-y-2">
|
||||
<div v-for="fwd in portForwards" :key="fwd.id" class="flex items-center justify-between p-2 bg-white/5 rounded-lg">
|
||||
<div class="text-sm text-white">
|
||||
<span class="font-medium">{{ fwd.service_name }}</span>
|
||||
<span class="text-white/40 text-xs ml-2">:{{ fwd.internal_port }} → :{{ fwd.external_port }} ({{ fwd.protocol }})</span>
|
||||
</div>
|
||||
<button @click="removePortForward(fwd.id)" class="text-white/30 hover:text-red-400 transition-colors p-1">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-white/40 text-xs py-3">No port forwards configured</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex justify-end pt-3">
|
||||
<button @click="runNetworkDiag" :disabled="networkLoading" class="glass-button glass-button-sm px-4 rounded-lg text-sm disabled:opacity-50">
|
||||
{{ networkLoading ? 'Running...' : 'Run Diagnostics' }}
|
||||
<button @click="router.push('/dashboard/server')" class="glass-button px-4 py-2 rounded-lg text-sm flex items-center gap-2">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
Network Diagnostics
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Port Forward Modal -->
|
||||
<div v-if="showAddForwardModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" @click.self="showAddForwardModal = false">
|
||||
<div class="glass-card p-6 w-full max-w-sm mx-4">
|
||||
<h2 class="text-lg font-bold text-white mb-4">Add Port Forward</h2>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="text-white/60 text-xs block mb-1">Service Name</label>
|
||||
<input v-model="newFwdService" type="text" placeholder="Bitcoin RPC" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-white/60 text-xs block mb-1">Internal Port</label>
|
||||
<input v-model.number="newFwdInternal" type="number" placeholder="8332" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-white/60 text-xs block mb-1">External Port</label>
|
||||
<input v-model.number="newFwdExternal" type="number" placeholder="8332" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="fwdError" class="text-xs text-red-400 mt-2">{{ fwdError }}</div>
|
||||
<div class="flex gap-3 mt-4">
|
||||
<button @click="showAddForwardModal = false" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">Cancel</button>
|
||||
<button @click="addPortForward" :disabled="!newFwdService.trim() || !newFwdInternal || !newFwdExternal" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-blue-500/20 border-blue-500/30 disabled:opacity-50">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -956,81 +866,9 @@ function closeChangePasswordModal() {
|
||||
changePasswordForm.value = { currentPassword: '', newPassword: '', confirmPassword: '', alsoChangeSsh: true }
|
||||
}
|
||||
|
||||
// --- Network & Connectivity ---
|
||||
interface NetworkDiagData {
|
||||
wan_ip: string | null
|
||||
nat_type: string
|
||||
upnp_available: boolean
|
||||
tor_connected: boolean
|
||||
dns_working: boolean
|
||||
recommendations: string[]
|
||||
}
|
||||
interface PortForwardData {
|
||||
id: string
|
||||
service_name: string
|
||||
internal_port: number
|
||||
external_port: number
|
||||
protocol: string
|
||||
enabled: boolean
|
||||
}
|
||||
const networkDiag = ref<NetworkDiagData | null>(null)
|
||||
const networkLoading = ref(false)
|
||||
const portForwards = ref<PortForwardData[]>([])
|
||||
const showAddForwardModal = ref(false)
|
||||
const newFwdService = ref('')
|
||||
const newFwdInternal = ref(0)
|
||||
const newFwdExternal = ref(0)
|
||||
const fwdError = ref('')
|
||||
|
||||
async function runNetworkDiag() {
|
||||
networkLoading.value = true
|
||||
try {
|
||||
const [diagRes, fwdRes] = await Promise.all([
|
||||
rpcClient.call<NetworkDiagData>({ method: 'network.diagnostics' }),
|
||||
rpcClient.call<{ forwards: PortForwardData[] }>({ method: 'router.list-forwards' }),
|
||||
])
|
||||
networkDiag.value = diagRes
|
||||
portForwards.value = fwdRes.forwards || []
|
||||
} catch {
|
||||
networkDiag.value = null
|
||||
} finally {
|
||||
networkLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function addPortForward() {
|
||||
if (!newFwdService.value.trim() || !newFwdInternal.value || !newFwdExternal.value) return
|
||||
fwdError.value = ''
|
||||
try {
|
||||
await rpcClient.call({ method: 'router.add-forward', params: {
|
||||
service_name: newFwdService.value.trim(),
|
||||
internal_port: newFwdInternal.value,
|
||||
external_port: newFwdExternal.value,
|
||||
protocol: 'TCP',
|
||||
}})
|
||||
showAddForwardModal.value = false
|
||||
newFwdService.value = ''
|
||||
newFwdInternal.value = 0
|
||||
newFwdExternal.value = 0
|
||||
await runNetworkDiag()
|
||||
} catch (e: unknown) {
|
||||
fwdError.value = e instanceof Error ? e.message : 'Failed to add forward'
|
||||
}
|
||||
}
|
||||
|
||||
async function removePortForward(id: string) {
|
||||
try {
|
||||
await rpcClient.call({ method: 'router.remove-forward', params: { id } })
|
||||
await runNetworkDiag()
|
||||
} catch {
|
||||
// Silent
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
checkClaudeStatus()
|
||||
loadTotpStatus()
|
||||
runNetworkDiag()
|
||||
if (!serverTorAddressFromStore.value) {
|
||||
try {
|
||||
const res = await rpcClient.getTorAddress()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user