/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { InfoIcon } from '@primer/octicons-react' import { type ActionFunctionArgs, json, type LoaderFunctionArgs } from '@remix-run/node' import { useFetcher, useLoaderData } from '@remix-run/react' import { Button, Tooltip, TooltipTrigger } from 'react-aria-components' import Code from '~/components/Code' import { type Machine, type Route } from '~/types' import { cn } from '~/utils/cn' import { getConfig, getContext } from '~/utils/config' import { del, post, pull } from '~/utils/headscale' import { getSession } from '~/utils/sessions' import { useLiveData } from '~/utils/useLiveData' import MachineRow from './machine' export async function loader({ request }: LoaderFunctionArgs) { const session = await getSession(request.headers.get('Cookie')) const machines = await pull<{ nodes: Machine[] }>('v1/node', session.get('hsApiKey')!) const routes = await pull<{ routes: Route[] }>('v1/routes', session.get('hsApiKey')!) const context = await getContext() let magic: string | undefined if (context.hasConfig) { const config = await getConfig() if (config.dns_config.magic_dns) { magic = config.dns_config.base_domain } } return { nodes: machines.nodes, routes: routes.routes, magic } } export async function action({ request }: ActionFunctionArgs) { const session = await getSession(request.headers.get('Cookie')) if (!session.has('hsApiKey')) { return json({ message: 'Unauthorized' }, { status: 401 }) } const data = await request.formData() if (!data.has('_method') || !data.has('id')) { return json({ message: 'No method or ID provided' }, { status: 400 }) } const id = String(data.get('id')) const method = String(data.get('_method')) switch (method) { case 'delete': { await del(`v1/node/${id}`, session.get('hsApiKey')!) return json({ message: 'Machine removed' }) } case 'expire': { console.log('expire') const data = await post(`v1/node/${id}/expire`, session.get('hsApiKey')!) console.log(data) return json({ message: 'Machine expired' }) } case 'rename': { if (!data.has('name')) { return json({ message: 'No name provided' }, { status: 400 }) } const name = String(data.get('name')) await post(`v1/node/${id}/rename/${name}`, session.get('hsApiKey')!) return json({ message: 'Machine renamed' }) } case 'routes': { if (!data.has('route') || !data.has('enabled')) { return json({ message: 'No route or enabled provided' }, { status: 400 }) } const route = String(data.get('route')) const enabled = data.get('enabled') === 'true' const postfix = enabled ? 'enable' : 'disable' await post(`v1/routes/${route}/${postfix}`, session.get('hsApiKey')!) return json({ message: 'Route updated' }) } default: { return json({ message: 'Invalid method' }, { status: 400 }) } } } export default function Page() { useLiveData({ interval: 3000 }) const data = useLoaderData() const fetcher = useFetcher() return ( <>

Machines

{data.nodes.map(machine => ( route.node.id === machine.id) as unknown as Route[]} fetcher={fetcher} magic={data.magic} /> ))}
Name
Addresses {data.magic ? ( Since MagicDNS is enabled, you can access devices based on their name and also at {' '} [name].[user].{data.magic} ) : undefined}
Last Seen
) }