mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-11-26 10:39:21 +08:00
增加登陆和注册页面
This commit is contained in:
@@ -1,36 +1,36 @@
|
||||
.auth-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
// .auth-page {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// height: 100%;
|
||||
// width: 100%;
|
||||
// flex-direction: column;
|
||||
|
||||
.auth-logo {
|
||||
transform: scale(1.4);
|
||||
}
|
||||
// .auth-logo {
|
||||
// transform: scale(1.4);
|
||||
// }
|
||||
|
||||
.auth-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 2;
|
||||
}
|
||||
// .auth-title {
|
||||
// font-size: 24px;
|
||||
// font-weight: bold;
|
||||
// line-height: 2;
|
||||
// }
|
||||
|
||||
.auth-tips {
|
||||
font-size: 14px;
|
||||
}
|
||||
// .auth-tips {
|
||||
// font-size: 14px;
|
||||
// }
|
||||
|
||||
.auth-input {
|
||||
margin: 3vh 0;
|
||||
}
|
||||
// .auth-input {
|
||||
// margin: 3vh 0;
|
||||
// }
|
||||
|
||||
.auth-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
// .auth-actions {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// flex-direction: column;
|
||||
|
||||
button:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// button:not(:last-child) {
|
||||
// margin-bottom: 10px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
import styles from "./auth.module.scss";
|
||||
import { IconButton } from "./button";
|
||||
// import styles from "./auth.module.scss";
|
||||
// import { IconButton } from "./button";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Path } from "../constant";
|
||||
import { useAccessStore } from "../store";
|
||||
import Locale from "../locales";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import { Path } from "../constant";
|
||||
// import { useAccessStore } from "../store";
|
||||
// import Locale from "../locales";
|
||||
|
||||
import BotIcon from "../icons/bot.svg";
|
||||
import { useEffect } from "react";
|
||||
import { getClientConfig } from "../config/client";
|
||||
// import BotIcon from "../icons/bot.svg";
|
||||
// import { useEffect } from "react";
|
||||
// import { getClientConfig } from "../config/client";
|
||||
|
||||
export function AuthPage() {
|
||||
const navigate = useNavigate();
|
||||
const accessStore = useAccessStore();
|
||||
// export function AuthPage() {
|
||||
// const navigate = useNavigate();
|
||||
// const accessStore = useAccessStore();
|
||||
|
||||
const goHome = () => navigate(Path.Home);
|
||||
const goChat = () => navigate(Path.Chat);
|
||||
const resetAccessCode = () => {
|
||||
accessStore.update((access) => {
|
||||
access.openaiApiKey = "";
|
||||
access.accessCode = "";
|
||||
});
|
||||
}; // Reset access code to empty string
|
||||
// const goHome = () => navigate(Path.Home);
|
||||
// const goChat = () => navigate(Path.Chat);
|
||||
// const resetAccessCode = () => {
|
||||
// accessStore.update((access) => {
|
||||
// access.openaiApiKey = "";
|
||||
// access.accessCode = "";
|
||||
// });
|
||||
// }; // Reset access code to empty string
|
||||
|
||||
useEffect(() => {
|
||||
if (getClientConfig()?.isApp) {
|
||||
navigate(Path.Settings);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// if (getClientConfig()?.isApp) {
|
||||
// navigate(Path.Settings);
|
||||
// }
|
||||
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<div className={styles["auth-page"]}>
|
||||
<div className={`no-dark ${styles["auth-logo"]}`}>
|
||||
<BotIcon />
|
||||
</div>
|
||||
// return (
|
||||
// <div className={styles["auth-page"]}>
|
||||
// <div className={`no-dark ${styles["auth-logo"]}`}>
|
||||
// <BotIcon />
|
||||
// </div>
|
||||
|
||||
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
|
||||
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
|
||||
// <div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
|
||||
// <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
|
||||
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Auth.Input}
|
||||
value={accessStore.accessCode}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
(access) => (access.accessCode = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{!accessStore.hideUserApiKey ? (
|
||||
<>
|
||||
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||
value={accessStore.openaiApiKey}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
(access) => (access.openaiApiKey = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||
value={accessStore.googleApiKey}
|
||||
onChange={(e) => {
|
||||
accessStore.update(
|
||||
(access) => (access.googleApiKey = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
// <input
|
||||
// className={styles["auth-input"]}
|
||||
// type="password"
|
||||
// placeholder={Locale.Auth.Input}
|
||||
// value={accessStore.accessCode}
|
||||
// onChange={(e) => {
|
||||
// accessStore.update(
|
||||
// (access) => (access.accessCode = e.currentTarget.value),
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// {!accessStore.hideUserApiKey ? (
|
||||
// <>
|
||||
// <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
|
||||
// <input
|
||||
// className={styles["auth-input"]}
|
||||
// type="password"
|
||||
// placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
|
||||
// value={accessStore.openaiApiKey}
|
||||
// onChange={(e) => {
|
||||
// accessStore.update(
|
||||
// (access) => (access.openaiApiKey = e.currentTarget.value),
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// <input
|
||||
// className={styles["auth-input"]}
|
||||
// type="password"
|
||||
// placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
|
||||
// value={accessStore.googleApiKey}
|
||||
// onChange={(e) => {
|
||||
// accessStore.update(
|
||||
// (access) => (access.googleApiKey = e.currentTarget.value),
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
// </>
|
||||
// ) : null}
|
||||
|
||||
<div className={styles["auth-actions"]}>
|
||||
<IconButton
|
||||
text={Locale.Auth.Confirm}
|
||||
type="primary"
|
||||
onClick={goChat}
|
||||
/>
|
||||
<IconButton
|
||||
text={Locale.Auth.Later}
|
||||
onClick={() => {
|
||||
resetAccessCode();
|
||||
goHome();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// <div className={styles["auth-actions"]}>
|
||||
// <IconButton
|
||||
// text={Locale.Auth.Confirm}
|
||||
// type="primary"
|
||||
// onClick={goChat}
|
||||
// />
|
||||
// <IconButton
|
||||
// text={Locale.Auth.Later}
|
||||
// onClick={() => {
|
||||
// resetAccessCode();
|
||||
// goHome();
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from "react-router-dom";
|
||||
import { SideBar } from "./sidebar";
|
||||
import { useAppConfig } from "../store/config";
|
||||
import { AuthPage } from "./auth";
|
||||
import { AuthPage } from "./login";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { type ClientApi, getClientApi } from "../client/api";
|
||||
import { useAccessStore } from "../store";
|
||||
@@ -126,7 +126,7 @@ function Screen() {
|
||||
const config = useAppConfig();
|
||||
const location = useLocation();
|
||||
const isHome = location.pathname === Path.Home;
|
||||
const isAuth = location.pathname === Path.Auth;
|
||||
const isAuth = location.pathname === Path.Login;
|
||||
const isMobileScreen = useMobileScreen();
|
||||
const shouldTightBorder =
|
||||
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
|
||||
|
||||
36
app/components/login.module.scss
Normal file
36
app/components/login.module.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
.auth-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
|
||||
.auth-logo {
|
||||
transform: scale(1.4);
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.auth-tips {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.auth-input {
|
||||
margin: 1vh 0;
|
||||
}
|
||||
|
||||
.auth-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
button:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
app/components/login.tsx
Normal file
174
app/components/login.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import styles from "./login.module.scss";
|
||||
import { IconButton } from "./button";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Path, Login, BASE_URL } from "../constant";
|
||||
import { useAccessStore } from "../store";
|
||||
import Locale from "../locales";
|
||||
|
||||
import BotIcon from "../icons/bot.svg";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { showToast } from "./ui-lib";
|
||||
|
||||
export function AuthPage() {
|
||||
const navigate = useNavigate();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const [isLogin, setIsLogin] = useState(true);
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
|
||||
const goHome = () => navigate(Path.Home);
|
||||
const goChat = () => navigate(Path.Chat);
|
||||
const resetAccessCode = () => {
|
||||
accessStore.update((access) => {
|
||||
access.openaiApiKey = "";
|
||||
access.accessCode = "";
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (getClientConfig()?.isApp) {
|
||||
navigate(Path.Settings);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleLogin = async () => {
|
||||
try {
|
||||
const url = BASE_URL + Login.Login;
|
||||
|
||||
const formData = new URLSearchParams();
|
||||
formData.append("username", username);
|
||||
formData.append("password", password);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status.code === 0 && data.result === true) {
|
||||
// 登录成功逻辑
|
||||
accessStore.update((access) => {
|
||||
access.accessCode = data.accessCode; // 如果需要从响应中获取 accessCode 的话
|
||||
});
|
||||
goChat(); // 导航到聊天页面或其他逻辑
|
||||
} else {
|
||||
// 登录失败
|
||||
showToast(Locale.Login.LoginFail);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("API 请求失败", error);
|
||||
showToast(Locale.Login.SystemFail);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRegister = async () => {
|
||||
try {
|
||||
const url = BASE_URL + Login.Register;
|
||||
|
||||
const formData = new URLSearchParams();
|
||||
formData.append("username", username);
|
||||
formData.append("password", password);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status.code === 0 && data.result === true) {
|
||||
// 注册成功逻辑
|
||||
showToast(Locale.Login.ResisterSuc);
|
||||
setIsLogin(true); // 切换到登录状态
|
||||
} else {
|
||||
// 注册失败
|
||||
showToast(Locale.Login.ResisterFail);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("API 请求失败", error);
|
||||
showToast(Locale.Login.SystemFail);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (isLogin) {
|
||||
handleLogin();
|
||||
} else {
|
||||
if (password !== confirmPassword) {
|
||||
showToast(Locale.Login.PasswordFail);
|
||||
return;
|
||||
}
|
||||
handleRegister();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles["auth-page"]}>
|
||||
<div className={`no-dark ${styles["auth-logo"]}`}>
|
||||
<BotIcon />
|
||||
</div>
|
||||
|
||||
<div className={styles["auth-title"]}>
|
||||
{isLogin ? Locale.Login.LoginTitle : Locale.Login.RegisterTitle}
|
||||
</div>
|
||||
<div className={styles["auth-tips"]}>
|
||||
{isLogin ? Locale.Login.LoginTips : Locale.Login.RegisterTips}
|
||||
</div>
|
||||
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="text"
|
||||
placeholder={Locale.Login.Username}
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.currentTarget.value)}
|
||||
/>
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Login.Password}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.currentTarget.value)}
|
||||
/>
|
||||
{!isLogin && (
|
||||
<input
|
||||
className={styles["auth-input"]}
|
||||
type="password"
|
||||
placeholder={Locale.Login.ConfirmPassword}
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={styles["auth-actions"]}>
|
||||
<IconButton
|
||||
text={isLogin ? Locale.Login.Login : Locale.Login.Register}
|
||||
type="primary"
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<IconButton
|
||||
text={Locale.Login.Switch}
|
||||
onClick={() => setIsLogin(!isLogin)}
|
||||
/>
|
||||
<IconButton
|
||||
text={Locale.Login.Later}
|
||||
onClick={() => {
|
||||
resetAccessCode();
|
||||
goHome();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -18,13 +18,20 @@ export const BAIDU_BASE_URL = "https://aip.baidubce.com";
|
||||
|
||||
export const BAIDU_OATUH_URL = `${BAIDU_BASE_URL}/oauth/2.0/token`;
|
||||
|
||||
export const BASE_URL = `http://127.0.0.1:8080`;
|
||||
|
||||
export enum Login {
|
||||
Register = "/login/register",
|
||||
Login = "/login/username",
|
||||
}
|
||||
|
||||
export enum Path {
|
||||
Home = "/",
|
||||
Chat = "/chat",
|
||||
Settings = "/settings",
|
||||
NewChat = "/new-chat",
|
||||
Masks = "/masks",
|
||||
Auth = "/auth",
|
||||
Login = "/login",
|
||||
}
|
||||
|
||||
export enum ApiPath {
|
||||
|
||||
@@ -8,15 +8,27 @@ const cn = {
|
||||
Error: {
|
||||
Unauthorized: isApp
|
||||
? "检测到无效 API Key,请前往[设置](/#/settings)页检查 API Key 是否配置正确。"
|
||||
: "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。",
|
||||
: "请先登录后使用,请前往[登录](/#/login)页输入正确的用户名密码",
|
||||
},
|
||||
Auth: {
|
||||
Title: "需要密码",
|
||||
Tips: "管理员开启了密码验证,请在下方填入访问码",
|
||||
SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
|
||||
Input: "在此处填写访问码",
|
||||
Login: {
|
||||
Title: "需要登陆",
|
||||
Confirm: "确认",
|
||||
Later: "稍后再说",
|
||||
Switch: "切换注册/登录",
|
||||
LoginTitle: "用户登录",
|
||||
RegisterTitle: "用户注册",
|
||||
LoginTips: "请输入您的用户名和密码登录",
|
||||
RegisterTips: "请输入您的用户名和密码进行注册",
|
||||
Username: "用户名",
|
||||
Password: "密码",
|
||||
ConfirmPassword: "确认密码",
|
||||
Login: "登录",
|
||||
Register: "注册",
|
||||
LoginFail: "登录失败,请检查用户名和密码",
|
||||
SystemFail: "请求失败,请重试",
|
||||
ResisterSuc: "注册成功,请登录",
|
||||
ResisterFail: "注册失败,请稍后重试",
|
||||
PasswordFail: "密码前后不一致",
|
||||
},
|
||||
ChatItem: {
|
||||
ChatItemCount: (count: number) => `${count} 条对话`,
|
||||
|
||||
Reference in New Issue
Block a user