mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-09-09 12:50:42 -04:00
feat: add 'proof of React' challenge (#1038)
* feat: add 'proof of React' challenge Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenge/preact): use JSX fragments Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenge/preact): ensure that the client waits as long as it needs to Signed-off-by: Xe Iaso <me@xeiaso.net> * docs: fix spelling Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenges/xeact): add noscript warning Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenges/xeact): add default loading message Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenges/xeact): make a UI render without JS Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenges/xeact): use %s here, not %w Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(test/healthcheck): run asset build Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(challenge/preact): fix build in ci Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
This commit is contained in:
parent
00afa72c4b
commit
0e0847cbeb
1
.github/actions/spelling/allow.txt
vendored
1
.github/actions/spelling/allow.txt
vendored
@ -5,3 +5,4 @@ ubuntu
|
||||
workarounds
|
||||
rjack
|
||||
msgbox
|
||||
xeact
|
@ -211,6 +211,21 @@ thresholds:
|
||||
- weight >= 10
|
||||
- weight < 20
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/preact
|
||||
#
|
||||
# This challenge proves the client can run a webapp written with Preact.
|
||||
# The preact webapp simply loads, calculates the SHA-256 checksum of the
|
||||
# challenge data, and forwards that to the client.
|
||||
algorithm: preact
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
- name: mild-proof-of-work
|
||||
expression:
|
||||
all:
|
||||
- weight >= 20
|
||||
- weight < 30
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
algorithm: fast
|
||||
@ -218,7 +233,7 @@ thresholds:
|
||||
report_as: 2
|
||||
# For clients that are browser like and have gained many points from custom rules
|
||||
- name: extreme-suspicion
|
||||
expression: weight >= 20
|
||||
expression: weight >= 30
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
# https://anubis.techaro.lol/docs/admin/configuration/challenges/proof-of-work
|
||||
|
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
<!-- This changes the project to: -->
|
||||
|
||||
- Add a "proof of React" challenge to prove that the client is able to run a simple JSX app.
|
||||
- Added possibility to disable HTTP keep-alive to support backends not properly
|
||||
handling it
|
||||
- Added a missing link to the Caddy installation environment in the installation documentation.
|
||||
|
19
docs/docs/admin/configuration/challenges/preact.mdx
Normal file
19
docs/docs/admin/configuration/challenges/preact.mdx
Normal file
@ -0,0 +1,19 @@
|
||||
# Preact
|
||||
|
||||
The `preact` challenge sends the browser a simple challenge that makes it run very lightweight JavaScript that proves the client is able to execute client-side JavaScript. It uses [Preact](https://www.npmjs.com/package/preact) (a lightweight client side web framework in the vein of React) to do this.
|
||||
|
||||
To use it in your Anubis configuration:
|
||||
|
||||
```yaml
|
||||
# Generic catchall rule
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
Mozilla|Opera
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
difficulty: 1 # Number of seconds to wait before refreshing the page
|
||||
report_as: 4 # Unused by this challenge method
|
||||
algorithm: preact
|
||||
```
|
||||
|
||||
This is the default challenge method for most clients.
|
@ -36,6 +36,7 @@ import (
|
||||
|
||||
// challenge implementations
|
||||
_ "github.com/TecharoHQ/anubis/lib/challenge/metarefresh"
|
||||
_ "github.com/TecharoHQ/anubis/lib/challenge/preact"
|
||||
_ "github.com/TecharoHQ/anubis/lib/challenge/proofofwork"
|
||||
)
|
||||
|
||||
|
49
lib/challenge/preact/build.sh
Executable file
49
lib/challenge/preact/build.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
LICENSE='/*
|
||||
@licstart The following is the entire license notice for the
|
||||
JavaScript code in this page.
|
||||
|
||||
Copyright (c) 2025 Xe Iaso <xe.iaso@techaro.lol>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Includes code from https://www.npmjs.com/package/preact which is used under
|
||||
the terms of the MIT license.
|
||||
|
||||
Includes code from https://github.com/aws/aws-sdk-js-crypto-helpers which is
|
||||
used under the terms of the Apache 2 license.
|
||||
|
||||
@licend The above is the entire license notice
|
||||
for the JavaScript code in this page.
|
||||
*/'
|
||||
|
||||
mkdir -p static/js
|
||||
|
||||
for file in js/*.jsx; do
|
||||
filename="${file##*/}" # Extracts "app.jsx" from "./js/app.jsx"
|
||||
output="${filename%.jsx}.js" # Changes "app.jsx" to "app.js"
|
||||
echo $output
|
||||
|
||||
esbuild "${file}" --minify --bundle --outfile=static/"${output}" --banner:js="${LICENSE}"
|
||||
done
|
62
lib/challenge/preact/js/app.jsx
Normal file
62
lib/challenge/preact/js/app.jsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { render, h, Fragment } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { g, j, u, x } from "./xeact.js";
|
||||
import { Sha256 } from '@aws-crypto/sha256-js';
|
||||
|
||||
/** @jsx h */
|
||||
/** @jsxFrag Fragment */
|
||||
|
||||
function toHexString(arr) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
const App = () => {
|
||||
const [state, setState] = useState(null);
|
||||
const [imageURL, setImageURL] = useState(null);
|
||||
const [passed, setPassed] = useState(false);
|
||||
const [challenge, setChallenge] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setState(j("preact_info"));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setImageURL(state.pensive_url);
|
||||
const hash = new Sha256('');
|
||||
hash.update(state.challenge);
|
||||
setChallenge(toHexString(hash.digestSync()));
|
||||
}, [state]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setPassed(true);
|
||||
}, state.difficulty * 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [challenge]);
|
||||
|
||||
useEffect(() => {
|
||||
window.location.href = u(state.redir, {
|
||||
result: challenge,
|
||||
});
|
||||
}, [passed]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{imageURL !== null && (
|
||||
<img src={imageURL} style="width:100%;max-width:256px;" />
|
||||
)}
|
||||
{state !== null && (
|
||||
<>
|
||||
<p id="status">{state.loading_message}</p>
|
||||
<p>{state.connection_security_message}</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
x(g("app"));
|
||||
render(<App />, g("app"));
|
129
lib/challenge/preact/js/xeact.js
Normal file
129
lib/challenge/preact/js/xeact.js
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Creates a DOM element, assigns the properties of `data` to it, and appends all `children`.
|
||||
*
|
||||
* @type{function(string|Function, Object=, Node|Array.<Node|string>=)}
|
||||
*/
|
||||
const h = (name, data = {}, children = []) => {
|
||||
const result =
|
||||
typeof name == "function" ? name(data) : Object.assign(document.createElement(name), data);
|
||||
if (!Array.isArray(children)) {
|
||||
children = [children];
|
||||
}
|
||||
result.append(...children);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a text node.
|
||||
*
|
||||
* Equivalent to `document.createTextNode(text)`
|
||||
*
|
||||
* @type{function(string): Text}
|
||||
*/
|
||||
const t = (text) => document.createTextNode(text);
|
||||
|
||||
/**
|
||||
* Remove all child nodes from a DOM element.
|
||||
*
|
||||
* @type{function(Node)}
|
||||
*/
|
||||
const x = (elem) => {
|
||||
while (elem.lastChild) {
|
||||
elem.removeChild(elem.lastChild);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all elements with the given ID.
|
||||
*
|
||||
* Equivalent to `document.getElementById(name)`
|
||||
*
|
||||
* @type{function(string): HTMLElement}
|
||||
*/
|
||||
const g = (name) => document.getElementById(name);
|
||||
|
||||
/**
|
||||
* Get all elements with the given class name.
|
||||
*
|
||||
* Equivalent to `document.getElementsByClassName(name)`
|
||||
*
|
||||
* @type{function(string): HTMLCollectionOf.<Element>}
|
||||
*/
|
||||
const c = (name) => document.getElementsByClassName(name);
|
||||
|
||||
/** @type{function(string): HTMLCollectionOf.<Element>} */
|
||||
const n = (name) => document.getElementsByName(name);
|
||||
|
||||
/**
|
||||
* Get all elements matching the given HTML selector.
|
||||
*
|
||||
* Matches selectors with `document.querySelectorAll(selector)`
|
||||
*
|
||||
* @type{function(string): Array.<HTMLElement>}
|
||||
*/
|
||||
const s = (selector) => Array.from(document.querySelectorAll(selector));
|
||||
|
||||
/**
|
||||
* Generate a relative URL from `url`, appending all key-value pairs from `params` as URL-encoded parameters.
|
||||
*
|
||||
* @type{function(string=, Object=): string}
|
||||
*/
|
||||
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();
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes a callback to run when all DOM content is loaded.
|
||||
*
|
||||
* Equivalent to `window.addEventListener('DOMContentLoaded', callback)`
|
||||
*
|
||||
* @type{function(function())}
|
||||
*/
|
||||
const r = (callback) => window.addEventListener("DOMContentLoaded", callback);
|
||||
|
||||
/**
|
||||
* Allows a stateful value to be tracked by consumers.
|
||||
*
|
||||
* This is the Xeact version of the React useState hook.
|
||||
*
|
||||
* @type{function(any): [function(): any, function(any): void]}
|
||||
*/
|
||||
const useState = (value = undefined) => {
|
||||
return [
|
||||
() => value,
|
||||
(x) => {
|
||||
value = x;
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Debounce an action for up to ms milliseconds.
|
||||
*
|
||||
* @type{function(number): function(function(any): void)}
|
||||
*/
|
||||
const d = (ms) => {
|
||||
let debounceTimer = null;
|
||||
return (f) => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(f, ms);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the contents of a given HTML page element as JSON and
|
||||
* return the results.
|
||||
*
|
||||
* This is useful when using templ to pass complicated data from
|
||||
* the server to the client via HTML[1].
|
||||
*
|
||||
* [1]: https://templ.guide/syntax-and-usage/script-templates/#pass-server-side-data-to-the-client-in-a-html-attribute
|
||||
*/
|
||||
const j = (id) => JSON.parse(g(id).textContent);
|
||||
|
||||
export { h, t, x, g, j, c, n, u, s, r, useState, d };
|
74
lib/challenge/preact/preact.go
Normal file
74
lib/challenge/preact/preact.go
Normal file
@ -0,0 +1,74 @@
|
||||
package preact
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/internal"
|
||||
"github.com/TecharoHQ/anubis/lib/challenge"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
//go:generate ./build.sh
|
||||
//go:generate go tool github.com/a-h/templ/cmd/templ generate
|
||||
|
||||
//go:embed static/app.js
|
||||
var appJS []byte
|
||||
|
||||
func renderAppJS(ctx context.Context, out io.Writer) error {
|
||||
fmt.Fprint(out, `<script type="module">`)
|
||||
out.Write(appJS)
|
||||
fmt.Fprint(out, "</script>")
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
challenge.Register("preact", &impl{})
|
||||
}
|
||||
|
||||
type impl struct{}
|
||||
|
||||
func (i *impl) Setup(mux *http.ServeMux) {}
|
||||
|
||||
func (i *impl) Issue(r *http.Request, lg *slog.Logger, in *challenge.IssueInput) (templ.Component, error) {
|
||||
u, err := r.URL.Parse(anubis.BasePrefix + "/.within.website/x/cmd/anubis/api/pass-challenge")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't render page: %w", err)
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Set("redir", r.URL.String())
|
||||
q.Set("id", in.Challenge.ID)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
loc := localization.GetLocalizer(r)
|
||||
|
||||
result := page(u.String(), in.Challenge.RandomData, in.Rule.Challenge.Difficulty, loc)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (i *impl) Validate(r *http.Request, lg *slog.Logger, in *challenge.ValidateInput) error {
|
||||
wantTime := in.Challenge.IssuedAt.Add(time.Duration(in.Rule.Challenge.Difficulty) * 95 * time.Millisecond)
|
||||
|
||||
if time.Now().Before(wantTime) {
|
||||
return challenge.NewError("validate", "insufficent time", fmt.Errorf("%w: wanted user to wait until at least %s", challenge.ErrFailed, wantTime.Format(time.RFC3339)))
|
||||
}
|
||||
|
||||
got := r.FormValue("result")
|
||||
want := internal.SHA256sum(in.Challenge.RandomData)
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(want), []byte(got)) != 1 {
|
||||
return challenge.NewError("validate", "invalid response", fmt.Errorf("%w: wanted response %s but got %s", challenge.ErrFailed, want, got))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
28
lib/challenge/preact/preact.templ
Normal file
28
lib/challenge/preact/preact.templ
Normal file
@ -0,0 +1,28 @@
|
||||
package preact
|
||||
|
||||
import (
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
)
|
||||
|
||||
templ page(redir, challenge string, difficulty int, loc *localization.SimpleLocalizer) {
|
||||
<div class="centered-div">
|
||||
<div id="app">
|
||||
<img id="image" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version }/>
|
||||
<p id="status">{ loc.T("loading") }</p>
|
||||
<p>{ loc.T("connection_security") }</p>
|
||||
</div>
|
||||
@templ.JSONScript("preact_info", map[string]any{
|
||||
"redir": redir,
|
||||
"challenge": challenge,
|
||||
"difficulty": difficulty,
|
||||
"connection_security_message": loc.T("connection_security"),
|
||||
"loading_message": loc.T("loading"),
|
||||
"pensive_url": anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version,
|
||||
})
|
||||
@templ.ComponentFunc(renderAppJS)
|
||||
<noscript>
|
||||
{ loc.T("javascript_required") }
|
||||
</noscript>
|
||||
</div>
|
||||
}
|
116
lib/challenge/preact/preact_templ.go
generated
Normal file
116
lib/challenge/preact/preact_templ.go
generated
Normal file
@ -0,0 +1,116 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.924
|
||||
package preact
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
)
|
||||
|
||||
func page(redir, challenge string, difficulty int, loc *localization.SimpleLocalizer) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"centered-div\"><div id=\"app\"><img id=\"image\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `preact.templ`, Line: 11, Col: 166}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><p id=\"status\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(loc.T("loading"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `preact.templ`, Line: 12, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</p><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(loc.T("connection_security"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `preact.templ`, Line: 13, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.JSONScript("preact_info", map[string]any{
|
||||
"redir": redir,
|
||||
"challenge": challenge,
|
||||
"difficulty": difficulty,
|
||||
"connection_security_message": loc.T("connection_security"),
|
||||
"loading_message": loc.T("loading"),
|
||||
"pensive_url": anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version,
|
||||
}).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.ComponentFunc(renderAppJS).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<noscript>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(loc.T("javascript_required"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `preact.templ`, Line: 25, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</noscript></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
1
lib/challenge/preact/static/.gitignore
vendored
Normal file
1
lib/challenge/preact/static/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
app.js
|
13
package-lock.json
generated
13
package-lock.json
generated
@ -9,7 +9,8 @@
|
||||
"version": "1.22.0-pre1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^5.2.0"
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
"preact": "^10.27.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.1.1",
|
||||
@ -2326,6 +2327,16 @@
|
||||
"postcss": "^8.4.32"
|
||||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.27.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.27.1.tgz",
|
||||
"integrity": "sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-hrtime": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
|
||||
|
@ -28,6 +28,7 @@
|
||||
"postcss-url": "^10.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^5.2.0"
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
"preact": "^10.27.1"
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
npm ci && \
|
||||
npm run assets \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,8 @@ set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
npm ci && \
|
||||
npm run assets \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,8 @@ set -u
|
||||
|
||||
(
|
||||
cd ../.. && \
|
||||
npm ci && \
|
||||
npm run assets \
|
||||
ko build --platform=all --base-import-paths --tags="latest" --image-user=1000 --image-annotation="" --image-label="" ./cmd/anubis -L
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user