Pending course, rest ready for launch

This commit is contained in:
Marcelo
2026-03-15 13:52:11 +00:00
parent be4ca2ed78
commit 62b3cfe467
77 changed files with 6450 additions and 868 deletions

View File

@@ -1,53 +1,44 @@
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";
import { db } from "@/lib/prisma";
import { UserRole } from "@prisma/client";
import { logger } from "@/lib/logger";
import { supabaseServer } from "@/lib/supabase/server";
export async function requireTeacher() {
const supabase = await supabaseServer();
if (!supabase) {
return null;
}
const cookieStore = await cookies();
// 1. Get Supabase Session
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(cookiesToSet: { name: string; value: string; options?: CookieOptions }[]) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch (error) {
// This is expected in Server Components, but let's log it just in case
logger.warn("Failed to set cookies in Server Component context (expected behavior)", error);
}
},
},
}
);
const { data: { user } } = await supabase.auth.getUser();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return null; // Let the caller handle the redirect
}
// 2. Check Role in Database
const profile = await db.profile.findUnique({
where: { id: user.id },
}
);
console.log("AUTH_USER_ID:", user.id);
console.log("DB_PROFILE:", profile);
}).catch((error) => {
logger.error("Failed to load profile in requireTeacher", {
userId: user.id,
email: user.email,
error: error instanceof Error ? error.message : "unknown",
});
return null;
});
if (!profile || (profile.role !== UserRole.TEACHER && profile.role !== UserRole.SUPER_ADMIN)) {
// You can decide to return null or throw an error here
if (
!profile ||
(profile.role !== UserRole.TEACHER && profile.role !== UserRole.SUPER_ADMIN)
) {
logger.info("User authenticated but not authorized as teacher", {
userId: user.id,
email: user.email,
role: profile?.role ?? "none",
});
return null;
}
return profile;
}
}

View File

@@ -1,6 +1,7 @@
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
import { db } from "@/lib/prisma";
import { UserRole } from "@prisma/client";
export async function requireUser() {
const cookieStore = await cookies();
@@ -13,6 +14,20 @@ export async function requireUser() {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return null;
const profile = await db.profile.findUnique({ where: { id: user.id } });
return profile;
}
const profile = await db.profile.findUnique({ where: { id: user.id } }).catch((error) => {
console.error("Failed to load profile for authenticated user.", error);
return null;
});
if (profile) return profile;
// Keep authenticated flows working even if profile lookup fails.
return {
id: user.id,
email: user.email ?? "unknown@acve.local",
fullName: (user.user_metadata?.full_name as string | undefined) ?? null,
avatarUrl: null,
role: UserRole.LEARNER,
createdAt: new Date(0),
updatedAt: new Date(0),
};
}

View File

@@ -1,16 +0,0 @@
const parseTeacherEmails = (source: string | undefined): string[] =>
(source ?? "")
.split(",")
.map((email) => email.trim().toLowerCase())
.filter(Boolean);
export const readTeacherEmailsServer = (): string[] => parseTeacherEmails(process.env.TEACHER_EMAILS);
export const readTeacherEmailsBrowser = (): string[] =>
parseTeacherEmails(process.env.NEXT_PUBLIC_TEACHER_EMAILS);
export const isTeacherEmailAllowed = (email: string | null, allowed: string[]): boolean => {
if (!email) return false;
if (allowed.length === 0) return false;
return allowed.includes(email.toLowerCase());
};