import { createEventSignal } from '@solid-primitives/event-listener' import { createReconnectingWS } from '@solid-primitives/websocket' import { IconSortAscending, IconSortDescending, IconX, } from '@tabler/icons-solidjs' import { ColumnDef, SortingState, createSolidTable, flexRender, getCoreRowModel, getSortedRowModel, } from '@tanstack/solid-table' import byteSize from 'byte-size' import { isIPv6 } from 'is-ip' import { For, createSignal } from 'solid-js' import { twMerge } from 'tailwind-merge' import { secret, useRequest, wsEndpointURL } from '~/signals' import type { Connection } from '~/types' export const Connections = () => { const request = useRequest() const [search, setSearch] = createSignal('') const ws = createReconnectingWS( `${wsEndpointURL()}/connections?token=${secret()}`, ) const messageEvent = createEventSignal<{ message: WebSocketEventMap['message'] }>(ws, 'message') const connections = () => { const data = messageEvent()?.data if (!data) { return [] } return ( JSON.parse(data) as { connections: Connection[] } ).connections.slice(-100) } const onCloseConnection = (id: string) => request.delete(`connections/${id}`) const [sorting, setSorting] = createSignal([]) const columns: ColumnDef[] = [ { id: 'close', header: () => (
), cell: ({ row }) => (
), }, { accessorKey: 'ID', accessorFn: (row) => row.id, }, { accessorKey: 'Network', accessorFn: (row) => row.metadata.network, }, { accessorKey: 'Download', accessorFn: (row) => byteSize(row.download), }, { accessorKey: 'Upload', accessorFn: (row) => byteSize(row.upload), }, { accessorKey: 'Rule', accessorFn: (row) => row.rule, }, { accessorKey: 'Chains', accessorFn: (row) => row.chains.join(' -> '), }, { accessorKey: 'Remote Destination', accessorFn: (row) => row.metadata.remoteDestination, }, { accessorKey: 'Host', accessorFn: (row) => row.metadata.host, }, { accessorKey: 'DNS Mode', accessorFn: (row) => row.metadata.dnsMode, }, { accessorKey: 'Type', accessorFn: (row) => row.metadata.type, }, { accessorKey: 'Source', accessorFn: (row) => isIPv6(row.metadata.sourceIP) ? `[${row.metadata.sourceIP}]:${row.metadata.sourcePort}` : `${row.metadata.sourceIP}:${row.metadata.sourcePort}`, }, { accessorKey: 'Destination', accessorFn: (row) => isIPv6(row.metadata.destinationIP) ? `[${row.metadata.destinationIP}]:${row.metadata.destinationPort}` : `${row.metadata.destinationIP}:${row.metadata.destinationPort}`, }, ] const table = createSolidTable({ state: { get sorting() { return sorting() }, }, get data() { return search() ? connections().filter((connection) => Object.values(connection).some((conn) => JSON.stringify(conn) .toLowerCase() .includes(search().toLowerCase()), ), ) : connections() }, columns, onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), getCoreRowModel: getCoreRowModel(), }) return (
setSearch(e.target.value)} />
{(headerGroup) => ( {(header) => ( )} )} {(row) => ( {(cell) => ( )} )}
{header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} {{ asc: , desc: , }[header.column.getIsSorted() as string] ?? null}
{flexRender( cell.column.columnDef.cell, cell.getContext(), )}
) }