tetratto/crates/app/src/public/html/auth/login.lisp
2025-08-05 23:50:45 -04:00

121 lines
3.6 KiB
Common Lisp

(text "{% extends \"auth/base.html\" %} {% block head %}")
(title
(text "Login"))
(text "{% endblock %} {% block title %}Login{% endblock %} {% block content %}")
(form
("class" "w_full 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
("type" "text")
("placeholder" "username")
("required" "")
("name" "username")
("id" "username")))
(div
("class" "flex flex_col gap_1")
(label
("for" "username")
(b
(text "Password")))
(input
("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
("type" "text")
("placeholder" "totp code")
("name" "totp")
("id" "totp"))))
(button
(icon (text "arrow-right"))
(str (text "auth:action.continue"))))
(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}/totp/check`,
)
).json();
trigger(\"atto::toast\", [res.ok ? \"success\" : \"error\", res.message]);
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) => {
trigger(\"atto::toast\", [
res.ok ? \"success\" : \"error\",
res.message,
]);
if (res.ok) {
// update tokens
const new_tokens = (await ns(\"me\")).LOGIN_ACCOUNT_TOKENS;
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/register")
(text "register")))
(text "{% endblock %}")