Skip to article
Documentation

src/lib/auth/page.tsx

Linked repository file from this build.

import { NavigationLink as Link } from "@/components/navigation-link";
import { redirect } from "next/navigation";
import type { ReactNode } from "react";
import { AccountSetupError, getActor } from "./server";
import { AccessError, can, type Capability } from "./scope";
import { ReviewProvider } from "@/features/reviews/provider";
const capabilityLabels: Partial<Record<Capability, string>> = {
  import: "imports",
  report: "reports",
  export: "exports",
  approvalQueue: "the Approval Queue",
};

/** Server wrapper for app pages: requires a session and, if given, one capability. */
export async function ProtectedPage({
  children,
  capability,
}: {
  children: ReactNode;
  capability?: Capability;
}) {
  const actor = await getPageActor();
  const content =
    capability && !can(actor, capability) ? (
      <main className="mx-auto max-w-xl space-y-4 p-8">
        <h1 className="text-xl font-semibold">Access restricted</h1>
        <p>Your role does not include {capabilityLabels[capability] ?? capability}.</p>
        <Link className="underline" href="/dashboard">
          Return to Home
        </Link>
      </main>
    ) : (
      children
    );
  return <ReviewProvider loadReview={false}>{content}</ReviewProvider>;
}

export async function getPageActor() {
  let actor;
  try {
    actor = await getActor();
  } catch (error) {
    if (error instanceof AccountSetupError) redirect("/account-setup");
    if (error instanceof AccessError && error.status === 401) redirect("/sign-in");
    throw error;
  }
  return actor;
}