From 58811194f0bced9b578937cdeb36d00243ac7df1 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Sat, 25 Jan 2025 15:32:51 -0500 Subject: cmd/anubis: minify JS, add video element test Signed-off-by: Xe Iaso --- cmd/anubis/index.templ | 86 ++++++++++++++++----------------- cmd/anubis/index_templ.go | 18 +++---- cmd/anubis/js/main.mjs | 70 +++++++++++++++++++++++++++ cmd/anubis/js/proof-of-work.mjs | 62 ++++++++++++++++++++++++ cmd/anubis/js/video.mjs | 16 ++++++ cmd/anubis/main.go | 12 +++-- cmd/anubis/static/js/main.mjs | 51 +------------------ cmd/anubis/static/js/proof-of-work.mjs | 62 ------------------------ cmd/anubis/static/testdata/black.mp4 | Bin 0 -> 1667 bytes 9 files changed, 207 insertions(+), 170 deletions(-) create mode 100644 cmd/anubis/js/main.mjs create mode 100644 cmd/anubis/js/proof-of-work.mjs create mode 100644 cmd/anubis/js/video.mjs delete mode 100644 cmd/anubis/static/js/proof-of-work.mjs create mode 100644 cmd/anubis/static/testdata/black.mp4 (limited to 'cmd') diff --git a/cmd/anubis/index.templ b/cmd/anubis/index.templ index e0530d6..189cf8b 100644 --- a/cmd/anubis/index.templ +++ b/cmd/anubis/index.templ @@ -1,19 +1,18 @@ package main import ( - "within.website/x" - "within.website/x/xess" + "within.website/x" + "within.website/x/xess" ) templ base(title string, body templ.Component) { - - - - - { title } - - - - - - -
-
-

{ title }

-
- @body - -
- - - + + +
+
+

{ title }

+
+ @body + +
+ + } templ index() { -
- - -

Loading...

- -
- -
+
+ Loading...

+
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -185,7 +185,7 @@ func errorPage(message string) templ.Component { var templ_7745c5c3_Var10 string templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/sad.webp?cacheBuster=" + x.Version) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 156, Col: 113} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 154, Col: 112} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) if templ_7745c5c3_Err != nil { @@ -198,7 +198,7 @@ func errorPage(message string) templ.Component { var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(message) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 157, Col: 16} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 155, Col: 14} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { diff --git a/cmd/anubis/js/main.mjs b/cmd/anubis/js/main.mjs new file mode 100644 index 0000000..38002d9 --- /dev/null +++ b/cmd/anubis/js/main.mjs @@ -0,0 +1,70 @@ +import { process } from './proof-of-work.mjs'; +import { testVideo } from './video.mjs'; + +// from Xeact +const u = (url = "", params = {}) => { + let result = new URL(url, window.location.href); + Object.entries(params).forEach((kv) => { + let [k, v] = kv; + result.searchParams.set(k, v); + }); + return result.toString(); +}; + +const imageURL = (mood) => { + return `/.within.website/x/cmd/anubis/static/img/${mood}.webp`; +}; + +(async () => { + const status = document.getElementById('status'); + const image = document.getElementById('image'); + const title = document.getElementById('title'); + const spinner = document.getElementById('spinner'); + const testarea = document.getElementById('testarea'); + + const videoWorks = await testVideo(testarea); + + if (!videoWorks) { + title.innerHTML = "Oh no!"; + status.innerHTML = "Checks failed. Please check your browser's settings and try again."; + image.src = imageURL("sad"); + spinner.innerHTML = ""; + spinner.style.display = "none"; + return; + } + + status.innerHTML = 'Calculating...'; + + const { challenge, difficulty } = await fetch("/.within.website/x/cmd/anubis/api/make-challenge", { method: "POST" }) + .then(r => { + if (!r.ok) { + throw new Error("Failed to fetch config"); + } + return r.json(); + }) + .catch(err => { + title.innerHTML = "Oh no!"; + status.innerHTML = `Failed to fetch config: ${err.message}`; + image.src = imageURL("sad"); + spinner.innerHTML = ""; + spinner.style.display = "none"; + throw err; + }); + + status.innerHTML = `Calculating...
Difficulty: ${difficulty}`; + + const t0 = Date.now(); + const { hash, nonce } = await process(challenge, difficulty); + const t1 = Date.now(); + + title.innerHTML = "Success!"; + status.innerHTML = `Done! Took ${t1 - t0}ms, ${nonce} iterations`; + image.src = imageURL("happy"); + spinner.innerHTML = ""; + spinner.style.display = "none"; + + setTimeout(() => { + const redir = window.location.href; + window.location.href = u("/.within.website/x/cmd/anubis/api/pass-challenge", { response: hash, nonce, redir, elapsedTime: t1 - t0, difficulty }); + }, 2000); +})(); \ No newline at end of file diff --git a/cmd/anubis/js/proof-of-work.mjs b/cmd/anubis/js/proof-of-work.mjs new file mode 100644 index 0000000..d71d2db --- /dev/null +++ b/cmd/anubis/js/proof-of-work.mjs @@ -0,0 +1,62 @@ +// https://dev.to/ratmd/simple-proof-of-work-in-javascript-3kgm + +export function process(data, difficulty = 5) { + return new Promise((resolve, reject) => { + let webWorkerURL = URL.createObjectURL(new Blob([ + '(', processTask(), ')()' + ], { type: 'application/javascript' })); + + let worker = new Worker(webWorkerURL); + + worker.onmessage = (event) => { + worker.terminate(); + resolve(event.data); + }; + + worker.onerror = (event) => { + worker.terminate(); + reject(); + }; + + worker.postMessage({ + data, + difficulty + }); + + URL.revokeObjectURL(webWorkerURL); + }); +} + +function processTask() { + return function () { + const sha256 = (text) => { + const encoded = new TextEncoder().encode(text); + return crypto.subtle.digest("SHA-256", encoded.buffer).then((result) => + Array.from(new Uint8Array(result)) + .map((c) => c.toString(16).padStart(2, "0")) + .join(""), + ); + }; + + addEventListener('message', async (event) => { + let data = event.data.data; + let difficulty = event.data.difficulty; + + let hash; + let nonce = 0; + do { + hash = await sha256(data + nonce++); + } while (hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')); + + nonce -= 1; // last nonce was post-incremented + + postMessage({ + hash, + data, + difficulty, + nonce, + }); + }); + }.toString(); +} + diff --git a/cmd/anubis/js/video.mjs b/cmd/anubis/js/video.mjs new file mode 100644 index 0000000..59cde1e --- /dev/null +++ b/cmd/anubis/js/video.mjs @@ -0,0 +1,16 @@ +const videoElement = ``; + +export const testVideo = async (testarea) => { + testarea.innerHTML = videoElement; + return (await new Promise((resolve) => { + const video = document.getElementById('videotest'); + video.oncanplay = () => { + testarea.style.display = "none"; + resolve(true); + }; + video.onerror = (ev) => { + testarea.style.display = "none"; + resolve(false); + }; + })); +}; \ No newline at end of file diff --git a/cmd/anubis/main.go b/cmd/anubis/main.go index 43d9ab0..6217b48 100644 --- a/cmd/anubis/main.go +++ b/cmd/anubis/main.go @@ -73,6 +73,7 @@ const ( ) //go:generate go run github.com/a-h/templ/cmd/templ@latest generate +//go:generate esbuild js/main.mjs --minify --bundle --outfile=static/js/main.mjs func main() { internal.HandleStartup() @@ -445,7 +446,7 @@ func (s *Server) passChallenge(w http.ResponseWriter, r *http.Request) { Name: cookieName, Value: tokenString, Expires: time.Now().Add(24 * 7 * time.Hour), - SameSite: http.SameSiteDefaultMode, + SameSite: http.SameSiteLaxMode, Path: "/", }) @@ -461,10 +462,11 @@ func (s *Server) testError(w http.ResponseWriter, r *http.Request) { func clearCookie(w http.ResponseWriter) { http.SetCookie(w, &http.Cookie{ - Name: cookieName, - Value: "", - Expires: time.Now().Add(-1 * time.Hour), - MaxAge: -1, + Name: cookieName, + Value: "", + Expires: time.Now().Add(-1 * time.Hour), + MaxAge: -1, + SameSite: http.SameSiteLaxMode, }) } diff --git a/cmd/anubis/static/js/main.mjs b/cmd/anubis/static/js/main.mjs index f111bb4..4d8c60c 100644 --- a/cmd/anubis/static/js/main.mjs +++ b/cmd/anubis/static/js/main.mjs @@ -1,50 +1 @@ -import { process } from './proof-of-work.mjs'; - -// from Xeact -const u = (url = "", params = {}) => { - let result = new URL(url, window.location.href); - Object.entries(params).forEach((kv) => { - let [k, v] = kv; - result.searchParams.set(k, v); - }); - return result.toString(); -}; - -(async () => { - const status = document.getElementById('status'); - const image = document.getElementById('image'); - const title = document.getElementById('title'); - const spinner = document.getElementById('spinner'); - - status.innerHTML = 'Calculating...'; - - const { challenge, difficulty } = await fetch("/.within.website/x/cmd/anubis/api/make-challenge", { method: "POST" }) - .then(r => { - if (!r.ok) { - throw new Error("Failed to fetch config"); - } - return r.json(); - }) - .catch(err => { - status.innerHTML = `Failed to fetch config: ${err.message}`; - image.src = "/.within.website/x/cmd/anubis/static/img/sad.webp"; - throw err; - }); - - status.innerHTML = `Calculating...
Difficulty: ${difficulty}`; - - const t0 = Date.now(); - const { hash, nonce } = await process(challenge, difficulty); - const t1 = Date.now(); - - title.innerHTML = "Success!"; - status.innerHTML = `Done! Took ${t1 - t0}ms, ${nonce} iterations`; - image.src = "/.within.website/x/cmd/anubis/static/img/happy.webp"; - spinner.innerHTML = ""; - spinner.style.display = "none"; - - setTimeout(() => { - const redir = window.location.href; - window.location.href = u("/.within.website/x/cmd/anubis/api/pass-challenge", { response: hash, nonce, redir, elapsedTime: t1 - t0, difficulty }); - }, 2000); -})(); \ No newline at end of file +(()=>{function w(e,i=5){return new Promise((n,t)=>{let o=URL.createObjectURL(new Blob(["(",y(),")()"],{type:"application/javascript"})),s=new Worker(o);s.onmessage=r=>{s.terminate(),n(r.data)},s.onerror=r=>{s.terminate(),t()},s.postMessage({data:e,difficulty:i}),URL.revokeObjectURL(o)})}function y(){return function(){let e=i=>{let n=new TextEncoder().encode(i);return crypto.subtle.digest("SHA-256",n.buffer).then(t=>Array.from(new Uint8Array(t)).map(o=>o.toString(16).padStart(2,"0")).join(""))};addEventListener("message",async i=>{let n=i.data.data,t=i.data.difficulty,o,s=0;do o=await e(n+s++);while(o.substring(0,t)!==Array(t+1).join("0"));s-=1,postMessage({hash:o,data:n,difficulty:t,nonce:s})})}.toString()}var g='',h=async e=>(e.innerHTML=g,await new Promise(i=>{let n=document.getElementById("videotest");n.oncanplay=()=>{e.style.display="none",i(!0)},n.onerror=t=>{e.style.display="none",i(!1)}}));var f=(e="",i={})=>{let n=new URL(e,window.location.href);return Object.entries(i).forEach(t=>{let[o,s]=t;n.searchParams.set(o,s)}),n.toString()},d=e=>`/.within.website/x/cmd/anubis/static/img/${e}.webp`;(async()=>{let e=document.getElementById("status"),i=document.getElementById("image"),n=document.getElementById("title"),t=document.getElementById("spinner"),o=document.getElementById("testarea");if(!await h(o)){n.innerHTML="Oh no!",e.innerHTML="Checks failed. Please check your browser's settings and try again.",i.src=d("sad"),t.innerHTML="",t.style.display="none";return}e.innerHTML="Calculating...";let{challenge:r,difficulty:c}=await fetch("/.within.website/x/cmd/anubis/api/make-challenge",{method:"POST"}).then(a=>{if(!a.ok)throw new Error("Failed to fetch config");return a.json()}).catch(a=>{throw n.innerHTML="Oh no!",e.innerHTML=`Failed to fetch config: ${a.message}`,i.src=d("sad"),t.innerHTML="",t.style.display="none",a});e.innerHTML=`Calculating...
Difficulty: ${c}`;let l=Date.now(),{hash:p,nonce:m}=await w(r,c),u=Date.now();n.innerHTML="Success!",e.innerHTML=`Done! Took ${u-l}ms, ${m} iterations`,i.src=d("happy"),t.innerHTML="",t.style.display="none",setTimeout(()=>{let a=window.location.href;window.location.href=f("/.within.website/x/cmd/anubis/api/pass-challenge",{response:p,nonce:m,redir:a,elapsedTime:u-l,difficulty:c})},2e3)})();})(); diff --git a/cmd/anubis/static/js/proof-of-work.mjs b/cmd/anubis/static/js/proof-of-work.mjs deleted file mode 100644 index d71d2db..0000000 --- a/cmd/anubis/static/js/proof-of-work.mjs +++ /dev/null @@ -1,62 +0,0 @@ -// https://dev.to/ratmd/simple-proof-of-work-in-javascript-3kgm - -export function process(data, difficulty = 5) { - return new Promise((resolve, reject) => { - let webWorkerURL = URL.createObjectURL(new Blob([ - '(', processTask(), ')()' - ], { type: 'application/javascript' })); - - let worker = new Worker(webWorkerURL); - - worker.onmessage = (event) => { - worker.terminate(); - resolve(event.data); - }; - - worker.onerror = (event) => { - worker.terminate(); - reject(); - }; - - worker.postMessage({ - data, - difficulty - }); - - URL.revokeObjectURL(webWorkerURL); - }); -} - -function processTask() { - return function () { - const sha256 = (text) => { - const encoded = new TextEncoder().encode(text); - return crypto.subtle.digest("SHA-256", encoded.buffer).then((result) => - Array.from(new Uint8Array(result)) - .map((c) => c.toString(16).padStart(2, "0")) - .join(""), - ); - }; - - addEventListener('message', async (event) => { - let data = event.data.data; - let difficulty = event.data.difficulty; - - let hash; - let nonce = 0; - do { - hash = await sha256(data + nonce++); - } while (hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')); - - nonce -= 1; // last nonce was post-incremented - - postMessage({ - hash, - data, - difficulty, - nonce, - }); - }); - }.toString(); -} - diff --git a/cmd/anubis/static/testdata/black.mp4 b/cmd/anubis/static/testdata/black.mp4 new file mode 100644 index 0000000..83a9989 Binary files /dev/null and b/cmd/anubis/static/testdata/black.mp4 differ -- cgit v1.2.3