Skip to content
File

Blob: ui/operator/ClientTunnels.svelte

3.2 KB
1<script lang="ts">
2 import Table from './Table.svelte';
3 import SnapshotTime from './SnapshotTime.svelte';
4 import type { TunnelDetail } from './types';
5 
6 let { data }: { data: TunnelDetail } = $props();
7</script>
8 
9<p class="text-muted wrap-anywhere">Client <span class="ml-1 font-mono text-[0.92em] text-foreground">{data.identity}</span></p>
10{#if data.configurationError}
11 <section class="mt-5 rounded-md border border-line bg-surface px-4 py-3" aria-labelledby="configuration-error">
12 <h2 id="configuration-error" class="text-[15px] font-semibold text-warning">Configuration unavailable</h2>
13 <details class="mt-1">
14 <summary class="min-h-8 cursor-pointer py-1 text-muted">Lookup error</summary>
15 <p class="mt-1 max-w-[72ch] font-mono text-xs wrap-anywhere">{data.configurationError}</p>
16 </details>
17 </section>
18{/if}
19{#if data.registrationError}
20 <section class="mt-5 rounded-md border border-line bg-surface px-4 py-3" aria-labelledby="registration-error">
21 <h2 id="registration-error" class="text-[15px] font-semibold text-warning">Registration unavailable</h2>
22 <details class="mt-1">
23 <summary class="min-h-8 cursor-pointer py-1 text-muted">Lookup error</summary>
24 <p class="mt-1 max-w-[72ch] font-mono text-xs wrap-anywhere">{data.registrationError}</p>
25 </details>
26 </section>
27{/if}
28 
29<div class="mt-6 mb-2.5 flex flex-wrap items-baseline justify-between gap-x-6 gap-y-1">
30 <h2 class="text-[15px] font-semibold">Hostnames</h2>
31 <p class="text-[13px] text-muted">Observed <SnapshotTime value={data.observedAt} /></p>
32</div>
33{#if data.tunnels.length}
34 <Table label="Client tunnel configuration and registration">
35 <thead><tr><th scope="col">Hostname</th><th scope="col">Configured target</th><th scope="col">Configured</th><th scope="col">Registered</th></tr></thead>
36 <tbody>
37 {#each data.tunnels as tunnel (tunnel.hostname)}
38 <tr>
39 <th scope="row" class="font-mono text-[0.92em]">{tunnel.hostname}</th>
40 <td>
41 {#if tunnel.target}
42 <span class="font-mono text-[0.92em]">{tunnel.target}</span>
43 {:else}
44 <span class="text-muted">{tunnel.configured === 'No' ? 'Not configured' : tunnel.configured === 'Unknown' ? 'Unknown' : 'No target reported'}</span>
45 {/if}
46 </td>
47 <td><span class="text-[13px] whitespace-nowrap" class:text-warning={tunnel.configured === 'Unknown'}>{tunnel.configured}</span></td>
48 <td><span class="text-[13px] whitespace-nowrap" class:text-warning={tunnel.registered === 'Unknown'}>{tunnel.registered}</span></td>
49 </tr>
50 {/each}
51 </tbody>
52 </Table>
53{:else}
54 <p class="border-y border-line py-6 text-muted">{data.configurationError || data.registrationError ? 'No tunnel details available.' : 'No configured tunnels or registered hostnames.'}</p>
55{/if}
56<details class="mt-2.5 text-[13px]">
57 <summary class="min-h-10 cursor-pointer py-2 text-muted">State key</summary>
58 <dl class="mt-1 grid grid-cols-[max-content_minmax(0,1fr)] gap-x-4 gap-y-1">
59 <dt>Configured</dt><dd class="text-muted">Current client configuration</dd>
60 <dt>Registered</dt><dd class="text-muted">Hostname ownership record</dd>
61 <dt>Unknown</dt><dd class="text-muted">Lookup unavailable</dd>
62 </dl>
63</details>