diff --git a/app/components/page-error.tsx b/app/components/page-error.tsx
index 315b9c6..265f75e 100644
--- a/app/components/page-error.tsx
+++ b/app/components/page-error.tsx
@@ -4,7 +4,7 @@ import { isRouteErrorResponse, useRevalidator } from "react-router";
import { isConnectionError } from "~/server/headscale/api/error-client";
import cn from "~/utils/cn";
-import Button from "./Button";
+import Button from "./button";
import { ErrorBanner } from "./error-banner";
interface PageErrorProps {
diff --git a/app/layouts/dashboard.tsx b/app/layouts/dashboard.tsx
deleted file mode 100644
index f07728b..0000000
--- a/app/layouts/dashboard.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Outlet, redirect } from "react-router";
-
-import { ErrorBanner } from "~/components/error-banner";
-import { pruneEphemeralNodes } from "~/server/db/pruner";
-import { isDataUnauthorizedError } from "~/server/headscale/api/error-client";
-import log from "~/utils/log";
-
-import type { Route } from "./+types/dashboard";
-
-export async function loader({ request, context, ...rest }: Route.LoaderArgs) {
- const principal = await context.auth.require(request);
- const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
- const api = context.hsApi.getRuntimeClient(apiKey);
-
- // MARK: The session should stay valid if Headscale isn't healthy
- const healthy = await api.isHealthy();
- if (healthy) {
- try {
- await api.getApiKeys();
- await pruneEphemeralNodes({ context, request, ...rest });
- } catch (error) {
- if (isDataUnauthorizedError(error)) {
- const displayName =
- principal.kind === "oidc" ? principal.profile.name : principal.displayName;
- log.warn("auth", "Logging out %s due to expired API key", displayName);
- return redirect("/login", {
- headers: {
- "Set-Cookie": await context.auth.destroySession(request),
- },
- });
- }
- }
- }
-
- return {
- healthy,
- };
-}
-
-export default function Layout() {
- return (
-
-
-
- );
-}
-
-export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
- return (
-
-
-
- );
-}
diff --git a/app/routes/dns/components/rename-tailnet.tsx b/app/routes/dns/components/rename-tailnet.tsx
index 524194c..8a08d20 100644
--- a/app/routes/dns/components/rename-tailnet.tsx
+++ b/app/routes/dns/components/rename-tailnet.tsx
@@ -1,4 +1,4 @@
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Code from "~/components/Code";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
diff --git a/app/routes/dns/components/toggle-magic.tsx b/app/routes/dns/components/toggle-magic.tsx
index 6177229..c7fe3e4 100644
--- a/app/routes/dns/components/toggle-magic.tsx
+++ b/app/routes/dns/components/toggle-magic.tsx
@@ -1,4 +1,4 @@
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Text from "~/components/Text";
import Title from "~/components/Title";
diff --git a/app/routes/dns/dialogs/add-ns.tsx b/app/routes/dns/dialogs/add-ns.tsx
index f5256b5..00832a9 100644
--- a/app/routes/dns/dialogs/add-ns.tsx
+++ b/app/routes/dns/dialogs/add-ns.tsx
@@ -1,7 +1,7 @@
import { Split } from "lucide-react";
import { useMemo, useState } from "react";
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Chip from "~/components/Chip";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
diff --git a/app/routes/dns/dialogs/add-record.tsx b/app/routes/dns/dialogs/add-record.tsx
index 55f8ed9..e3def7b 100644
--- a/app/routes/dns/dialogs/add-record.tsx
+++ b/app/routes/dns/dialogs/add-record.tsx
@@ -1,6 +1,6 @@
import { useMemo, useState } from "react";
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Code from "~/components/Code";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
diff --git a/app/routes/machines/machine.tsx b/app/routes/machines/machine.tsx
index 0df5995..386cd99 100644
--- a/app/routes/machines/machine.tsx
+++ b/app/routes/machines/machine.tsx
@@ -48,11 +48,14 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
const nodes = nodesSnap.data;
const users = usersSnap.data;
const node = nodes.find((node) => node.id === params.id);
+ if (node == null) {
+ throw data(null, { status: 404 });
+ }
const lookup = await context.agents?.lookup([node.nodeKey]);
const [enhancedNode] = mapNodes([node], lookup);
const tags = [...node.tags].toSorted();
- const supportsNodeOwnerChange = !context.hsApi.clientHelpers.isAtleast("0.28.0-beta.1");
+ const supportsNodeOwnerChange = !context.hsApi.clientHelpers.isAtleast("0.28.0");
return {
agent: context.agents?.agentID(),
diff --git a/app/routes/machines/overview.tsx b/app/routes/machines/overview.tsx
index faff4d3..94d44d5 100644
--- a/app/routes/machines/overview.tsx
+++ b/app/routes/machines/overview.tsx
@@ -46,7 +46,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
const stats = await context.agents?.lookup(nodes.map((node) => node.nodeKey));
const populatedNodes = mapNodes(nodes, stats);
- const supportsNodeOwnerChange = !context.hsApi.clientHelpers.isAtleast("0.28.0-beta.1");
+ const supportsNodeOwnerChange = !context.hsApi.clientHelpers.isAtleast("0.28.0");
return {
agent: context.agents?.agentID(),
diff --git a/app/routes/settings/auth-keys/dialogs/expire-auth-key.tsx b/app/routes/settings/auth-keys/dialogs/expire-auth-key.tsx
index b19a44f..b8a2792 100644
--- a/app/routes/settings/auth-keys/dialogs/expire-auth-key.tsx
+++ b/app/routes/settings/auth-keys/dialogs/expire-auth-key.tsx
@@ -1,4 +1,4 @@
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Text from "~/components/Text";
import Title from "~/components/Title";
diff --git a/app/routes/settings/restrictions/dialogs/add-domain.tsx b/app/routes/settings/restrictions/dialogs/add-domain.tsx
index e3f9eb5..8c9ddf5 100644
--- a/app/routes/settings/restrictions/dialogs/add-domain.tsx
+++ b/app/routes/settings/restrictions/dialogs/add-domain.tsx
@@ -1,6 +1,6 @@
import { useMemo, useState } from "react";
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
import Text from "~/components/Text";
diff --git a/app/routes/settings/restrictions/dialogs/add-group.tsx b/app/routes/settings/restrictions/dialogs/add-group.tsx
index 82d0e05..78985b2 100644
--- a/app/routes/settings/restrictions/dialogs/add-group.tsx
+++ b/app/routes/settings/restrictions/dialogs/add-group.tsx
@@ -1,6 +1,6 @@
import { useMemo, useState } from "react";
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
import Text from "~/components/Text";
diff --git a/app/routes/settings/restrictions/dialogs/add-user.tsx b/app/routes/settings/restrictions/dialogs/add-user.tsx
index 039d420..f2ac2f1 100644
--- a/app/routes/settings/restrictions/dialogs/add-user.tsx
+++ b/app/routes/settings/restrictions/dialogs/add-user.tsx
@@ -1,6 +1,6 @@
import { useMemo, useState } from "react";
-import Button from "~/components/Button";
+import Button from "~/components/button";
import Dialog, { DialogPanel } from "~/components/Dialog";
import Input from "~/components/Input";
import Text from "~/components/Text";
diff --git a/app/routes/ssh/user-prompt.tsx b/app/routes/ssh/user-prompt.tsx
index bbf160d..d8a2e4b 100644
--- a/app/routes/ssh/user-prompt.tsx
+++ b/app/routes/ssh/user-prompt.tsx
@@ -1,47 +1,46 @@
-import { useState } from 'react';
-import Button from '~/components/Button';
-import Card from '~/components/Card';
-import Code from '~/components/Code';
-import Input from '~/components/Input';
+import { useState } from "react";
+
+import Button from "~/components/button";
+import Card from "~/components/Card";
+import Code from "~/components/Code";
+import Input from "~/components/Input";
interface UserPromptProps {
- hostname: string;
+ hostname: string;
}
export default function UserPrompt({ hostname }: UserPromptProps) {
- const [username, setUsername] = useState('');
+ const [username, setUsername] = useState("");
- return (
-
-
- Enter Username
-
- Enter the username you want to use to connect to{' '}
- {hostname}
- {'. '}
- WebSSH follows the Headscale ACLs, so only permitted usernames will be
- able to connect.
-
-
-
-
-
- );
+ return (
+
+
+ Enter Username
+
+ Enter the username you want to use to connect to {hostname}
+ {". "}
+ WebSSH follows the Headscale ACLs, so only permitted usernames will be able to connect.
+
+
+
+
+