import { GlobeLock, RouteOff } from 'lucide-react'; import { useMemo } from 'react'; import { useFetcher } from 'react-router'; import Dialog from '~/components/Dialog'; import Link from '~/components/Link'; import Switch from '~/components/Switch'; import TableList from '~/components/TableList'; import type { Machine, Route } from '~/types'; import cn from '~/utils/cn'; interface RoutesProps { machine: Machine; routes: Route[]; isOpen: boolean; setIsOpen: (isOpen: boolean) => void; } // TODO: Support deleting routes export default function Routes({ machine, routes, isOpen, setIsOpen, }: RoutesProps) { const fetcher = useFetcher(); // This is much easier with Object.groupBy but it's too new for us const { exit, subnet } = routes.reduce<{ exit: Route[]; subnet: Route[]; }>( (acc, route) => { if (route.prefix === '::/0' || route.prefix === '0.0.0.0/0') { acc.exit.push(route); return acc; } acc.subnet.push(route); return acc; }, { exit: [], subnet: [] }, ); const exitEnabled = useMemo(() => { if (exit.length !== 2) return false; return exit[0].enabled && exit[1].enabled; }, [exit]); return ( Edit route settings of {machine.givenName} Subnet routes Connect to devices you can't install Tailscale on by advertising IP ranges as subnet routes.{' '} Learn More {subnet.length === 0 ? (

No routes are advertised by this machine

) : undefined} {subnet.map((route) => (

{route.prefix}

{ const form = new FormData(); form.set('id', machine.id); form.set('_method', 'routes'); form.set('route', route.id); form.set('enabled', String(checked)); fetcher.submit(form, { method: 'POST', }); }} />
))}
Exit nodes Allow your network to route internet traffic through this machine.{' '} Learn More {exit.length === 0 ? (

This machine is not an exit node

) : (

Use as exit node

{ const form = new FormData(); form.set('id', machine.id); form.set('_method', 'exit-node'); form.set('routes', exit.map((route) => route.id).join(',')); form.set('enabled', String(checked)); fetcher.submit(form, { method: 'POST', }); }} />
)}
); }