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

@@ -0,0 +1,38 @@
import { NextResponse } from "next/server";
import { db } from "@/lib/prisma";
import { UserRole } from "@prisma/client";
import { supabaseServer } from "@/lib/supabase/server";
export async function GET() {
try {
const supabase = await supabaseServer();
if (!supabase) {
return NextResponse.json({ user: null, isTeacher: false });
}
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return NextResponse.json({ user: null, isTeacher: false });
}
const profile = await db.profile.findUnique({
where: { id: user.id },
select: { role: true },
}).catch(() => null);
const isTeacher =
!!profile &&
(profile.role === UserRole.TEACHER || profile.role === UserRole.SUPER_ADMIN);
return NextResponse.json({
user: { id: user.id, email: user.email ?? null },
isTeacher,
});
} catch (error) {
console.error("Failed to resolve auth session route. Returning anonymous fallback.", error);
return NextResponse.json({ user: null, isTeacher: false });
}
}

View File

@@ -0,0 +1,69 @@
import { NextResponse } from "next/server";
import { requireUser } from "@/lib/auth/requireUser";
import { db } from "@/lib/prisma";
import { buildCertificatePdf } from "@/lib/certificates";
function getText(value: unknown): string {
if (!value) return "";
if (typeof value === "string") return value;
if (typeof value === "object") {
const record = value as Record<string, unknown>;
if (typeof record.en === "string") return record.en;
if (typeof record.es === "string") return record.es;
}
return "";
}
type Params = {
params: Promise<{ id: string }>;
};
export async function GET(_: Request, { params }: Params) {
try {
const user = await requireUser();
if (!user?.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { id } = await params;
const certificate = await db.certificate.findUnique({
where: { id },
include: {
course: {
select: {
title: true,
},
},
},
});
if (!certificate || certificate.userId !== user.id) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
const certificateNumber =
(certificate as Record<string, unknown>).certificateNumber ??
(certificate.metadataSnapshot as Record<string, unknown> | null)?.certificateNumber ??
`ACVE-${certificate.id.slice(0, 8)}`;
const pdf = buildCertificatePdf({
certificateNumber: String(certificateNumber),
learnerName: user.fullName || user.email || "Learner",
learnerEmail: user.email,
courseTitle: getText(certificate.course.title) || "Untitled course",
issuedAt: certificate.issuedAt,
});
return new Response(Buffer.from(pdf), {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `attachment; filename="certificate-${String(certificateNumber)}.pdf"`,
"Cache-Control": "private, no-store",
},
});
} catch {
return NextResponse.json(
{ error: "Certificate service is not available until latest migrations are applied." },
{ status: 503 },
);
}
}