Skip to content

Commit

Permalink
update: auth validators
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Dec 23, 2023
1 parent f1ae776 commit 0bc356c
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 71 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,8 @@ Replace `https://api.openai.com` with `https://api.chatnio.net` and fill in the
- 应用技术: PWA + HTTP2 + WebSocket + Stream Buffer


## 🎃 开发团队 | Team
- [@ProgramZmh](https://github.com/zmh-program) (全栈开发)
- [@Sh1n3zz](https://github.com/sh1n3zz) (全栈开发)
- [@一個小果冻](https://b23.tv/XjdZ4DN) (美工、UI 设计)

## 🎃 贡献者 | Contributors
![Contributors](https://stats.deeptrain.net/contributor/Deeptrain-Community/chatnio/?column=6&theme=light)

## 📚 SDKs
- [JavaScript SDK](https://github.com/Deeptrain-Community/chatnio-api-node)
Expand Down
23 changes: 23 additions & 0 deletions app/src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios from "axios";
import { getErrorMessage } from "@/utils/base.ts";
import { isEmailValid } from "@/utils/form.ts";

export type LoginForm = {
username: string;
Expand Down Expand Up @@ -109,3 +110,25 @@ export async function doReset(data: ResetForm): Promise<ResetResponse> {
};
}
}

export async function sendCode(
t: any,
toast: any,
email: string,
): Promise<boolean> {
if (email.trim().length === 0 || !isEmailValid(email)) return false;

const res = await doVerify(email);
if (!res.status)
toast({
title: t("auth.send-code-failed"),
description: t("auth.send-code-failed-prompt", { reason: res.error }),
});
else
toast({
title: t("auth.send-code-success"),
description: t("auth.send-code-success-prompt"),
});

return res.status;
}
12 changes: 11 additions & 1 deletion app/src/resources/i18n/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"home": "首页",
"login": "登录",
"register": "注册",
"reset": "重置",
"login-require": "您需要登录才能使用此功能",
"logout": "登出",
"quota": "点数",
Expand Down Expand Up @@ -35,6 +36,7 @@
"fatal": "应用崩溃",
"download-fatal-log": "下载错误日志",
"fatal-tips": "请您先检查您的网络,浏览器兼容性,尝试清除浏览器缓存并刷新页面。如果问题仍然存在,请将日志提供给开发者以便我们排查问题。",
"request-error": "请求失败,原因:{{reason}}",
"auth": {
"username": "用户名",
"username-placeholder": "请输入用户名",
Expand All @@ -61,7 +63,15 @@
"verify": "验证",
"length-range": "应为 {{min}} ~ {{max}} 位",
"same-rule": "两次输入不一致",
"invalid-email": "邮箱格式错误"
"invalid-email": "邮箱格式错误",
"reset-success": "重置成功",
"reset-success-prompt": "您的密码已重置,请使用新密码登录。",
"send-code-success": "发送成功",
"send-code-success-prompt": "验证码已发送至您的邮箱,请注意查收。",
"send-code-failed": "发送失败",
"send-code-failed-prompt": "验证码发送失败,原因:{{reason}}",
"register-success": "注册成功",
"register-success-prompt": "您已成功注册,欢迎你的到来!"
},
"tag": {
"free": "免费",
Expand Down
14 changes: 12 additions & 2 deletions app/src/resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@
"fall-back": "Go back one step",
"length-range": "Expected {{min}} ~ {{max}} digits",
"same-rule": "* Fields do not match",
"invalid-email": "The email doesn't look right !"
}
"invalid-email": "The email doesn't look right !",
"reset-success": "Reset successful",
"reset-success-prompt": "Your password has been reset, please log in with your new password.",
"send-code-success": "Send success !",
"send-code-success-prompt": "The verification code has been sent to your email, please check it.",
"send-code-failed": "Send failed",
"send-code-failed-prompt": "Failed to send verification code, reason: {{reason}}",
"register-success": "Account created !",
"register-success-prompt": "You have successfully registered, welcome!"
},
"reset": "Reset",
"request-error": "Request failed for {{reason}}"
}
14 changes: 12 additions & 2 deletions app/src/resources/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@
"fall-back": "1つ前のステップに戻る",
"length-range": "{{min }}〜{{ max}}桁が必要です",
"same-rule": "一貫性のない入力",
"invalid-email": "メールの形式が正しくありません"
}
"invalid-email": "メールの形式が正しくありません",
"reset-success": "リセットに成功しました",
"reset-success-prompt": "パスワードがリセットされました。新しいパスワードでログインしてください。",
"send-code-success": "送信成功",
"send-code-success-prompt": "認証コードがメールアドレスに送信されました。ご確認ください。",
"send-code-failed": "送信失敗",
"send-code-failed-prompt": "認証コードの送信に失敗しました。理由:{{ reason}}",
"register-success": "登録に成功しました",
"register-success-prompt": "登録が完了しました。ようこそ!"
},
"reset": "リセット",
"request-error": "{{reason}}のためにリクエストできませんでした"
}
14 changes: 12 additions & 2 deletions app/src/resources/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@
"fall-back": "Вернитесь на шаг назад",
"length-range": "Ожидаемые цифры: {{min}} ~ {{max}}",
"same-rule": "Несогласованные входные данные",
"invalid-email": "Неверный формат электронной почты"
}
"invalid-email": "Неверный формат электронной почты",
"reset-success": "Сброс выполнен успешно",
"reset-success-prompt": "Ваш пароль был сброшен, пожалуйста, войдите с новым паролем.",
"send-code-success": "Отправка прошла успешно",
"send-code-success-prompt": "Код подтверждения отправлен на ваш адрес электронной почты. Проверьте его.",
"send-code-failed": "Не удалось отправить",
"send-code-failed-prompt": "Не удалось отправить код подтверждения, причина: {{reason}}",
"register-success": "Регистрация прошла успешно",
"register-success-prompt": "Вы успешно зарегистрировались, добро пожаловать!"
},
"reset": "сброс",
"request-error": "Запрос не выполнен по {{reason}}"
}
31 changes: 25 additions & 6 deletions app/src/routes/Auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { Card, CardContent } from "@/components/ui/card.tsx";
import { goAuth } from "@/utils/app.ts";
import { Label } from "@/components/ui/label.tsx";
import { Input } from "@/components/ui/input.tsx";
import Require from "@/components/Require.tsx";
import Require, { LengthRangeRequired } from "@/components/Require.tsx";
import { Button } from "@/components/ui/button.tsx";
import { formReducer } from "@/utils/form.ts";
import { formReducer, isTextInRange } from "@/utils/form.ts";
import { doLogin, LoginForm } from "@/api/auth.ts";
import { getErrorMessage } from "@/utils/base.ts";

