feat: support 0.26+ routes only

This commit is contained in:
Aarnav Tale 2025-05-16 11:05:20 -04:00
parent 59b32c0842
commit eda8ed0b3e
7 changed files with 78 additions and 62 deletions

View file

@ -46,16 +46,16 @@ export default function Routes({ node, isOpen, setIsOpen }: RoutesProps) {
</TableList.Item>
) : undefined}
{subnets.map((route) => (
<TableList.Item key={route.id}>
<p>{route.prefix}</p>
<TableList.Item key={route}>
<p>{route}</p>
<Switch
defaultSelected={route.enabled}
defaultSelected={node.approvedRoutes.includes(route)}
label="Enabled"
onChange={(checked) => {
const form = new FormData();
form.set('action_id', 'update_routes');
form.set('node_id', node.id);
form.set('routes', [route.id].join(','));
form.set('routes', [route].join(','));
form.set('enabled', String(checked));
fetcher.submit(form, {
@ -95,7 +95,7 @@ export default function Routes({ node, isOpen, setIsOpen }: RoutesProps) {
form.set(
'routes',
node.customRouting.exitRoutes
.map((route) => route.id)
.map((route) => route)
.join(','),
);

View file

@ -188,6 +188,12 @@ async function updateRoutes(
nodeId: string,
context: LoadContext,
) {
const { node } = await context.client.get<{ node: Machine }>(
`v1/node/${nodeId}`,
apiKey,
);
const newApproved = node.approvedRoutes;
const routes = formData.get('routes')?.toString();
if (!routes) {
throw data('Missing `routes` in the form data.', {
@ -209,12 +215,32 @@ async function updateRoutes(
});
}
const postfix = enabled === 'true' ? 'enable' : 'disable';
await Promise.all(
allRoutes.map(async (route) => {
await context.client.post(`v1/routes/${route}/${postfix}`, apiKey);
}),
);
if (enabled === 'true') {
for (const route of allRoutes) {
// If already approved, skip, otherwise add to approved
if (newApproved.includes(route)) {
continue;
}
newApproved.push(route);
}
} else {
for (const route of allRoutes) {
// If not approved, skip, otherwise remove from approved
if (!newApproved.includes(route)) {
continue;
}
const index = newApproved.indexOf(route);
if (index > -1) {
newApproved.splice(index, 1);
}
}
}
await context.client.post(`v1/node/${nodeId}/approve_routes`, apiKey, {
routes: newApproved,
});
return { message: 'Routes updated' };
}

View file

@ -11,7 +11,7 @@ import Link from '~/components/Link';
import StatusCircle from '~/components/StatusCircle';
import Tooltip from '~/components/Tooltip';
import type { LoadContext } from '~/server';
import type { Machine, Route, User } from '~/types';
import type { Machine, User } from '~/types';
import cn from '~/utils/cn';
import { getOSInfo, getTSVersion } from '~/utils/host-info';
import { mapNodes } from '~/utils/node-info';
@ -37,20 +37,16 @@ export async function loader({
}
}
const [machine, { routes }, { users }] = await Promise.all([
const [machine, { users }] = await Promise.all([
context.client.get<{ node: Machine }>(
`v1/node/${params.id}`,
session.get('api_key')!,
),
context.client.get<{ routes: Route[] }>(
'v1/routes',
session.get('api_key')!,
),
context.client.get<{ users: User[] }>('v1/user', session.get('api_key')!),
]);
const [node] = mapNodes([machine.node], routes);
const lookup = await context.agents?.lookup([node.nodeKey]);
const lookup = await context.agents?.lookup([machine.node.nodeKey]);
const [node] = mapNodes([machine.node], lookup);
return {
node,
@ -160,7 +156,7 @@ export default function Page() {
) : (
<ul className="leading-normal">
{node.customRouting.subnetApprovedRoutes.map((route) => (
<li key={route.id}>{route.prefix}</li>
<li key={route}>{route}</li>
))}
</ul>
)}
@ -192,7 +188,7 @@ export default function Page() {
) : (
<ul className="leading-normal">
{node.customRouting.subnetWaitingRoutes.map((route) => (
<li key={route.id}>{route.prefix}</li>
<li key={route}>{route}</li>
))}
</ul>
)}

View file

@ -2,12 +2,11 @@ import { InfoIcon } from '@primer/octicons-react';
import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
import { useLoaderData } from 'react-router';
import Code from '~/components/Code';
import { ErrorPopup } from '~/components/Error';
import Link from '~/components/Link';
import Tooltip from '~/components/Tooltip';
import type { LoadContext } from '~/server';
import { Capabilities } from '~/server/web/roles';
import type { Machine, Route, User } from '~/types';
import type { Machine, User } from '~/types';
import cn from '~/utils/cn';
import { mapNodes } from '~/utils/node-info';
import MachineRow from './components/machine-row';
@ -41,15 +40,11 @@ export async function loader({
Capabilities.write_machines,
);
const [{ nodes }, { routes }, { users }] = await Promise.all([
const [{ nodes }, { users }] = await Promise.all([
context.client.get<{ nodes: Machine[] }>(
'v1/node',
session.get('api_key')!,
),
context.client.get<{ routes: Route[] }>(
'v1/routes',
session.get('api_key')!,
),
context.client.get<{ users: User[] }>('v1/user', session.get('api_key')!),
]);
@ -61,12 +56,11 @@ export async function loader({
}
const stats = await context.agents?.lookup(nodes.map((node) => node.nodeKey));
const populatedNodes = mapNodes(nodes, routes, stats);
const populatedNodes = mapNodes(nodes, stats);
return {
populatedNodes,
nodes,
routes,
users,
magic,
server: context.config.headscale.url,