Stage 1.5: categories, enrollment expiry, access log, bulk grant, user page

This commit is contained in:
2026-04-07 11:59:13 +05:00
parent 992763aeb9
commit e9eff5bae5
16 changed files with 790 additions and 93 deletions
+32 -6
View File
@@ -8,6 +8,7 @@ import { revalidatePath } from "next/cache";
async function requireAdmin() {
const session = await auth.api.getSession({ headers: await headers() });
if (!session || session.user.role !== "admin") throw new Error("Forbidden");
return session;
}
// ── Modules ──────────────────────────────────────────────────────────────────
@@ -45,20 +46,45 @@ export async function reorderModules(courseId: string, orderedIds: string[]) {
// ── Enrollment ───────────────────────────────────────────────────────────────
export async function grantAccess(courseId: string, userId: string) {
await requireAdmin();
export async function grantAccess(
courseId: string,
userId: string,
expiresAt?: string | null,
note?: string
) {
const session = await requireAdmin();
await prisma.courseEnrollment.upsert({
where: { userId_courseId: { userId, courseId } },
update: {},
create: { userId, courseId },
update: { expiresAt: expiresAt ? new Date(expiresAt) : null },
create: { userId, courseId, expiresAt: expiresAt ? new Date(expiresAt) : null },
});
await prisma.accessLog.create({
data: {
courseId,
userId,
action: "granted",
method: "manual",
grantedById: session.user.id,
note: note || null,
},
});
revalidatePath(`/admin/courses/${courseId}`);
}
export async function revokeAccess(courseId: string, userId: string) {
await requireAdmin();
export async function revokeAccess(courseId: string, userId: string, note?: string) {
const session = await requireAdmin();
await prisma.courseEnrollment.delete({
where: { userId_courseId: { userId, courseId } },
});
await prisma.accessLog.create({
data: {
courseId,
userId,
action: "revoked",
method: "manual",
grantedById: session.user.id,
note: note || null,
},
});
revalidatePath(`/admin/courses/${courseId}`);
}