From 52309a8e21dc4a6af8c9d15877125f58700a4b34 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Fri, 16 Aug 2024 21:51:09 +0200 Subject: [PATCH] move css to build step, add challenge url --- package.json | 1 + public/challenge.json | 5 +++ public/index.html | 80 ++---------------------------------------- src/challenge.ts | 10 ++++++ src/default-styles.css | 70 ++++++++++++++++++++++++++++++++++++ src/hash.ts | 2 +- src/main.ts | 77 +++++++++++++++++++++++++++------------- 7 files changed, 142 insertions(+), 103 deletions(-) create mode 100644 public/challenge.json create mode 100644 src/challenge.ts create mode 100644 src/default-styles.css diff --git a/package.json b/package.json index 69cc385..9461308 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "src/main.ts", "scripts": { "build": "esbuild src/main.ts --bundle --outfile=public/bundle.js", + "build-mini": "esbuild src/main.ts --minify --bundle --outfile=public/bundle.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/public/challenge.json b/public/challenge.json new file mode 100644 index 0000000..d66771e --- /dev/null +++ b/public/challenge.json @@ -0,0 +1,5 @@ +{ + "algo": "SHA-256", + "prefixBits": "15", + "input": "challenge:" +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index 861ac9f..92d9349 100644 --- a/public/index.html +++ b/public/index.html @@ -2,85 +2,9 @@ - - + -
+
diff --git a/src/challenge.ts b/src/challenge.ts new file mode 100644 index 0000000..b9045cd --- /dev/null +++ b/src/challenge.ts @@ -0,0 +1,10 @@ + +export type Challenge = { + algo: string, + prefixBits: number, + input: string, +} + +export function validateChallenge(challenge: Challenge): boolean { + return !!challenge.algo && challenge.prefixBits > 0 && !!challenge.input; +} \ No newline at end of file diff --git a/src/default-styles.css b/src/default-styles.css new file mode 100644 index 0000000..afa7db5 --- /dev/null +++ b/src/default-styles.css @@ -0,0 +1,70 @@ +.captcha { + width: 300px; + height: 70px; + padding: 19px; + border: 1px solid grey; + box-sizing: border-box; + font-size: 20px; + font-family: sans-serif; +} + +.captcha span { + position: relative; + top: -9px; +} + +.captcha .checkbox { + position: relative; + margin: 0 20px 0 0; + height: 30px; + width: 30px; + display: inline-block; + background-color: white; + cursor: pointer; + border: 1px solid black; + border-radius: 3px; +} + +.captcha .checkbox:hover { + background-color: lightgrey; +} + +.captcha .checkbox.checked { + background-color: #2196F3; +} + +.captcha .checkbox.checked::before { + position: absolute; + display: block; + content: ""; + width: 7px; + height: 15px; + top: 2px; + left: 9px; + border: white solid; + border-width: 0 5px 5px 0; + transform: rotate(45deg); +} + +.captcha .checkbox.loading { +} + +.captcha .checkbox.loading::before { + content: ""; + display: block; + position: absolute; + top: 3px; + left: 3px; + width: 17px; + height: 17px; + border: 4px solid; + border-radius: 50%; + border-color: black transparent black transparent; + animation: captchaLoading infinite 1s; +} + +@keyframes captchaLoading { + to { + transform: rotate(.5turn) + } +} \ No newline at end of file diff --git a/src/hash.ts b/src/hash.ts index 94d62ed..65c347c 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -29,4 +29,4 @@ Uint8Array.prototype.hasPrefix = function(prefix: Uint8Array, bits: number): boo export async function digest(algo: AlgorithmIdentifier, input: string): Promise { return new Uint8Array(await crypto.subtle.digest(algo, new TextEncoder().encode(input))); -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index dbddc43..5903a07 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,9 @@ import {makeSuffix} from "./content"; import {digest, sha256} from "./hash"; +import "./default-styles.css"; +import {Challenge, validateChallenge} from "./challenge"; + async function findHashWithPrefix(hashPrefixBits: number, inputPrefix: string): Promise { const hashPrefix = new Uint8Array(Array(Math.ceil(hashPrefixBits / 8)).map(_ => 0)); let iteration = 0; @@ -13,30 +16,56 @@ async function findHashWithPrefix(hashPrefixBits: number, inputPrefix: string): return message; } +function initCaptchaContentAndGetCheckbox(captcha: Element): Element { + const checkbox = document.createElement("div"); + checkbox.classList.add("checkbox"); + captcha.append(checkbox); + + const text = document.createElement("span"); + text.innerText = "I am not a robot"; + captcha.append(text); + + return checkbox; +} + +function prepareChallengeExecution(challenge: Challenge): () => Promise { + return async function() { + console.log("Calculating..."); + + this.classList.add("loading"); + + const response = await findHashWithPrefix(challenge.prefixBits, challenge.input); + console.log("Challenge Response: " + response); + + this.classList.remove("loading"); + this.classList.add("checked"); + } +} + +async function prepareCaptcha(captcha: Element) { + const challengeUrl = captcha.getAttribute("data-challenge-url"); + if (!challengeUrl) { + console.warn("No challenge URL found."); + return; + } + + const checkbox = initCaptchaContentAndGetCheckbox(captcha); + checkbox.classList.add("loading"); + + const challengeResponse = await fetch(challengeUrl); + const challenge = await challengeResponse.json() as Challenge; + + if (!validateChallenge(challenge)) { + console.warn("Challenge is invalid."); + return; + } + + checkbox.classList.remove("loading"); + + checkbox.addEventListener("click", prepareChallengeExecution(challenge)); +} + window.addEventListener("load", () => { console.log("load"); - [...document.getElementsByClassName("captcha")].forEach(captcha => { - console.dir(captcha); - const checkbox = document.createElement("div"); - checkbox.classList.add("checkbox"); - captcha.append(checkbox); - - const text = document.createElement("span"); - text.innerText = "I am not a robot"; - captcha.append(text); - - checkbox.addEventListener("click", async function() { - console.log("Calculating..."); - - this.classList.add("loading"); - - const challenge = makeSuffix(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) + ":" + makeSuffix(new Date().valueOf()) + ":"; - - const response = await findHashWithPrefix(15, challenge); - console.log("Challenge Response: " + response); - - this.classList.remove("loading"); - this.classList.add("checked"); - }) - }); + [...document.getElementsByClassName("captcha")].forEach(prepareCaptcha); });