mirror of
https://github.com/tale/headplane.git
synced 2026-09-04 19:25:43 +00:00
feat: refactor several components and clean up ui
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Key, useEffect, useRef, useState } from "react";
|
||||
import type { Key } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useFetcher } from "react-router";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
@@ -21,9 +22,13 @@ interface AddAuthKeyProps {
|
||||
}
|
||||
|
||||
function findCurrentUser(users: User[], subject: string | undefined): User | undefined {
|
||||
if (!subject) return undefined;
|
||||
if (!subject) {
|
||||
return undefined;
|
||||
}
|
||||
return users.find((u) => {
|
||||
if (u.provider !== "oidc" || !u.providerId) return false;
|
||||
if (u.provider !== "oidc" || !u.providerId) {
|
||||
return false;
|
||||
}
|
||||
return u.providerId.split("/").pop() === subject;
|
||||
});
|
||||
}
|
||||
@@ -76,7 +81,9 @@ export default function AddAuthKey({
|
||||
<Dialog
|
||||
isOpen={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open && submittingRef.current) return;
|
||||
if (!open && submittingRef.current) {
|
||||
return;
|
||||
}
|
||||
setIsOpen(open);
|
||||
}}
|
||||
>
|
||||
@@ -203,11 +210,7 @@ export default function AddAuthKey({
|
||||
<Dialog.Text className="text-sm">
|
||||
Devices authenticated with this key will be automatically removed once they go
|
||||
offline.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Ephemeral Nodes Documentation"
|
||||
to="https://tailscale.com/kb/1111/ephemeral-nodes"
|
||||
>
|
||||
<Link external styled to="https://tailscale.com/kb/1111/ephemeral-nodes">
|
||||
Learn more
|
||||
</Link>
|
||||
</Dialog.Text>
|
||||
|
||||
@@ -32,7 +32,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
||||
try {
|
||||
allKeys = await api.getAllPreAuthKeys();
|
||||
} catch {
|
||||
// older versions don't support this endpoint
|
||||
// Older versions don't support this endpoint
|
||||
}
|
||||
|
||||
if (allKeys !== null) {
|
||||
@@ -47,12 +47,12 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
||||
keys = [];
|
||||
const tagOnly = keysByUser.get(null);
|
||||
if (tagOnly?.length) {
|
||||
keys.push({ user: null, preAuthKeys: tagOnly });
|
||||
keys.push({ preAuthKeys: tagOnly, user: null });
|
||||
}
|
||||
for (const user of users) {
|
||||
const userKeys = keysByUser.get(user.id);
|
||||
if (userKeys?.length) {
|
||||
keys.push({ user, preAuthKeys: userKeys });
|
||||
keys.push({ preAuthKeys: userKeys, user });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -66,34 +66,34 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
||||
.map(async (user) => {
|
||||
try {
|
||||
const preAuthKeys = await api.getPreAuthKeys(user.id);
|
||||
return { success: true as const, user, preAuthKeys };
|
||||
return { preAuthKeys, success: true as const, user };
|
||||
} catch (error) {
|
||||
log.error("api", "GET /v1/preauthkey for %s: %o", user.name, error);
|
||||
return { success: false as const, user, error, preAuthKeys: [] as const };
|
||||
return { error, preAuthKeys: [] as const, success: false as const, user };
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
keys = results
|
||||
.filter(({ success }) => success)
|
||||
.map(({ user, preAuthKeys }) => ({ user, preAuthKeys }));
|
||||
.map(({ user, preAuthKeys }) => ({ preAuthKeys, user }));
|
||||
|
||||
missing = results
|
||||
.filter((r): r is Extract<FetchResult, { success: false }> => !r.success)
|
||||
.map(({ user, error }) => ({ user, error }));
|
||||
.map(({ user, error }) => ({ error, user }));
|
||||
}
|
||||
|
||||
const canGenerateAny = context.auth.can(principal, Capabilities.generate_authkeys);
|
||||
const canGenerateOwn = context.auth.can(principal, Capabilities.generate_own_authkeys);
|
||||
|
||||
return {
|
||||
access: canGenerateAny || canGenerateOwn,
|
||||
currentSubject: principal.kind === "oidc" ? principal.user.subject : undefined,
|
||||
keys,
|
||||
missing,
|
||||
users,
|
||||
access: canGenerateAny || canGenerateOwn,
|
||||
selfServiceOnly: !canGenerateAny && canGenerateOwn,
|
||||
currentSubject: principal.kind === "oidc" ? principal.user.subject : undefined,
|
||||
url: context.config.headscale.public_url ?? context.config.headscale.url,
|
||||
users,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -192,11 +192,7 @@ export default function Page({
|
||||
<p className="mb-4">
|
||||
Headscale fully supports pre-authentication keys in order to easily add devices to your
|
||||
Tailnet. To learn more about using pre-authentication keys, visit the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Auth Keys documentation"
|
||||
to="https://tailscale.com/kb/1085/auth-keys/"
|
||||
>
|
||||
<Link external styled to="https://tailscale.com/kb/1085/auth-keys/">
|
||||
Tailscale documentation
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -28,11 +28,7 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
<p>
|
||||
Headscale fully supports pre-authentication keys in order to easily add devices to your
|
||||
Tailnet. To learn more about using pre-authentication keys, visit the{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Tailscale Auth Keys documentation"
|
||||
to="https://tailscale.com/kb/1085/auth-keys/"
|
||||
>
|
||||
<Link external styled to="https://tailscale.com/kb/1085/auth-keys/">
|
||||
Tailscale documentation
|
||||
</Link>
|
||||
</p>
|
||||
@@ -52,11 +48,7 @@ export default function Page({ loaderData: { config, isOidcEnabled } }: Route.Co
|
||||
domains, groups, or users to authenticate. This can be used to limit access to your
|
||||
Tailnet to only certain users or groups and Headplane will also respect these settings
|
||||
when authenticating.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headscale OIDC documentation"
|
||||
to="https://headscale.net/stable/ref/oidc/#basic-configuration"
|
||||
>
|
||||
<Link external styled to="https://headscale.net/stable/ref/oidc/#basic-configuration">
|
||||
Learn More
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -28,12 +28,12 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
||||
|
||||
return {
|
||||
access: context.auth.can(principal, Capabilities.configure_iam),
|
||||
writable: context.hs.writable(),
|
||||
settings: {
|
||||
domains: [...new Set(context.hs.c.oidc.allowed_domains)],
|
||||
groups: [...new Set(context.hs.c.oidc.allowed_groups)],
|
||||
users: [...new Set(context.hs.c.oidc.allowed_users)],
|
||||
},
|
||||
writable: context.hs.writable(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,11 +69,7 @@ export default function Page({ loaderData: { access, writable, settings } }: Rou
|
||||
groups, or users to authenticate. This can be used to limit access to your Tailnet to only
|
||||
certain users or groups and Headplane will also respect these settings when
|
||||
authenticating.{" "}
|
||||
<Link
|
||||
isExternal
|
||||
name="Headscale OIDC documentation"
|
||||
to="https://headscale.net/stable/ref/oidc/#basic-configuration"
|
||||
>
|
||||
<Link external styled to="https://headscale.net/stable/ref/oidc/#basic-configuration">
|
||||
Learn More
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user