Skip to content

Commit 1831eff

Browse files
committed
feat(web): port PoW challenge page from vanilla JS to Preact
Replace the imperative DOM manipulation in web/js/main.ts with a declarative Preact component (web/js/main.tsx). The project already uses Preact for the timed-delay challenge (lib/challenge/preact/), so this aligns the PoW challenge with the existing codebase direction. ## Approach Convert web/js/main.ts to a Preact TSX component. The worker orchestration layer (web/js/algorithms/fast.ts) stays untouched -- it is already cleanly separated and works via a Promise API. ## What changed web/js/main.ts -> web/js/main.tsx: - Phase-based state machine (loading -> computing -> reading/error) replaces scattered imperative DOM updates. - Worker lifecycle managed in useEffect; progress callback drives state setters for speed and progress percentage. - Speed updates remain throttled to 1 second intervals. - i18n functions (initTranslations, t(), loadTranslations) kept as module-level state -- no need for React context in a single- component app. - The <details> section stays in the templ file as server-rendered HTML; the Preact component tracks its toggle state via useRef. - Uses esbuild automatic JSX transform (--jsx=automatic --jsx-import-source=preact) instead of classic pragmas. web/build.sh: - Add js/**/*.tsx to the glob so esbuild bundles TSX files. - Pass --jsx=automatic --jsx-import-source=preact for .tsx files. web/tsconfig.json (new): - IDE-only config (noEmit) so TypeScript understands Preact JSX types for editor diagnostics and autocompletion. lib/challenge/proofofwork/proofofwork.templ: - Replace individual DOM elements (img#image, p#status, div#progress) with a <div id="app"> Preact mount point containing server-rendered fallback (pensive image + loading text). - Keep <details>, <noscript>, and <div id="testarea"> outside the Preact tree as server-rendered content. lib/anubis.go: - Add challenge method to the "new challenge issued" log line. docs/docs/CHANGELOG.md: - Add entry for the Preact rewrite. ## What stayed the same - web/js/algorithms/fast.ts -- untouched - web/js/algorithms/index.ts -- untouched - web/js/worker/sha256-*.ts -- untouched - Server-side Go code (proofofwork.go) -- untouched - JSON script data embedding -- untouched - Redirect URL construction -- same logic, same parameters - Progress bar CSS in web/index.templ -- untouched Signed-off-by: Xe Iaso <me@xeiaso.net> Assisted-by: Claude Opus 4.6 via Claude Code Signed-off-by: Xe Iaso <me@xeiaso.net>
1 parent e0ece7d commit 1831eff

8 files changed

Lines changed: 390 additions & 306 deletions

File tree

docs/docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- fix: prevent nil pointer panic in challenge validation when threshold rules match during PassChallenge (#1463)
1515
- Instruct reverse proxies to not cache error pages.
1616
- Fixed mixed tab/space indentation in Caddy documentation code block
17+
- Rewrite main proof of work challenge to use Preact instead of Vanilla.js ([#1149](https://github.com/TecharoHQ/anubis/issues/1149))
1718

1819
<!-- This changes the project to: -->
1920

lib/anubis.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (s *Server) issueChallenge(ctx context.Context, r *http.Request, lg *slog.L
141141
return nil, err
142142
}
143143

144-
lg.Info("new challenge issued", "challenge", id.String())
144+
lg.Info("new challenge issued", "challenge", id.String(), "method", chall.Method)
145145

146146
return &chall, err
147147
}

lib/challenge/proofofwork/proofofwork.templ

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77

88
templ page(localizer *localization.SimpleLocalizer) {
99
<div class="centered-div">
10-
<img id="image" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version }/>
1110
<img style="display:none;" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" + anubis.Version }/>
12-
<p id="status">{ localizer.T("loading") }</p>
13-
<script async type="module" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/main.mjs?cacheBuster=" + anubis.Version }></script>
14-
<div id="progress" role="progressbar" aria-labelledby="status">
15-
<div class="bar-inner"></div>
11+
<div id="app">
12+
<img style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version }/>
13+
<p id="status">{ localizer.T("loading") }</p>
1614
</div>
15+
<script async type="module" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/main.mjs?cacheBuster=" + anubis.Version }></script>
1716
<details>
1817
if anubis.UseSimplifiedExplanation {
1918
<p>

lib/challenge/proofofwork/proofofwork_templ.go

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/build.sh

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,22 @@ cp ../lib/localization/locales/*.json static/locales/
4141

4242
shopt -s nullglob globstar
4343

44-
for file in js/**/*.ts js/**/*.mjs; do
44+
for file in js/**/*.ts js/**/*.tsx js/**/*.mjs; do
4545
out="static/${file}"
46-
if [[ "$file" == *.ts ]]; then
46+
if [[ "$file" == *.tsx ]]; then
47+
out="static/${file%.tsx}.mjs"
48+
elif [[ "$file" == *.ts ]]; then
4749
out="static/${file%.ts}.mjs"
4850
fi
4951

5052
mkdir -p "$(dirname "$out")"
5153

52-
esbuild "$file" --sourcemap --bundle --minify --outfile="$out" --banner:js="$LICENSE"
54+
JSX_FLAGS=""
55+
if [[ "$file" == *.tsx ]]; then
56+
JSX_FLAGS="--jsx=automatic --jsx-import-source=preact"
57+
fi
58+
59+
esbuild "$file" --sourcemap --bundle --minify --outfile="$out" $JSX_FLAGS --banner:js="$LICENSE"
5360
gzip -f -k -n "$out"
5461
zstd -f -k --ultra -22 "$out"
5562
brotli -fZk "$out"

0 commit comments

Comments
 (0)