diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..cb83045 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/supabase/functions/.env b/supabase/functions/.env new file mode 100644 index 0000000..db53efa --- /dev/null +++ b/supabase/functions/.env @@ -0,0 +1 @@ +JUDGE0_ENDPOINT=http://192.168.50.83:2358 \ No newline at end of file diff --git a/supabase/functions/fresh-challenges/index.ts b/supabase/functions/fresh-challenges/index.ts new file mode 100644 index 0000000..f9ef8b1 --- /dev/null +++ b/supabase/functions/fresh-challenges/index.ts @@ -0,0 +1,44 @@ +import { createClient } from "https://esm.sh/@supabase/supabase-js"; + +// http://127.0.0.1:54321/functions/v1/fresh-challenges +// Auto get judging status from remote lawyers + +Deno.serve(async (req) => { + try { + const client = createClient( + Deno.env.get("SUPABASE_URL") ?? "", + Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "" + ); + + const body = await req.json(); + + const { data } = await client + .from("challenges") + .select("*") + .eq("status", "judging") + .eq("id", body.id) + .single(); + + if (data == null || data?.details?.submissions == null) { + return new Response(String("Unable to find the challenge with the id in the request."), { status: 400 }); + } + + const tokens = data?.details?.submissions + .map((item: { token: string }) => item.token) + .join(","); + + const resp = await fetch( + Deno.env.get("JUDGE0_ENDPOINT") + `/submissions/batch?tokens=${tokens}`, + { method: "GET" } + ); + + const result = await resp.json(); + + return new Response(JSON.stringify({ result }), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); + } catch (err) { + return new Response(String(err?.message ?? err), { status: 500 }); + } +}); \ No newline at end of file diff --git a/supabase/functions/judge-challenges/index.ts b/supabase/functions/judge-challenges/index.ts index f1b224e..dd13803 100644 --- a/supabase/functions/judge-challenges/index.ts +++ b/supabase/functions/judge-challenges/index.ts @@ -1,39 +1,58 @@ import { createClient } from "https://esm.sh/@supabase/supabase-js"; +import { runChallenge } from "./runners/index.ts"; -Deno.serve(async (req) => { +// http://127.0.0.1:54321/functions/v1/judge-challenges +// Judge all status is submitted challenges + +Deno.serve(async (_) => { try { const client = createClient( - Deno.env.get('SUPABASE_URL') ?? '', - Deno.env.get('SUPABASE_ANON_KEY') ?? '', - { global: { headers: { Authorization: req.headers.get('Authorization')! } } } - ) + Deno.env.get("SUPABASE_URL") ?? "", + Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "" + ); const { data, error } = await client - .from('challenges') - .select('*') - .eq('status', 'submitted') + .from("challenges") + .select("*") + .eq("status", "submitted") + .limit(20); if (error) { - throw error + throw error; } - return new Response(JSON.stringify({ data }), { - headers: { 'Content-Type': 'application/json' }, - status: 200, - }) + let counter = 0; + for (const item of data) { + const { data: problem } = await client + .from("problems") + .select("*") + .eq("id", item.problem) + .single(); + + if (problem == null) { + throw new Error("Problem was not found."); + } + + const { data: cases } = await client + .from("problem_cases") + .select("*") + .eq("problem", problem.id); + + const result = await runChallenge(item, problem, cases); + + await client + .from("challenges") + .update({ status: result.status === "skipped" ? "finished" : "judging", details: result }) + .eq("id", item.id); + + counter++; + } + + return new Response(JSON.stringify({ judged: counter }), { + headers: { "Content-Type": "application/json" }, + status: 200 + }); } catch (err) { - return new Response(String(err?.message ?? err), { status: 500 }) + return new Response(String(err?.message ?? err), { status: 500 }); } }); - -/* To invoke locally: - - 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) - 2. Make an HTTP request: - - curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/judge-challenges' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ - --header 'Content-Type: application/json' \ - --data '{"name":"Functions"}' - -*/ diff --git a/supabase/functions/judge-challenges/runners/index.ts b/supabase/functions/judge-challenges/runners/index.ts new file mode 100644 index 0000000..b829904 --- /dev/null +++ b/supabase/functions/judge-challenges/runners/index.ts @@ -0,0 +1,10 @@ +import { runProgramChallenge } from "./programming.ts"; + +export async function runChallenge(challenge: any, problem: any, cases: any) { + switch (problem.type) { + case "programming": + return await runProgramChallenge(challenge, problem, cases) + default: + throw new Error("Unsupported problem type.") + } +} \ No newline at end of file diff --git a/supabase/functions/judge-challenges/runners/programming.ts b/supabase/functions/judge-challenges/runners/programming.ts new file mode 100644 index 0000000..158e866 --- /dev/null +++ b/supabase/functions/judge-challenges/runners/programming.ts @@ -0,0 +1,40 @@ +export async function runProgramChallenge(challenge: any, problem: any, cases: any[]) { + const languages: { [id: string]: number } = { + "cpp": 54 + }; + + const code = challenge.answers?.code; + const language = challenge.answers?.language; + if (!code || !language || !cases || Object.keys(languages).indexOf(language) < 0) { + return { + status: "skipped", + submissions: null + }; + } + + const idx = languages[language]; + const submissions = cases.map((item: any) => { + return { + "language_id": idx, + "source_code": challenge.answers?.code, + "expected_output": item?.stdout, + "stdin": code + }; + }); + + const resp = await fetch( + Deno.env.get("JUDGE0_ENDPOINT") + "/submissions/batch", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ submissions }) + } + ); + + const result = await resp.json(); + + return { + status: "judging", + submissions: result + }; +} \ No newline at end of file