2025-06-13 06:03:17 +00:00
|
|
|
"use server";
|
|
|
|
|
|
|
|
|
|
import prisma from "@/lib/prisma";
|
|
|
|
|
import { auth, signIn } from "@/lib/auth";
|
|
|
|
|
import { revalidatePath } from "next/cache";
|
2026-05-13 07:10:06 +00:00
|
|
|
import { Language, Locale, Status } from "@/generated/client";
|
2025-06-21 05:52:34 +00:00
|
|
|
import { analyzeCode } from "@/app/actions/analyze-code";
|
2026-05-13 07:10:06 +00:00
|
|
|
import { inngest } from "@/inngest/client";
|
|
|
|
|
import {
|
|
|
|
|
createSubmissionWithStatus,
|
|
|
|
|
updateSubmissionStatus,
|
|
|
|
|
} from "@/lib/submission-status";
|
2025-06-13 06:03:17 +00:00
|
|
|
|
|
|
|
|
export const judge = async (
|
|
|
|
|
problemId: string,
|
|
|
|
|
language: Language,
|
2026-05-06 13:16:01 +00:00
|
|
|
content: string,
|
2026-05-13 07:10:06 +00:00
|
|
|
assignmentId?: string,
|
|
|
|
|
locale: Locale = "zh"
|
2025-06-13 06:03:17 +00:00
|
|
|
): Promise<Status> => {
|
|
|
|
|
const session = await auth();
|
|
|
|
|
const userId = session?.user?.id;
|
|
|
|
|
if (!userId) {
|
|
|
|
|
await signIn();
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2026-05-06 13:16:01 +00:00
|
|
|
const actor = await prisma.user.findUnique({
|
|
|
|
|
where: { id: userId },
|
|
|
|
|
select: { id: true, role: true },
|
2025-06-13 06:03:17 +00:00
|
|
|
});
|
|
|
|
|
|
2026-05-06 13:16:01 +00:00
|
|
|
if (!actor) {
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const createSystemErrorSubmission = async (
|
|
|
|
|
message: string,
|
|
|
|
|
options?: { assignmentId?: string | null }
|
|
|
|
|
) => {
|
2026-05-13 07:10:06 +00:00
|
|
|
await createSubmissionWithStatus({
|
|
|
|
|
language,
|
|
|
|
|
content,
|
|
|
|
|
status: Status.SE,
|
|
|
|
|
message,
|
|
|
|
|
userId,
|
|
|
|
|
problemId,
|
|
|
|
|
assignmentId: options?.assignmentId ?? null,
|
2025-06-13 06:03:17 +00:00
|
|
|
});
|
2026-05-06 13:16:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let validatedAssignmentId: string | undefined;
|
|
|
|
|
if (assignmentId) {
|
|
|
|
|
const assignment = await prisma.assignment.findUnique({
|
|
|
|
|
where: { id: assignmentId },
|
|
|
|
|
select: {
|
|
|
|
|
id: true,
|
|
|
|
|
published: true,
|
|
|
|
|
course: {
|
|
|
|
|
select: {
|
|
|
|
|
archived: true,
|
|
|
|
|
teacherId: true,
|
|
|
|
|
enrollments: {
|
|
|
|
|
where: { userId },
|
|
|
|
|
select: { userId: true },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
problems: {
|
|
|
|
|
where: { problemId },
|
|
|
|
|
select: { problemId: true },
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!assignment) {
|
|
|
|
|
await createSystemErrorSubmission("Assignment not found");
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isTeacherOwner = assignment.course.teacherId === userId;
|
|
|
|
|
const isStudentEnrolled = assignment.course.enrollments.length > 0;
|
|
|
|
|
const canAccessAssignment =
|
|
|
|
|
actor.role === "ADMIN" ||
|
|
|
|
|
(actor.role === "TEACHER" && isTeacherOwner) ||
|
|
|
|
|
(actor.role === "GUEST" && isStudentEnrolled);
|
|
|
|
|
|
|
|
|
|
if (!canAccessAssignment) {
|
|
|
|
|
await createSystemErrorSubmission("No permission for assignment", {
|
|
|
|
|
assignmentId: assignment.id,
|
|
|
|
|
});
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (assignment.course.archived) {
|
|
|
|
|
await createSystemErrorSubmission("Course is archived", {
|
|
|
|
|
assignmentId: assignment.id,
|
|
|
|
|
});
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!assignment.published && actor.role === "GUEST") {
|
|
|
|
|
await createSystemErrorSubmission("Assignment is not published", {
|
|
|
|
|
assignmentId: assignment.id,
|
|
|
|
|
});
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (assignment.problems.length === 0) {
|
|
|
|
|
await createSystemErrorSubmission(
|
|
|
|
|
"Problem does not belong to assignment",
|
|
|
|
|
{
|
|
|
|
|
assignmentId: assignment.id,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validatedAssignmentId = assignment.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const problem = await prisma.problem.findUnique({
|
|
|
|
|
where: {
|
|
|
|
|
id: problemId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!problem) {
|
|
|
|
|
await createSystemErrorSubmission("Problem not found", {
|
|
|
|
|
assignmentId: validatedAssignmentId,
|
|
|
|
|
});
|
2025-06-13 06:03:17 +00:00
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const testcases = await prisma.testcase.findMany({
|
|
|
|
|
where: {
|
|
|
|
|
problemId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!testcases.length) {
|
2026-05-06 13:16:01 +00:00
|
|
|
await createSystemErrorSubmission(
|
|
|
|
|
"No testcases available for this problem",
|
|
|
|
|
{ assignmentId: validatedAssignmentId }
|
|
|
|
|
);
|
2025-06-13 06:03:17 +00:00
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dockerConfig = await prisma.dockerConfig.findUnique({
|
|
|
|
|
where: {
|
|
|
|
|
language,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!dockerConfig) {
|
2026-05-06 13:16:01 +00:00
|
|
|
await createSystemErrorSubmission(
|
|
|
|
|
`Docker configuration not found for language: ${language}`,
|
|
|
|
|
{ assignmentId: validatedAssignmentId }
|
|
|
|
|
);
|
2025-06-13 06:03:17 +00:00
|
|
|
return Status.SE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 07:10:06 +00:00
|
|
|
const submission = await createSubmissionWithStatus({
|
|
|
|
|
language,
|
|
|
|
|
content,
|
|
|
|
|
status: Status.QD,
|
|
|
|
|
userId,
|
|
|
|
|
problemId,
|
|
|
|
|
assignmentId: validatedAssignmentId,
|
2025-06-13 06:03:17 +00:00
|
|
|
});
|
|
|
|
|
|
2025-06-21 09:04:52 +00:00
|
|
|
const executeAnalyzeCode = async () => {
|
|
|
|
|
await analyzeCode({
|
|
|
|
|
content,
|
|
|
|
|
submissionId: submission.id,
|
2026-05-13 07:10:06 +00:00
|
|
|
locale,
|
2025-06-21 09:04:52 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 07:10:06 +00:00
|
|
|
executeAnalyzeCode();
|
2025-06-13 06:03:17 +00:00
|
|
|
|
2026-05-13 07:10:06 +00:00
|
|
|
try {
|
|
|
|
|
await inngest.send({
|
|
|
|
|
name: "submissions/create",
|
|
|
|
|
data: {
|
|
|
|
|
submissionId: submission.id,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
} catch (inngestError) {
|
|
|
|
|
console.error("Failed to enqueue judge event:", inngestError);
|
|
|
|
|
await updateSubmissionStatus(submission.id, Status.SE, {
|
|
|
|
|
message: "Failed to enqueue judge event",
|
|
|
|
|
});
|
|
|
|
|
return Status.SE;
|
|
|
|
|
}
|
2025-06-13 06:03:17 +00:00
|
|
|
|
2026-05-13 07:10:06 +00:00
|
|
|
return Status.QD;
|
2025-06-13 06:03:17 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error in judge:", error);
|
|
|
|
|
return Status.SE;
|
|
|
|
|
} finally {
|
|
|
|
|
revalidatePath(`/problems/${problemId}`);
|
|
|
|
|
}
|
|
|
|
|
};
|