session.ts66 lines · main
1import { redirect } from 'next/navigation';
2
3import { ApiError, apiFetch } from './api';
4
5export interface SessionUser {
6 id: string;
7 email: string;
8 name: string | null;
9 emailVerified: boolean;
10 image: string | null;
11 isAdmin: boolean;
12 // Personal org id auto-created by migration 0010. Web uses this as the
13 // implicit org context for every billing + project route; Phase 3 adds
14 // an org switcher that overrides it.
15 defaultOrgId: string;
16 suspendedAt: string | null;
17 // EU KYC / billing profile (all optional until paid checkout).
18 legalName: string | null;
19 companyName: string | null;
20 companyRegistrationNumber: string | null;
21 vatId: string | null;
22 vatVerifiedAt: string | null;
23 addressLine1: string | null;
24 addressLine2: string | null;
25 addressCity: string | null;
26 addressPostalCode: string | null;
27 addressRegion: string | null;
28 addressCountry: string | null;
29 dateOfBirth: string | null;
30 countryOfBirth: string | null;
31 timezone: string | null;
32 createdAt: string;
33 lastSignIn: {
34 at: string;
35 ipAddress: string | null;
36 userAgent: string | null;
37 nearBy: {
38 city: string | null;
39 region: string | null;
40 country: string | null;
41 } | null;
42 } | null;
43}
44
45/**
46 * Resolve the current session by calling apps/api's /v1/me. Returns null if
47 * the caller is unauthenticated. Use `requireUser()` below for pages that
48 * must redirect anonymous traffic to /signin.
49 */
50export async function getSessionUser(): Promise<SessionUser | null> {
51 try {
52 const res = await apiFetch('/v1/me');
53 if (res.status === 401) return null;
54 if (!res.ok) throw new ApiError(res.status, await res.text());
55 return (await res.json()) as SessionUser;
56 } catch (err) {
57 if (err instanceof ApiError && err.status === 401) return null;
58 throw err;
59 }
60}
61
62export async function requireUser(redirectTo = '/signin'): Promise<SessionUser> {
63 const user = await getSessionUser();
64 if (!user) redirect(redirectTo);
65 return user;
66}