add: login

This commit is contained in:
trisua 2025-08-24 17:04:27 -04:00
parent ce9ce4f635
commit 8c86dd6cda
13 changed files with 407 additions and 25 deletions

View file

@ -1,10 +1,6 @@
(text "{% extends \"root.lisp\" %} {% block head %}")
(title
(text "{{ name }}"))
(meta ("property" "og:title") ("content" "{{ name }}"))
(meta ("property" "twitter:title") ("content" "{{ name }}"))
(link ("rel" "icon") ("href" "/public/favicon.svg"))
(text "{% endblock %} {% block body %}")
(div
("class" "card")

View file

@ -0,0 +1,109 @@
(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 %}")

View file

@ -15,7 +15,12 @@
(meta ("property" "og:type") ("content" "website"))
(meta ("property" "og:site_name") ("content" "{{ name }}"))
(meta ("property" "og:title") ("content" "{{ name }}"))
(meta ("property" "twitter:title") ("content" "{{ name }}"))
(link ("rel" "icon") ("href" "/public/favicon.svg"))
(script ("src" "/public/app.js?v={{ build_code }}") ("defer"))
(script ("src" "/public/tokens.js?v={{ build_code }}") ("defer"))
(text "{% block head %}{% endblock %}"))
@ -42,6 +47,30 @@
("class" "button")
("href" "https://trisua.com/t/malachite")
(text "source"))
(hr)
(text "{% if not user -%}")
(a
("class" "button")
("href" "/login")
(text "login"))
(a
("class" "button")
("href" "{{ config.service_hosts.tetratto }}/auth/register")
(text "sign up"))
(text "{%- else -%}")
(a
("class" "button")
("href" "/app")
(text "app"))
(a
("class" "button")
("href" "{{ config.service_hosts.tetratto }}/settings")
(text "settings"))
(button
("class" "button red")
("onclick" "user_logout()")
(text "logout"))
(text "{%- endif %}")
(text "{% block dropdown %}{% endblock %}")))
(a ("class" "button camo") ("href" "/") (b (text "{{ name }}"))))
@ -51,14 +80,14 @@
; theme switches
(button
("class" "button camo fade")
("class" "button camo fade filled")
("id" "switch_light")
("title" "Switch theme")
("onclick" "set_theme('Dark')")
(text "{{ icon \"sun\" }}"))
(button
("class" "button camo fade hidden")
("class" "button camo fade filled hidden")
("id" "switch_dark")
("title" "Switch theme")
("onclick" "set_theme('Light')")