← back to Govarbitrage

src/lib/current-user.ts

38 lines

import { cookies } from "next/headers";
import { SESSION_COOKIE, verifySession, type SessionPayload } from "./session";

/** Read + verify the session for the current request (server components / routes). */
export async function getCurrentUser(): Promise<SessionPayload | null> {
  const store = await cookies();
  const token = store.get(SESSION_COOKIE)?.value;
  if (!token) return null;
  return verifySession(token);
}

export type Role = "ADMIN" | "ANALYST" | "VIEWER";
const RANK: Record<Role, number> = { VIEWER: 0, ANALYST: 1, ADMIN: 2 };

/** True if the user's role meets or exceeds the required role. */
export function hasRole(user: SessionPayload | null, required: Role): boolean {
  if (!user) return false;
  return (RANK[(user.role as Role) ?? "VIEWER"] ?? 0) >= RANK[required];
}

/**
 * Resolve the current request's subscription tier from the DB (not the JWT — a
 * subscription can change mid-session). Unauthenticated → FREE. ADMIN → PREMIUM
 * (Steve always sees everything). Imported lazily to keep this edge-safe module light.
 */
export async function getCurrentTier(): Promise<import("@prisma/client").Tier> {
  const user = await getCurrentUser();
  if (!user) return "FREE";
  if (user.role === "ADMIN") return "PREMIUM";
  // The synthetic fleet-SSO session is Steve arriving via the fleet login — an
  // internal operator, so show full data (money-math). It is still only ANALYST,
  // so the ADMIN-gated credential surface stays closed.
  if (user.sub === "fleet-sso") return "PREMIUM";
  const { prisma } = await import("./db");
  const row = await prisma.user.findUnique({ where: { id: user.sub }, select: { tier: true } });
  return row?.tier ?? "FREE";
}