2025-06-01 12:25:33 -04:00
|
|
|
(text "{% extends \"auth/base.html\" %} {% block head %}")
|
|
|
|
(title
|
|
|
|
(text "Register"))
|
|
|
|
|
|
|
|
(text "{% endblock %} {% block title %}Register{% endblock %} {% block content %}")
|
|
|
|
(script
|
|
|
|
("src" "https://challenges.cloudflare.com/turnstile/v0/api.js")
|
|
|
|
("defer" ""))
|
|
|
|
|
|
|
|
(form
|
|
|
|
("class" "w-full flex flex-col gap-4")
|
|
|
|
("onsubmit" "register(event)")
|
|
|
|
(div
|
|
|
|
("class" "flex flex-col gap-1")
|
|
|
|
(label
|
|
|
|
("for" "username")
|
|
|
|
(b
|
|
|
|
(text "Username")))
|
|
|
|
(input
|
|
|
|
("type" "text")
|
|
|
|
("placeholder" "username")
|
|
|
|
("required" "")
|
|
|
|
("name" "username")
|
|
|
|
("id" "username")))
|
|
|
|
(div
|
|
|
|
("class" "flex flex-col gap-1")
|
|
|
|
(label
|
2025-06-22 13:03:02 -04:00
|
|
|
("for" "password")
|
2025-06-01 12:25:33 -04:00
|
|
|
(b
|
|
|
|
(text "Password")))
|
|
|
|
(input
|
|
|
|
("type" "password")
|
|
|
|
("placeholder" "password")
|
|
|
|
("required" "")
|
|
|
|
("name" "password")
|
|
|
|
("id" "password")))
|
2025-06-22 13:03:02 -04:00
|
|
|
(text "{% if config.security.enable_invite_codes -%}")
|
|
|
|
(div
|
|
|
|
("class" "flex flex-col gap-1")
|
2025-07-03 21:56:21 -04:00
|
|
|
("oninput" "check_should_show_purchase(event)")
|
2025-06-22 13:03:02 -04:00
|
|
|
(label
|
|
|
|
("for" "invite_code")
|
|
|
|
(b
|
2025-07-03 21:56:21 -04:00
|
|
|
(text "Invite code (optional)")))
|
2025-06-22 13:03:02 -04:00
|
|
|
(input
|
|
|
|
("type" "text")
|
|
|
|
("placeholder" "invite code")
|
|
|
|
("name" "invite_code")
|
|
|
|
("id" "invite_code")))
|
2025-07-03 21:56:21 -04:00
|
|
|
|
|
|
|
(script
|
|
|
|
(text "function check_should_show_purchase(e) {
|
|
|
|
if (e.target.value.length > 0) {
|
|
|
|
document.querySelector('[ui_ident=purchase_account]').classList.add('hidden');
|
|
|
|
document.querySelector('[ui_ident=create_account]').classList.remove('hidden');
|
|
|
|
globalThis.DO_PURCHASE = false;
|
|
|
|
} else {
|
|
|
|
document.querySelector('[ui_ident=purchase_account]').classList.remove('hidden');
|
|
|
|
document.querySelector('[ui_ident=create_account]').classList.add('hidden');
|
|
|
|
globalThis.DO_PURCHASE = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
globalThis.DO_PURCHASE = true;"))
|
2025-06-22 13:03:02 -04:00
|
|
|
(text "{%- endif %}")
|
2025-06-01 12:25:33 -04:00
|
|
|
(hr)
|
|
|
|
(div
|
|
|
|
("class" "card-nest w-full")
|
|
|
|
(div
|
|
|
|
("class" "card small flex items-center gap-2")
|
|
|
|
(text "{{ icon \"scroll-text\" }}")
|
|
|
|
(b
|
|
|
|
(text "Policies")))
|
|
|
|
(div
|
|
|
|
("class" "card secondary flex flex-col gap-2")
|
|
|
|
(span
|
|
|
|
(text "By continuing, you agree to the following policies:"))
|
|
|
|
(ul
|
|
|
|
(li
|
|
|
|
(a
|
|
|
|
("href" "{{ config.policies.terms_of_service }}")
|
|
|
|
(text "Terms of service")))
|
|
|
|
(li
|
|
|
|
(a
|
|
|
|
("href" "{{ config.policies.privacy }}")
|
|
|
|
(text "Privacy policy"))))
|
|
|
|
(div
|
2025-06-23 22:31:14 -04:00
|
|
|
("class" "flex items-center gap-2")
|
2025-06-01 12:25:33 -04:00
|
|
|
(input
|
|
|
|
("type" "checkbox")
|
|
|
|
("name" "policy_consent")
|
|
|
|
("id" "policy_consent")
|
|
|
|
("class" "w-content")
|
|
|
|
("required" ""))
|
|
|
|
(label
|
|
|
|
("for" "policy_consent")
|
|
|
|
(text "I agree")))))
|
|
|
|
(div
|
|
|
|
("class" "cf-turnstile")
|
|
|
|
("data-sitekey" "{{ config.turnstile.site_key }}"))
|
|
|
|
(hr)
|
2025-07-03 21:56:21 -04:00
|
|
|
(text "{% if config.security.enable_invite_codes -%}")
|
|
|
|
(div
|
|
|
|
("class" "w-full flex gap-2 justify-between")
|
|
|
|
("ui_ident" "purchase_account")
|
|
|
|
|
|
|
|
(button
|
|
|
|
(icon (text "credit-card"))
|
|
|
|
(str (text "auth:action.purchase_account")))
|
|
|
|
|
|
|
|
(button
|
|
|
|
("class" "small square lowered")
|
|
|
|
("type" "button")
|
|
|
|
("onclick" "document.querySelector('[ui_ident=purchase_help]').classList.toggle('hidden')")
|
|
|
|
(icon (text "circle-question-mark"))))
|
|
|
|
|
|
|
|
(div
|
|
|
|
("class" "hidden lowered card w-full no_p_margin")
|
|
|
|
("ui_ident" "purchase_help")
|
|
|
|
(b (text "What does \"Purchase account\" mean?"))
|
|
|
|
(p (text "Your account will be created, but you cannot use it until you activate it for {{ config.stripe.supporter_price_text }}."))
|
|
|
|
(p (text "Alternatively, you can provide an invite code to create your account for free.")))
|
|
|
|
(text "{%- endif %}")
|
2025-06-01 12:25:33 -04:00
|
|
|
(button
|
2025-07-03 21:56:21 -04:00
|
|
|
("class" "{% if config.security.enable_invite_codes -%} hidden {%- endif %}")
|
|
|
|
("ui_ident" "create_account")
|
|
|
|
(icon (text "plus"))
|
|
|
|
(str (text "auth:action.create_account"))))
|
2025-06-01 12:25:33 -04:00
|
|
|
|
|
|
|
(script
|
|
|
|
(text "async function register(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
await trigger(\"atto::debounce\", [\"users::create\"]);
|
|
|
|
fetch(\"/api/v1/auth/register\", {
|
|
|
|
method: \"POST\",
|
|
|
|
headers: {
|
|
|
|
\"Content-Type\": \"application/json\",
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
username: e.target.username.value,
|
|
|
|
password: e.target.password.value,
|
|
|
|
policy_consent: e.target.policy_consent.checked,
|
|
|
|
captcha_response: e.target.querySelector(
|
|
|
|
\"[name=cf-turnstile-response]\",
|
|
|
|
).value,
|
2025-06-22 13:03:02 -04:00
|
|
|
invite_code: (e.target.invite_code || { value: \"\" }).value,
|
2025-07-03 21:56:21 -04:00
|
|
|
purchase: globalThis.DO_PURCHASE,
|
2025-06-01 12:25:33 -04:00
|
|
|
}),
|
|
|
|
})
|
|
|
|
.then((res) => res.json())
|
2025-06-25 23:15:24 -04:00
|
|
|
.then(async (res) => {
|
2025-06-01 12:25:33 -04:00
|
|
|
trigger(\"atto::toast\", [
|
|
|
|
res.ok ? \"success\" : \"error\",
|
|
|
|
res.message,
|
|
|
|
]);
|
|
|
|
|
|
|
|
if (res.ok) {
|
|
|
|
// update tokens
|
2025-06-25 23:15:24 -04:00
|
|
|
const new_tokens = (await ns(\"me\")).LOGIN_ACCOUNT_TOKENS;
|
2025-06-01 12:25:33 -04:00
|
|
|
new_tokens[e.target.username.value] = res.message;
|
|
|
|
trigger(\"me::set_login_account_tokens\", [new_tokens]);
|
|
|
|
|
|
|
|
// redirect
|
|
|
|
setTimeout(() => {
|
|
|
|
window.location.href = \"/\";
|
|
|
|
}, 150);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}"))
|
|
|
|
|
|
|
|
(text "{% endblock %} {% block footer %}")
|
|
|
|
(span
|
|
|
|
("class" "small w-full text-center")
|
|
|
|
(text "Or, ")
|
|
|
|
(a
|
|
|
|
("href" "/auth/login")
|
|
|
|
(text "login")))
|
|
|
|
|
|
|
|
(text "{% endblock %}")
|