Expand Down Expand Up @@ -98,7 +98,11 @@ function Login() {
});

const onSubmit = async () => {
if (!form.username.trim().length || !form.password.trim().length) return;
if (
!isTextInRange(form.username, 1, 255) ||
!isTextInRange(form.password, 6, 36)
)
return;

try {
const resp = await doLogin(form);
Expand All @@ -121,7 +125,7 @@ function Login() {
console.debug(err);
toast({
title: t("server-error"),
description: `${t("server-error-prompt")}\n${getErrorMessage(err)}`,
description: t("request-error", { reason: getErrorMessage(err) }),
});
}
};
Expand All @@ -134,7 +138,14 @@ function Login() {
<CardContent className={`pb-0`}>
<div className={`auth-wrapper`}>
<Label>
<Require /> {t("auth.username-or-email")}
<Require />
{t("auth.username-or-email")}
<LengthRangeRequired
content={form.username}
min={1}
max={255}
hideOnEmpty={true}
/>
</Label>
<Input
placeholder={t("auth.username-or-email-placeholder")}
Expand All @@ -145,11 +156,19 @@ function Login() {
/>

<Label>
<Require /> {t("auth.password")}
<Require />
{t("auth.password")}
<LengthRangeRequired
content={form.password}
min={6}
max={36}
hideOnEmpty={true}
/>
</Label>
<Input
placeholder={t("auth.password-placeholder")}
value={form.password}
type={"password"}
onChange={(e) =>
dispatch({ type: "update:password", payload: e.target.value })
}
Expand Down
131 changes: 96 additions & 35 deletions app/src/routes/Forgot.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,58 @@
import { useTranslation } from "react-i18next";
import { useToast } from "@/components/ui/use-toast.ts";
import { useDispatch } from "react-redux";
import { useReducer } from "react";
import { formReducer } from "@/utils/form.ts";
import { doLogin, LoginForm, ResetForm } from "@/api/auth.ts";
import { validateToken } from "@/store/auth.ts";
import { formReducer, isEmailValid, isTextInRange } from "@/utils/form.ts";
import { doReset, ResetForm, sendCode } from "@/api/auth.ts";
import router from "@/router.tsx";
import { getErrorMessage } from "@/utils/base.ts";
import { Card, CardContent } from "@/components/ui/card.tsx";
import { Label } from "@/components/ui/label.tsx";
import Require from "@/components/Require.tsx";
import Require, {
EmailRequire,
LengthRangeRequired,
SameRequired,
} from "@/components/Require.tsx";
import { Input } from "@/components/ui/input.tsx";
import { Button } from "@/components/ui/button.tsx";
import TickButton from "@/components/TickButton.tsx";

function Forgot() {
const { t } = useTranslation();
const { toast } = useToast();
const globalDispatch = useDispatch();
const [form, dispatch] = useReducer(formReducer<ResetForm>(), {
email: "",
code: "",
password: "",
repassword: "",
});

const onSubmit = async () => {
if (!form.username.trim().length || !form.password.trim().length) return;

try {
const resp = await doLogin(form);
if (!resp.status) {
toast({
title: t("login-failed"),
description: t("login-failed-prompt", { reason: resp.error }),
});
return;
}
const onVerify = async () => await sendCode(t, toast, form.email);

toast({
title: t("login-success"),
description: t("login-success-prompt"),
});
const onSubmit = async () => {
if (
!isEmailValid(form.email) ||
!form.code.length ||
!isTextInRange(form.password, 6, 36) ||
form.password.trim() !== form.repassword.trim()
)
return;

validateToken(globalDispatch, resp.token);
await router.navigate("/");
} catch (err) {
console.debug(err);
const res = await doReset(form);
if (!res.status) {
toast({
title: t("server-error"),
description: `${t("server-error-prompt")}\n${getErrorMessage(err)}`,
title: t("error"),
description: res.error,
});
return;
}

toast({
title: t("auth.reset-success"),
description: t("auth.reset-success-prompt"),
});

sessionStorage.setItem("username", form.email);
sessionStorage.setItem("password", form.password);
await router.navigate("/login");
};

return (
Expand All @@ -61,29 +63,88 @@ function Forgot() {
<CardContent className={`pb-0`}>
<div className={`auth-wrapper`}>
<Label>
<Require /> {t("auth.username-or-email")}
<Require />
{t("auth.email")}
<EmailRequire content={form.email} hideOnEmpty={true} />
</Label>
<Input
placeholder={t("auth.username-or-email-placeholder")}
value={form.username}
placeholder={t("auth.email-placeholder")}
value={form.email}
onChange={(e) =>
dispatch({ type: "update:username", payload: e.target.value })
dispatch({
type: "update:email",
payload: e.target.value,
})
}
/>

<Label>
<Require /> {t("auth.password")}
<Require /> {t("auth.code")}
</Label>

<div className={`flex flex-row`}>
<Input
placeholder={t("auth.code-placeholder")}
value={form.code}
onChange={(e) =>
dispatch({
type: "update:code",
payload: e.target.value,
})
}
/>
<TickButton
className={`ml-2 whitespace-nowrap`}
loading={true}
onClick={onVerify}
tick={60}
>
{t("auth.send-code")}
</TickButton>
</div>

<Label>
<Require />
{t("auth.password")}
<LengthRangeRequired
content={form.password}
min={6}
max={36}
hideOnEmpty={true}
/>
</Label>
<Input
placeholder={t("auth.password-placeholder")}
value={form.password}
type={"password"}
onChange={(e) =>
dispatch({ type: "update:password", payload: e.target.value })
}
/>

<Label>
<Require />
{t("auth.check-password")}
<SameRequired
content={form.password}
compare={form.repassword}
hideOnEmpty={true}
/>
</Label>
<Input
placeholder={t("auth.check-password-placeholder")}
value={form.repassword}
type={"password"}
onChange={(e) =>
dispatch({
type: "update:repassword",
payload: e.target.value,
})
}
/>

<Button onClick={onSubmit} className={`mt-2`} loading={true}>
{t("login")}
{t("reset")}
</Button>
</div>
</CardContent>
Expand Down
Loading

0 comments on commit 0bc356c

Please sign in to comment.