diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json index 7ab00696..aed2c386 100644 --- a/frontend/ui/package-lock.json +++ b/frontend/ui/package-lock.json @@ -707,6 +707,11 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "hash-wasm": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.9.0.tgz", + "integrity": "sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==" + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", diff --git a/frontend/ui/package.json b/frontend/ui/package.json index 0e8b6d29..019c9eb3 100644 --- a/frontend/ui/package.json +++ b/frontend/ui/package.json @@ -27,6 +27,7 @@ "bootstrap": "^5.1.0", "bootstrap-icons": "^1.5.0", "bootswatch": "^5.1.0", + "hash-wasm": "^4.9.0", "seedrandom": "^3.0.5" } } diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index 1c5063c9..4ae8026d 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -12,6 +12,10 @@ Spinner, } from 'sveltestrap'; + import { blake2b } from 'hash-wasm'; + + import { my } from '../stores/my.js'; + import DateFormat from './DateFormat.svelte'; import FlagKey from './FlagKey.svelte'; import FlagMCQ from './FlagMCQ.svelte'; @@ -36,42 +40,89 @@ }) } + export let forcesolved = false; let responses = { }; async function submitFlags() { submitInProgress = true; sberr = ""; message = ""; - const response = await fetch( - "submit/" + exercice.id, - { - method: "POST", - headers: {'Accept': 'application/json'}, - body: JSON.stringify(responses), - } - ) + if ($my && $my.team_id === 0) { + let allGoodResponse = true; + for (const f in flags) { + const flag = flags[f]; + + let soluce = ""; + if (flag.type == "mcq") { + for (const c in flag.choices) { + soluce += responses.mcqs[c] ? "t" : "f"; + } + } else { + soluce = responses.flags[flag.id]; + } + + if (flag.ignore_case) { + soluce = soluce.toLowerCase(); + } + + if (flag.validator_regexp) { + let re = new RegExp(flag.validator_regexp, flag.ignore_case?'i':''); + soluce = soluce.match(re).slice(1).join("+"); + } + + if (await blake2b(soluce) == flag.soluce) { + flags[f].found = new Date(); + } else if (!flag.found) { + allGoodResponse = false; + } + flags = flags; + } + + if (allGoodResponse) { + forcesolved = true; + } + + if (exercice.tries) { + exercice.tries += 1; + } else { + exercice.tries = 1; + } + exercice.solved_time = new Date(); + exercice = exercice; - if (response.status < 300) { - const data = await response.json(); - messageClass = 'text-success'; - message = data.errmsg; - waitDiff(20); - } else { submitInProgress = false; + } else { + const response = await fetch( + "submit/" + exercice.id, + { + method: "POST", + headers: {'Accept': 'application/json'}, + body: JSON.stringify(responses), + } + ) - messageClass = 'text-danger'; - - let data = ""; - try { - data = await response.json(); - } catch(e) { - data = null; - } - - if (data && data.errmsg) + if (response.status < 300) { + const data = await response.json(); + messageClass = 'text-success'; message = data.errmsg; - if (response.statys != 402) - sberr = "Oups !"; + waitDiff(20); + } else { + submitInProgress = false; + + messageClass = 'text-danger'; + + let data = ""; + try { + data = await response.json(); + } catch(e) { + data = null; + } + + if (data && data.errmsg) + message = data.errmsg; + if (response.statys != 402) + sberr = "Oups !"; + } } } diff --git a/frontend/ui/src/components/ExerciceHints.svelte b/frontend/ui/src/components/ExerciceHints.svelte index e8a8063d..70b1e0cb 100644 --- a/frontend/ui/src/components/ExerciceHints.svelte +++ b/frontend/ui/src/components/ExerciceHints.svelte @@ -39,6 +39,11 @@ }) } + function showHint(hint) { + hint.hidden = false; + hints = hints; // Force Svelte update + } + async function openHint(hint) { hints_submitted[hint.id] = true; hinterror = ""; @@ -122,7 +127,7 @@ {/if} {#if !hint.file && hint.hidden} - diff --git a/frontend/ui/src/routes/[theme]/[exercice].svelte b/frontend/ui/src/routes/[theme]/[exercice].svelte index 41452c6b..7b8a0cb6 100644 --- a/frontend/ui/src/routes/[theme]/[exercice].svelte +++ b/frontend/ui/src/routes/[theme]/[exercice].svelte @@ -47,6 +47,7 @@ export let theme; export let exercice; + let solved = {}; export let refresh_my; export let refresh_teams; @@ -158,11 +159,12 @@ {/if} - {#if !$my.exercices[exercice.id].solved_rank} + {#if !$my.exercices[exercice.id].solved_rank && !solved[exercice.id]} {:else} diff --git a/frontend/ui/src/routes/__layout.svelte b/frontend/ui/src/routes/__layout.svelte index 438dead2..56aa0ec4 100644 --- a/frontend/ui/src/routes/__layout.svelte +++ b/frontend/ui/src/routes/__layout.svelte @@ -5,6 +5,8 @@ import { themesStore } from '../stores/themes.js'; import { settings, time } from '../stores/settings.js'; + let stop_refresh = false; + let refresh_interval_settings = null; async function refresh_settings(cb=null, interval=null) { if (refresh_interval_settings) @@ -12,6 +14,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_settings = setInterval(refresh_settings, interval); if (!cb) { @@ -43,6 +48,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_teams = setInterval(refresh_teams, interval); teamsStore.update(await fetch('teams.json', {headers: {'Accept': 'application/json'}}), cb); @@ -55,6 +63,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_themes = setInterval(refresh_themes, interval); await themesStore.update(await fetch('themes.json', {headers: {'Accept': 'application/json'}}), cb); @@ -67,6 +78,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 24000; } + if (stop_refresh) { + return; + } refresh_interval_my = setInterval(refresh_my, interval); my.update(await fetch('my.json', {headers: {'Accept': 'application/json'}}), cb); @@ -79,6 +93,9 @@ if (interval === null) { interval = Math.floor(Math.random() * 24000) + 32000; } + if (stop_refresh) { + return; + } refresh_interval_issues = setInterval(refresh_issues, interval); issuesStore.update(await fetch('issues.json', {headers: {'Accept': 'application/json'}}), cb); @@ -88,7 +105,11 @@ await refresh_settings(); await refresh_themes(); refresh_teams(); - refresh_my(); + refresh_my((my) => { + if (my && my.team_id === 0) { + stop_refresh = true; + } + }); refresh_issues(); return { diff --git a/frontend/ui/src/stores/my.js b/frontend/ui/src/stores/my.js index 72f4be00..7500cd52 100644 --- a/frontend/ui/src/stores/my.js +++ b/frontend/ui/src/stores/my.js @@ -10,6 +10,12 @@ function createMyStore() { res_my.json().then((my) => { for (let k in my.exercices) { my.exercices[k].id = k; + + if (my.team_id === 0 && my.exercices[k].hints) { + for (let j in my.exercices[k].hints) { + my.exercices[k].hints[j].hidden = true; + } + } } update((m) => (Object.assign(m?m:{}, my)));