Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 1x 1x 10x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 8x 1x 7x 7x 3x 4x 1x 1x 2x 2x 1x 1x | import { headers } from "next/headers";
import { auth } from "@/lib/auth-server";
export interface Session {
user?: {
id: string;
email: string;
name?: string;
};
}
const DEV_AUTH_BYPASS_EMAIL = "dev@adored.test";
const DEV_AUTH_BYPASS_NAME = "Dev User";
export function isDevAuthBypassEnabled(): boolean {
return process.env.NODE_ENV === "development" && process.env.AUTH_BYPASS === "1";
}
async function getDevBypassSession(): Promise<Session> {
const [{ eq }, { getDb }, { user: userTable }, { generateId }] = await Promise.all([
import("drizzle-orm"),
import("@/lib/db"),
import("@/lib/db/schema"),
import("@/lib/id"),
]);
const db = getDb();
const email = process.env.AUTH_BYPASS_EMAIL || DEV_AUTH_BYPASS_EMAIL;
const name = process.env.AUTH_BYPASS_NAME || DEV_AUTH_BYPASS_NAME;
const existing = await db
.select({ id: userTable.id, email: userTable.email, name: userTable.name })
.from(userTable)
.where(eq(userTable.email, email))
.limit(1);
Iif (existing.length) {
const { ensureDevSampleCollection } = await import("@/lib/dev-sample-bootstrap");
await ensureDevSampleCollection(db, existing[0].id);
return { user: existing[0] };
}
const [created] = await db
.insert(userTable)
.values({
id: process.env.AUTH_BYPASS_USER_ID || generateId("user"),
email,
name,
emailVerified: true,
})
.onConflictDoNothing({ target: userTable.email })
.returning({ id: userTable.id, email: userTable.email, name: userTable.name });
Eif (created) {
const { ensureDevSampleCollection } = await import("@/lib/dev-sample-bootstrap");
await ensureDevSampleCollection(db, created.id);
return { user: created };
}
const [raced] = await db
.select({ id: userTable.id, email: userTable.email, name: userTable.name })
.from(userTable)
.where(eq(userTable.email, email))
.limit(1);
if (!raced) {
throw new Error("Unable to create dev auth bypass user");
}
const { ensureDevSampleCollection } = await import("@/lib/dev-sample-bootstrap");
await ensureDevSampleCollection(db, raced.id);
return { user: raced };
}
// THE FAÇADE (Cutover Pass 1). Reads a better-auth session and returns the
// byte-identical shape the ~249 call sites + the DAL (`userId: string`) already
// depend on: { user: { id, email, name } }. better-auth's getSession returns
// { session, user } | null; we project ONLY id/email/name off `user` so no
// extra fields (image/emailVerified/…) leak into the contract. Because we have
// NOT enabled session.cookieCache, auth.api.getSession does a fresh per-request
// DB read — so a profile display-name edit is reflected on the very next call
// (name-freshness) with no stale JWT claim to invalidate.
//
// NextAuth is fully retired (Cutover Pass 2b-i): the old authOptions +
// Credentials/EmailProvider/DrizzleAdapter config is gone, and better-auth now
// owns the whole sign-in/session surface at /api/auth. This module is nothing
// but the read façade over better-auth.
export async function getSession(): Promise<Session | null> {
if (isDevAuthBypassEnabled()) {
return getDevBypassSession();
}
const result = await auth.api.getSession({ headers: await headers() });
if (!result?.user) {
return null;
}
return {
user: {
id: result.user.id,
email: result.user.email,
name: result.user.name,
},
};
}
export class UnauthorizedError extends Error {
constructor(message = "Unauthorized") {
super(message);
this.name = "UnauthorizedError";
}
}
export async function requireAuth(): Promise<{ id: string; email: string; name?: string }> {
const session = await getSession();
if (!session?.user) {
throw new UnauthorizedError("Unauthorized");
}
return session.user;
}
|