diff --git a/src/plugin/admin.rs b/src/plugin/admin.rs index 1e49b10..5bf7115 100644 --- a/src/plugin/admin.rs +++ b/src/plugin/admin.rs @@ -242,7 +242,7 @@ impl AdminServe { } let path = req_header.uri.path(); if path.len() <= 1 - || Regex::new(r#".(js|css)$"#).unwrap().is_match(path) + || Regex::new(r#".(js|css|png)$"#).unwrap().is_match(path) { return true; } diff --git a/web/package-lock.json b/web/package-lock.json index 942df76..b83d52c 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -27,6 +27,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "crypto-js": "^4.2.0", "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", "lucide-react": "^0.453.0", @@ -46,6 +47,7 @@ }, "devDependencies": { "@eslint/js": "^9.13.0", + "@types/crypto-js": "^4.2.2", "@types/node": "^22.8.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -2391,6 +2393,13 @@ "@swc/counter": "^0.1.3" } }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -3483,6 +3492,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", diff --git a/web/package.json b/web/package.json index ea9c7ad..54d7671 100644 --- a/web/package.json +++ b/web/package.json @@ -29,6 +29,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "crypto-js": "^4.2.0", "i18next": "^23.16.4", "i18next-browser-languagedetector": "^8.0.0", "lucide-react": "^0.453.0", @@ -48,6 +49,7 @@ }, "devDependencies": { "@eslint/js": "^9.13.0", + "@types/crypto-js": "^4.2.2", "@types/node": "^22.8.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", diff --git a/web/src/helpers/util.ts b/web/src/helpers/util.ts index e73d68c..f7c6a34 100644 --- a/web/src/helpers/util.ts +++ b/web/src/helpers/util.ts @@ -1,4 +1,6 @@ import { z } from "zod"; +import sha256hash from "crypto-js/sha256"; +import hex from "crypto-js/enc-hex"; import HTTPError from "./http-error"; export function isError(err: Error | HTTPError | unknown, category: string) { @@ -80,11 +82,6 @@ export function formatLabel(label: string) { } export async function sha256(message: string) { - const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array - const hashBuffer = await window.crypto.subtle.digest("SHA-256", msgUint8); // hash the message - const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array - const hashHex = hashArray - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); // convert bytes to hex string - return hashHex; + const hashDigest = sha256hash(message); + return hex.stringify(hashDigest); } diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index fe2e2c6..0003f84 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -46,6 +46,7 @@ export default { account: "Account", password: "Password", submit: "Login", + fail: "Login Fail", }, home: { dashboard: "Dashboard", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index b56745a..0c3830e 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -40,6 +40,14 @@ export default { storage: "存储配置", searchPlaceholder: "输入关键字", }, + login: { + title: "登录", + description: "请输入管理员账号与密码", + account: "账号", + password: "密码", + submit: "登录", + fail: "登录失败", + }, home: { dashboard: "面板", basic: "基础信息", diff --git a/web/src/pages/Login.tsx b/web/src/pages/Login.tsx index 7948e24..9b3908a 100644 --- a/web/src/pages/Login.tsx +++ b/web/src/pages/Login.tsx @@ -15,9 +15,13 @@ import { saveLoginToken } from "@/states/token"; import useBasicState from "@/states/basic"; import { goToHome } from "@/routers"; import useConfigState from "@/states/config"; +import { useToast } from "@/hooks/use-toast"; +import { formatError } from "@/helpers/util"; export default function Login() { const loginI18n = useI18n("login"); + const { toast } = useToast(); + const [account, setAccount] = React.useState(""); const [password, setPassword] = React.useState(""); const [fetchBasicInfo] = useBasicState((state) => [state.fetch]); @@ -29,13 +33,16 @@ export default function Login() { await fetchConfig(); goToHome(); } catch (err) { - console.error(err); + toast({ + title: loginI18n("fail"), + description: formatError(err), + }); } }; return (
- + {loginI18n("title")} {loginI18n("description")} @@ -59,6 +66,11 @@ export default function Login() { onChange={(e) => { setPassword(e.target.value.trim()); }} + onKeyDown={(e) => { + if (e.code == "Enter") { + handleLogin(); + } + }} />