← back to Govarbitrage

src/lib/current-user.ts

54 lines

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

/** 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";
}

/**
 * Whether the money-math (recommended max bid / ROI / valuations / opportunity
 * scores) is visible for this request. Visibility is a function of the resolved
 * subscription tier ONLY — every client (iOS app, browser, curl) sees identical
 * data for a given tier. The FREE tier includes the full analysis, so a fresh
 * install with no account and no purchase gets the same JSON a signed-in user or
 * an anonymous browser gets. Nothing keys off the client, a header, or any other
 * undisclosed signal.
 */
export async function moneyMathVisible(
  tier: import("@prisma/client").Tier
): Promise<boolean> {
  return tierDef(tier).limits.showMoneyMath;
}