"use client"; import { useQuery } from "@tanstack/react-query"; import { CheckCircle2, Tailscale, X } from "@/components/icons/platform-icons"; import { queryKeys } from "@/lib/shared/query-keys"; import type { TailscaleConfigPublic, TailscaleStatusPublic, } from "@/lib/shared/contracts/tailscale"; import { PopoverShell } from "@/modules/system/components/status-bar/popover-shell"; import { cn } from "@/lib/utils"; type TailscalePopoverProps = { onClose: () => void; }; async function fetchTailscaleConfig(): Promise { const res = await fetch("/api/v1/settings/tailscale", { cache: "no-store" }); const json = (await res.json()) as { data?: TailscaleConfigPublic; error?: string }; if (!res.ok) throw new Error(json.error ?? "Failed to fetch Tailscale config"); return json.data!; } async function fetchTailscaleStatus(): Promise { const res = await fetch("/api/v1/system/tailscale/status", { cache: "no-store" }); const json = (await res.json()) as { data?: TailscaleStatusPublic; error?: string }; if (!res.ok) throw new Error(json.error ?? "Failed to fetch Tailscale status"); return json.data!; } export function TailscalePopover({ onClose }: TailscalePopoverProps) { const { data: config } = useQuery({ queryKey: queryKeys.tailscaleConfig, queryFn: fetchTailscaleConfig, }); const { data: status, isLoading, error } = useQuery({ queryKey: queryKeys.tailscaleStatus, queryFn: fetchTailscaleStatus, refetchInterval: 30_000, }); const configured = Boolean(config?.tailnet && config.hasApiKey); const connected = Boolean(status?.connected); const installed = Boolean(status?.installed); const missingTun = status?.issue === "missing_tun"; const title = connected ? "Tailscale Connected" : missingTun ? "Tailscale Needs TUN" : installed ? "Tailscale Not Connected" : "Tailscale Not Installed"; return (

{title}

{configured ? "Credentials configured" : "Credentials not configured"}

{missingTun && (
Proxmox LXC must expose dev/net/tun before Tailscale can start.
)} {(status?.error || error) && !missingTun && (
{status?.error ?? (error instanceof Error ? error.message : "Unable to read Tailscale status.")}
)} {!installed && (
Install support will use the official Tailscale Linux installer and then connect this server to your tailnet.
)}
); } function StatusRow({ label, value, ok }: { label: string; value: string; ok?: boolean }) { return (
{label} {typeof ok === "boolean" && ( ok ? ( ) : ( ) )} {value}
); }