import { CheckCircle, CircleSlash, Info, UserCircle } from 'lucide-react'; import { useMemo, useState } from 'react'; import { data, Link as RemixLink } from 'react-router'; import Attribute from '~/components/Attribute'; import Button from '~/components/Button'; import Card from '~/components/Card'; import Chip from '~/components/Chip'; import Link from '~/components/Link'; import StatusCircle from '~/components/StatusCircle'; import Tooltip from '~/components/Tooltip'; import cn from '~/utils/cn'; import { getOSInfo, getTSVersion } from '~/utils/host-info'; import { mapNodes, sortNodeTags } from '~/utils/node-info'; import type { Route } from './+types/machine'; import { mapTagsToComponents, uiTagsForNode } from './components/machine-row'; import MenuOptions from './components/menu'; import Routes from './dialogs/routes'; import { machineAction } from './machine-actions'; export async function loader({ request, params, context }: Route.LoaderArgs) { const session = await context.sessions.auth(request); if (!params.id) { throw new Error('No machine ID provided'); } if (params.id.endsWith('.ico')) { throw data(null, { status: 204 }); } let magic: string | undefined; if (context.hs.readable()) { if (context.hs.c?.dns.magic_dns) { magic = context.hs.c.dns.base_domain; } } const api = context.hsApi.getRuntimeClient(session.api_key); const [nodes, users] = await Promise.all([api.getNodes(), api.getUsers()]); const node = nodes.find((node) => node.id === params.id); const lookup = await context.agents?.lookup([node.nodeKey]); const [enhancedNode] = mapNodes([node], lookup); const tags = Array.from( new Set([...node.validTags, ...node.forcedTags]), ).sort(); return { node: enhancedNode, tags, users, magic, agent: context.agents?.agentID(), stats: lookup?.[enhancedNode.nodeKey], existingTags: sortNodeTags(nodes), }; } export const action = machineAction; export default function Page({ loaderData: { node, tags, users, magic, agent, stats, existingTags }, }: Route.ComponentProps) { const [showRouting, setShowRouting] = useState(false); const uiTags = useMemo(() => { const tags = uiTagsForNode(node, agent === node.nodeKey); return tags; }, [node, agent]); return (
Status
Subnets let you expose physical network routes onto Tailscale.{' '} Learn More
Information about this machine’s network. Used to debug connection issues.
Addresses
Client Connectivity