From a573d601911bbe61475e14ab3d49d7e74f7f097c Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sat, 15 Jul 2023 15:02:03 +0800 Subject: [PATCH] Add Login page --- components/Login.tsx | 85 +++++++++++++++++++++++++++++++++++++++++++ islands/Container.tsx | 2 + server/auth.ts | 4 +- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 components/Login.tsx diff --git a/components/Login.tsx b/components/Login.tsx new file mode 100644 index 0000000..1199dc0 --- /dev/null +++ b/components/Login.tsx @@ -0,0 +1,85 @@ +import { Component, ContextType } from "preact"; +import { GlobalCtx } from "./GlobalContext.tsx"; +import BMd3TextField from "./BMd3TextField.tsx"; +import t from "../server/i18n.ts"; +import { useState } from "preact/hooks"; +import { MdTonalButton } from "../server/dmodule.ts"; +import { set_state } from "../server/state.ts"; +import pbkdf2Hmac from "pbkdf2-hmac/?target=es2022"; +import { encode } from "std/encoding/base64.ts"; + +type Props = { + show: boolean; +}; + +export default class Login extends Component { + static contextType = GlobalCtx; + declare context: ContextType; + render() { + if (!this.props.show) return null; + if (!MdTonalButton.value) return null; + const [username, set_username] = useState(); + const [password, set_password] = useState(); + const [disabled, set_disabled] = useState(false); + const login = async (username: string, password: string) => { + set_disabled(true); + const b = new URLSearchParams(); + b.append("username", username); + const p = new Uint8Array( + await pbkdf2Hmac( + password, + "eh-downloader-salt", + 210000, + 64, + "SHA-512", + ), + ); + const t = (new Date()).getTime(); + const p2 = encode( + new Uint8Array( + await pbkdf2Hmac(p, t.toString(), 1000, 64, "SHA-512"), + ), + ); + b.append("password", p2); + b.append("t", t.toString()); + b.append("set_cookie", "1"); + const re2 = await fetch("/api/token", { method: "PUT", body: b }); + const token = await re2.json(); + if (!token.ok) { + throw Error(token.error); + } + }; + const Button = MdTonalButton.value; + return ( +
+ + + +
+ ); + } +} diff --git a/islands/Container.tsx b/islands/Container.tsx index 0af29ab..4274ea0 100644 --- a/islands/Container.tsx +++ b/islands/Container.tsx @@ -18,6 +18,7 @@ import { initCfg } from "../server/cfg.ts"; import { load_dmodule } from "../server/dmodule.ts"; import CreateRootUser from "../components/CreateRootUser.tsx"; import { check_auth_status } from "../server/auth.ts"; +import Login from "../components/Login.tsx"; export type ContainerProps = { i18n: I18NMap; @@ -108,6 +109,7 @@ export default class Container extends Component { /> + ); } diff --git a/server/auth.ts b/server/auth.ts index 061602e..9bd42f1 100644 --- a/server/auth.ts +++ b/server/auth.ts @@ -8,7 +8,7 @@ export async function check_auth_status() { const re = await fetch("/api/user"); const u: JSONResult = await re.json(); if (u.ok) return true; - if (u.status !== 404 && u.status !== 1) { + if (u.status !== 404 && u.status !== 1 && u.status !== 401) { throw Error(u.error); } const re2 = await fetch("/api/status"); @@ -20,5 +20,5 @@ export async function check_auth_status() { if (!parse_bool(localStorage.getItem("skip_create_root_user"), false)) { set_state("#/create_root_user"); } - } + } else set_state("#/login"); }