127 lines
4 KiB
HTML
127 lines
4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>NIMING Login</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
<script src="https://unpkg.com/htmx.org@2.0.4"
|
|
integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+"
|
|
crossorigin="anonymous"></script>
|
|
|
|
</head>
|
|
|
|
|
|
<body class="grid place-center w-dvw h-dvh bg-zinc-900">
|
|
<script>
|
|
const totp_length = 6;
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
let totp_cell = document.getElementById("totp_cell");
|
|
let totp_container = document.getElementById("totp");
|
|
|
|
for (let i = 0; i < totp_length; i++) {
|
|
totp_cell.content.firstElementChild.dataset.indexNumber = i
|
|
totp_cell.content.firstElementChild.name = `totp-${i}`
|
|
let node = document.importNode(totp_cell.content, true)
|
|
totp_container.appendChild(node);
|
|
}
|
|
|
|
const keyup_ev = (ev) => {
|
|
if (ev.keyCode <= 0x39 && ev.keyCode >= 0x30) {
|
|
ev.target.value = ev.key
|
|
if (ev.target.nextElementSibling) {
|
|
ev.target.nextElementSibling.focus()
|
|
}
|
|
}
|
|
if (ev.key = "a" && ev.ctrlKey) {
|
|
console.log("Select all")
|
|
for (let i = 1; i <= totp_length; i++) {
|
|
totp_container.children[i].select()
|
|
}
|
|
}
|
|
if (ev.key == "Backspace" && ev.target.dataset.indexNumber > 0) {
|
|
const previous = totp_container.querySelector(`[data-index-number='${ev.target.dataset.indexNumber - 1}']`)
|
|
previous.focus()
|
|
}
|
|
|
|
}
|
|
const paste_input_ev = (ev) => {
|
|
if (ev.inputType == "insertFromPaste") {
|
|
let data = ev.data
|
|
console.log(data)
|
|
for (let i = 1; i <= totp_length; i++) {
|
|
totp_container.children[i].value = data.slice(0, 1)
|
|
data = data.slice(1, data.length)
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const cell of totp_container.children) {
|
|
if (cell instanceof HTMLInputElement) {
|
|
cell.addEventListener("keyup", keyup_ev)
|
|
cell.addEventListener("input", paste_input_ev)
|
|
|
|
}
|
|
}
|
|
|
|
const form_onsubmit = (ev) => {
|
|
let form = new FormData(ev.target)
|
|
let miss = form.entries().find(p => p[1] == "")
|
|
if (miss) {
|
|
alert(`Missing ${miss[0]}`)
|
|
ev.preventDefault()
|
|
}
|
|
console.log(form)
|
|
|
|
}
|
|
const form_formdata = (ev) => {
|
|
const data = ev.formData
|
|
let totp = new Array(totp_length)
|
|
for (const [k, v] of data.entries()) {
|
|
if (k.match(/totp-\d/g)) {
|
|
totp[parseInt(k.split("-")[1])] = v
|
|
}
|
|
}
|
|
for (let i = 0; i < totp_length; i++) {
|
|
data.delete(`totp-${i}`)
|
|
}
|
|
data.set("totp_code", totp.join(""))
|
|
}
|
|
const form = document.getElementById("login_form")
|
|
form.addEventListener("submit", form_onsubmit)
|
|
form.addEventListener("formdata", form_formdata)
|
|
});
|
|
</script>
|
|
|
|
<form id="login_form"
|
|
class="m-auto h-fit w-10/12 md:w-5/12 lg:w-1/4 bg-zinc-400 drop-shadow-lg flex flex-col px-10 box-border py-5 gap-10 rounded-xl"
|
|
action="/admin/login" method="post">
|
|
<h1 class="text-3xl text-center">中工匿名管理</h1>
|
|
<div class="flex flex-col gap-2">
|
|
<label for="username">名稱</label>
|
|
<input name="name" id="username" type="text"
|
|
class="block rounded outline-none ring-3 border-box px-3" />
|
|
</div>
|
|
<div class="flex flex-col gap-2">
|
|
<label for="password">密碼</label>
|
|
<input name="password" id="password" type="password"
|
|
class="block rounded outline-none ring-3 border-box px-3" />
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-2">
|
|
<label for="totp">TOTP</label>
|
|
<div class="flex flex-row gap-3 w-full px-auto justify-center box-border" id="totp">
|
|
<template id="totp_cell">
|
|
<input type="text" inputmode="numeric" pattern="[0-9]{1}"
|
|
class="block rounded outline-none ring-2 border-box w-7 h-7 text-center totp"
|
|
data-index-number="0" maxlength="1" minlength="1" />
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<button type="submit"
|
|
class="w-fit px-5 py-1 mx-auto bg-sky-500 rounded-lg drop-shadow-cyan-500/50 drop-shadow-lg">確認</button>
|
|
</form>
|
|
</body>
|
|
|
|
</html>
|