mirror of
https://github.com/tale/headplane.git
synced 2026-07-28 00:28:56 +00:00
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import { eq } from "drizzle-orm";
|
|
import { Outlet, redirect } from "react-router";
|
|
|
|
import Footer from "~/components/Footer";
|
|
import Header from "~/components/Header";
|
|
import { users } from "~/server/db/schema";
|
|
import { Capabilities } from "~/server/web/roles";
|
|
|
|
import { Route } from "./+types/shell";
|
|
|
|
// This loads the bare minimum for the application to function
|
|
// So we know that if context fails to load then well, oops?
|
|
export async function loader({ request, context }: Route.LoaderArgs) {
|
|
try {
|
|
const session = await context.sessions.auth(request);
|
|
if (
|
|
typeof context.oidc === "object" &&
|
|
session.user.subject !== "unknown-non-oauth" &&
|
|
!request.url.endsWith("/onboarding")
|
|
) {
|
|
const [user] = await context.db
|
|
.select()
|
|
.from(users)
|
|
.where(eq(users.sub, session.user.subject))
|
|
.limit(1);
|
|
|
|
if (!user?.onboarded) {
|
|
return redirect("/onboarding");
|
|
}
|
|
}
|
|
|
|
const api = context.hsApi.getRuntimeClient(session.api_key);
|
|
const check = await context.sessions.check(request, Capabilities.ui_access);
|
|
|
|
// OIDC users without ui_access go to pending approval
|
|
if (
|
|
!check &&
|
|
session.user.subject !== "unknown-non-oauth" &&
|
|
!request.url.endsWith("/onboarding")
|
|
) {
|
|
return redirect("/pending-approval");
|
|
}
|
|
|
|
return {
|
|
config: context.hs.c,
|
|
url: context.config.headscale.public_url ?? context.config.headscale.url,
|
|
configAvailable: context.hs.readable(),
|
|
debug: context.config.debug,
|
|
user: session.user,
|
|
access: {
|
|
ui: check,
|
|
dns: await context.sessions.check(request, Capabilities.read_network),
|
|
users: await context.sessions.check(request, Capabilities.read_users),
|
|
policy: await context.sessions.check(request, Capabilities.read_policy),
|
|
machines: await context.sessions.check(request, Capabilities.read_machines),
|
|
settings: await context.sessions.check(request, Capabilities.read_feature),
|
|
},
|
|
onboarding: request.url.endsWith("/onboarding"),
|
|
healthy: await api.isHealthy(),
|
|
};
|
|
} catch {
|
|
return redirect("/login", {
|
|
headers: {
|
|
"Set-Cookie": await context.sessions.destroySession(),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
export default function Shell({ loaderData }: Route.ComponentProps) {
|
|
return (
|
|
<>
|
|
<Header {...loaderData} />
|
|
<Outlet />
|
|
<Footer {...loaderData} />
|
|
</>
|
|
);
|
|
}
|