diff --git a/deno.lock b/deno.lock index 41a2387..600baf8 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "3", "redirects": { "https://deno.land/x/oak/mod.ts": "https://deno.land/x/oak@v12.6.1/mod.ts", + "https://denopkg.com/chiefbiiko/sha1/mod.ts": "https://denopkg.com/chiefbiiko/sha1@master/mod.ts", "https://unpkg.com/pure-rand/lib/esm/pure-rand.js": "https://unpkg.com/pure-rand@6.0.4/lib/esm/pure-rand.js" }, "remote": { @@ -144,6 +145,9 @@ "https://deno.land/std@0.99.0/path/posix.ts": "f56c3c99feb47f30a40ce9d252ef6f00296fa7c0fcb6dd81211bdb3b8b99ca3b", "https://deno.land/std@0.99.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", "https://deno.land/std@0.99.0/path/win32.ts": "77f7b3604e0de40f3a7c698e8a79e7f601dc187035a1c21cb1e596666ce112f8", + "https://deno.land/x/base64@v0.2.1/base.ts": "47dc8d68f07dc91524bdd6db36eccbe59cf4d935b5fc09f27357a3944bb3ff7b", + "https://deno.land/x/base64@v0.2.1/base64url.ts": "18bbf879b31f1f32cca8adaa2b6885ae325c2cec6a66c5817b684ca12c46ad5e", + "https://deno.land/x/base64@v0.2.1/mod.ts": "1cbdc4ba7229d3c6d1763fecdb9d46844777c7e153abb6dabea8b0dd01448db4", "https://deno.land/x/media_types@v2.7.1/db.ts": "1d95aa3fc1fe076638550640609394f0e157b18b430800e419aa7ece5c151d9c", "https://deno.land/x/media_types@v2.7.1/deps.ts": "cd4c31e4b5ed8c4d8c61bd2fb38a82d68d3fd2dcf6d43f22511cbe6d9d310e51", "https://deno.land/x/media_types@v2.7.1/mod.ts": "d63583b978d32eff8b76e1ae5d83cba2fb27baa90cc1bcb0ad15a06122ea8c19", @@ -178,6 +182,9 @@ "https://deno.land/x/static_files@1.1.6/src/deps.ts": "fb62be7a780339157bf692a619d6d02ea9662490deca275f488f9f9db917fc4a", "https://deno.land/x/static_files@1.1.6/src/types.ts": "ef201288626294e9e1b8ee85cdfe2cdfb282e4d78af445df1726050dfa0c0988", "https://deno.land/x/static_files@1.1.6/src/utils.ts": "9e6172dc97fed01e70a6b063c5afff40e20fe726b3bae0c5f0a962bad74b7107", + "https://denopkg.com/chiefbiiko/sha1@master/deps.ts": "434b70f37ed145ca5259353c96fdee1e7d1cd8c6a32baad9dce2aedc79d0a3bc", + "https://denopkg.com/chiefbiiko/sha1@master/mod.ts": "146a101c9776cc9c807053c93f23e4b321ade5251f65745df418f4a75d5fd27b", + "https://denopkg.com/chiefbiiko/std-encoding@v1.1.1/mod.ts": "e61017da4cef146863d614a961b4e61f1df14b72c906092bd02161f913e76522", "https://esm.sh/preact-render-to-string@5.1.19?deps=preact@10.5.15": "fba6961b9688151eeccc5425e05740009c3e7abee863615e626fb13e1d17e87a", "https://esm.sh/preact@10.19.2": "41f31ba8eecf666f08289f2205f57c45f74baba5be815a55c15c4cc61086143c", "https://esm.sh/preact@10.19.2/jsx-runtime": "2a4111d9aa667a3e3776ffd9082d00d63f6960c038d01620d79f80645ecc049d", diff --git a/src/questions.js b/src/questions.js index 8ceabb2..0d317ea 100644 --- a/src/questions.js +++ b/src/questions.js @@ -1,3 +1,6 @@ +import { sha1 } from "https://denopkg.com/chiefbiiko/sha1/mod.ts"; + + function basicFrenchQuestion(argumentValue){ return `Quel est la valeur retournée par la fonction si la valeur de l'argument x est ${argumentValue}?` } @@ -7,7 +10,11 @@ function basicQuestionValidator(expected){ } function basicQuestionMetaData(question){ - return (x) => ({ + const sourceHash = sha1(question.toString(), "utf8", "hex"); + return (x, idx) => ({ + sourceHash: sourceHash, + idx: idx, + id: `${sourceHash}-${idx}`, question_fr: basicFrenchQuestion(x), validate: basicQuestionValidator(question(x)), answer: question(x), diff --git a/src/server.tsx b/src/server.tsx index 79e6aee..0a6e565 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -65,6 +65,7 @@ function getQuestionPage(question: Question, urls: QuestionURLS){
+
@@ -144,14 +145,49 @@ function getUrls(ctx): QuestionURLS{ return {nextQuestionUrl, validationUrl, answerQuestionUrl}; } -apiRouter.post("/api/v1/random-questions/:seed/:idx/validate", async (ctx)=> { - const answer = (await ctx.request.body().value).get("answer"); +async function persistAnswer(ctx, form): Promise{ + const answer = form.get("answer"); + const rendedingTimeStamp = form.get("rendedingTimeStamp"); + const answeredTimestamp = Date.now(); + const kv = await Deno.openKv(); const q = getQuestion(ctx); + + const username = ctx.request.headers.get('session'); + const data = { + ip: ctx.request.ip, + url: ctx.request.url.toString(), + date: Date.now(), + domain: ctx.request.url.hostname, + hasRightResponse: isValidAnswer(q, answer), + answer, + rendedingTimeStamp, + answeredTimestamp + } + const baseKey = ["answer", q[1].id, username]; + for(let i = 0 ; i < 1000; i++){ + const key = baseKey.concat([i]); + const res = await kv.atomic() + .check({ key, versionstamp: null }) // `null` versionstamps mean 'no value' + .set(key, data) + .commit(); + if (res.ok) { + return + } + } +} + +apiRouter.post("/api/v1/random-questions/:seed/:idx/validate", async (ctx)=> { + const form = (await ctx.request.body().value); + const answer = form.get("answer"); + const q = getQuestion(ctx); + if(isValidAnswer(q, answer)){ processValidAnswer(ctx, getUrls(ctx).nextQuestionUrl) }else{ processInvalidAnswer(ctx, getUrls(ctx).nextQuestionUrl) } + await persistAnswer(ctx, form); + }) apiRouter.get("/api/v1/random-questions/:seed/:idx/answer", (ctx)=> { diff --git a/src/shared.ts b/src/shared.ts index 513480c..1d2e67b 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -5,5 +5,6 @@ interface QuestionSource { export type QuestionMeta = { question_fr: string, validate: (answer: string) => boolean, - answer: string | number + answer: string | number, + id: string, } \ No newline at end of file