(text "{% extends \"root.lisp\" %} {% block head %}") (title (text "Login — {{ name }}")) (text "{% endblock %} {% block body %}") (div ("class" "card") (h4 (text "Login with Tetratto")) (form ("class" "flex flex_col gap_4") ("onsubmit" "login(event)") (div ("id" "flow_1") ("style" "display: contents") (div ("class" "flex flex_col gap_1") (label ("for" "username") (b (text "Username"))) (input ("class" "surface") ("type" "text") ("placeholder" "username") ("required" "") ("name" "username") ("id" "username"))) (div ("class" "flex flex_col gap_1") (label ("for" "username") (b (text "Password"))) (input ("class" "surface") ("type" "password") ("placeholder" "password") ("required" "") ("name" "password") ("id" "password")))) (div ("id" "flow_2") ("style" "display: none") (div ("class" "flex flex_col gap_1") (label ("for" "totp") (b (text "TOTP code"))) (input ("class" "surface") ("type" "text") ("placeholder" "totp code") ("name" "totp") ("id" "totp")))) (button ("class" "button surface") (text "{{ icon \"arrow-right\" }}") (text "Continue"))) (hr ("class" "margin")) (a ("href" "{{ config.service_hosts.tetratto }}/auth/register") (text "I don't have a Tetratto account"))) (script (text "let flow_page = 1; function next_page() { document.getElementById(`flow_${flow_page}`).style.display = \"none\"; flow_page += 1; document.getElementById(`flow_${flow_page}`).style.display = \"contents\"; } async function login(e) { e.preventDefault(); if (flow_page === 1) { // check if we need TOTP const res = await ( await fetch( `/api/v1/auth/user/${e.target.username.value}/check_totp`, ) ).json(); if (res.ok && res.payload) { // user exists AND totp is required return next_page(); } } fetch(\"/api/v1/auth/login\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\", }, body: JSON.stringify({ username: e.target.username.value, password: e.target.password.value, totp: e.target.totp.value, }), }) .then((res) => res.json()) .then(async (res) => { show_message(res.message, res.ok); if (res.ok) { // update tokens LOGIN_ACCOUNT_TOKENS[e.target.username.value] = res.message; save_login_account_tokens(); // redirect setTimeout(() => { window.location.href = \"/app\"; }, 150); } }); }")) (text "{% endblock %}")