refactor(server): replace AppContext undefined sentinels with Feature<T>

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019e5550-4435-7118-8393-cdcc97042178
This commit is contained in:
Aarnav Tale
2026-05-23 16:32:22 -04:00
parent fb4b0b1404
commit d4eee702e9
25 changed files with 253 additions and 124 deletions
+9 -9
View File
@@ -11,13 +11,13 @@ import { formatTimeDelta } from "~/utils/time";
import type { Route } from "./+types/agent";
export async function loader({ request, context }: Route.LoaderArgs) {
const principal = await context.auth.require(request);
await context.auth.require(request);
if (!context.agents) {
return { enabled: false as const };
if (context.agents.state !== "enabled") {
return { enabled: false as const, reason: context.agents.reason };
}
const sync = context.agents.lastSync();
const sync = context.agents.value.lastSync();
return {
enabled: true as const,
syncedAt: sync.syncedAt?.toISOString() ?? null,
@@ -29,12 +29,12 @@ export async function loader({ request, context }: Route.LoaderArgs) {
export async function action({ request, context }: Route.ActionArgs) {
await context.auth.require(request);
if (!context.agents) {
return { success: false, error: "Agent is not enabled" };
if (context.agents.state !== "enabled") {
return { success: false, error: context.agents.reason };
}
await context.agents.triggerSync();
const sync = context.agents.lastSync();
await context.agents.value.triggerSync();
const sync = context.agents.value.lastSync();
return { success: !sync.error, error: sync.error };
}
@@ -47,7 +47,7 @@ export default function Page({ loaderData }: Route.ComponentProps) {
<div className="flex max-w-(--breakpoint-lg) flex-col gap-8">
<Title>Headplane Agent</Title>
<Notice title="Agent Not Enabled">
The Headplane Agent is not enabled. To learn how to set up the agent, visit the{" "}
{loaderData.reason}. To learn how to set up the agent, visit the{" "}
<Link external styled to="https://headplane.dev/docs/agent">
documentation
</Link>
+1 -3
View File
@@ -7,9 +7,7 @@ import type { PreAuthKey } from "~/types";
import type { Route } from "./+types/overview";
export async function authKeysAction({ request, context }: Route.ActionArgs) {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const { principal, api } = await context.apiForRequest(request);
const canGenerateAny = context.auth.can(principal, Capabilities.generate_authkeys);
const canGenerateOwn = context.auth.can(principal, Capabilities.generate_own_authkeys);
+1 -3
View File
@@ -19,9 +19,7 @@ import AuthKeyRow from "./auth-key-row";
import AddAuthKey from "./dialogs/add-auth-key";
export async function loader({ request, context }: Route.LoaderArgs) {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const { principal, api } = await context.apiForRequest(request);
const usersSnap = await context.hsLive.get(usersResource, api);
const users = usersSnap.data;
+2 -1
View File
@@ -8,7 +8,8 @@ import type { Route } from "./+types/overview";
export async function loader({ context }: Route.LoaderArgs) {
return {
config: context.hs.writable(),
isOidcEnabled: context.oidc?.service.status().state === "ready",
isOidcEnabled:
context.oidc.state === "enabled" && context.oidc.value.status().state === "ready",
};
}