diff --git a/app/components/Attribute.tsx b/app/components/Attribute.tsx index 604a6c9..8b89f0f 100644 --- a/app/components/Attribute.tsx +++ b/app/components/Attribute.tsx @@ -10,8 +10,8 @@ type Properties = { export default function Attribute({ name, value, isCopyable }: Properties) { const canCopy = isCopyable ?? false return ( -
-
+
+
{name}
diff --git a/app/routes/_data.tsx b/app/routes/_data.tsx index aa671cd..6df72b2 100644 --- a/app/routes/_data.tsx +++ b/app/routes/_data.tsx @@ -1,4 +1,4 @@ -import { CpuChipIcon, ServerStackIcon } from '@heroicons/react/24/outline' +import { CpuChipIcon, ServerStackIcon, UsersIcon } from '@heroicons/react/24/outline' import { type LoaderFunctionArgs, redirect } from '@remix-run/node' import { Outlet } from '@remix-run/react' @@ -46,6 +46,7 @@ export default function Layout() {
}/> + }/>
diff --git a/app/routes/_data.users._index.tsx b/app/routes/_data.users._index.tsx new file mode 100644 index 0000000..7850898 --- /dev/null +++ b/app/routes/_data.users._index.tsx @@ -0,0 +1,66 @@ +/* eslint-disable unicorn/filename-case */ +import { ClipboardIcon, UserIcon } from '@heroicons/react/24/outline' +import { type LoaderFunctionArgs } from '@remix-run/node' +import { useLoaderData } from '@remix-run/react' +import { toast } from 'react-hot-toast/headless' + +import Attribute from '~/components/Attribute' +import StatusCircle from '~/components/StatusCircle' +import { type Machine, type User } from '~/types' +import { pull } from '~/utils/headscale' +import { getSession } from '~/utils/sessions' +import { useLiveData } from '~/utils/useLiveData' + +export async function loader({ request }: LoaderFunctionArgs) { + const session = await getSession(request.headers.get('Cookie')) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const data = await pull<{ nodes: Machine[] }>('v1/node', session.get('hsApiKey')!) + + const users = new Map() + for (const machine of data.nodes) { + const { user } = machine + if (!users.has(user.id)) { + users.set(user.id, []) + } + + users.get(user.id)?.push(machine) + } + + return [...users.values()].map(machines => { + const { user } = machines[0] + + return { + ...user, + machines + } + }) +} + +export default function Page() { + const data = useLoaderData() + useLiveData({ interval: 3000 }) + + return ( +
+ {data.map(user => ( +
+
+ + + {user.name} + +
+
+ {user.machines.map(machine => ( +
+ + +
+ ))} +
+
+ ))} +
+ ) +}