Files
new-api-oiss/web/src/components/settings/PersonalSetting.jsx
t0ng7u ada604db4e 🧹 refactor(settings): remove models API usage from Personal Settings
Personal Settings no longer needs to fetch `/api/user/models` since models are now displayed directly. This change removes the unused data flow to simplify the component and avoid unnecessary requests.

Changes:
- Removed `models` and `modelsLoading` state from `web/src/components/settings/PersonalSetting.jsx`
- Removed `loadModels` function and its invocation in the initial effect
- Kept UI behavior unchanged; no functional differences on the Personal Settings page

Notes:
- Lint passes with no new issues
- Other parts of the app still using `/api/user/models` (e.g., Tokens pages) are intentionally left intact

Rationale:
- Models are already displayed; the API call in Personal Settings became redundant
2025-08-22 23:01:32 +08:00

394 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useContext, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
API,
copy,
showError,
showInfo,
showSuccess
} from '../../helpers';
import { UserContext } from '../../context/User';
import { Modal } from '@douyinfe/semi-ui';
import { useTranslation } from 'react-i18next';
// 导入子组件
import UserInfoHeader from './personal/components/UserInfoHeader';
import AccountManagement from './personal/cards/AccountManagement';
import NotificationSettings from './personal/cards/NotificationSettings';
import EmailBindModal from './personal/modals/EmailBindModal';
import WeChatBindModal from './personal/modals/WeChatBindModal';
import AccountDeleteModal from './personal/modals/AccountDeleteModal';
import ChangePasswordModal from './personal/modals/ChangePasswordModal';
const PersonalSetting = () => {
const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate();
const { t } = useTranslation();
const [inputs, setInputs] = useState({
wechat_verification_code: '',
email_verification_code: '',
email: '',
self_account_deletion_confirmation: '',
original_password: '',
set_new_password: '',
set_new_password_confirmation: '',
});
const [status, setStatus] = useState({});
const [showChangePasswordModal, setShowChangePasswordModal] = useState(false);
const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
const [showEmailBindModal, setShowEmailBindModal] = useState(false);
const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
const [turnstileEnabled, setTurnstileEnabled] = useState(false);
const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
const [turnstileToken, setTurnstileToken] = useState('');
const [loading, setLoading] = useState(false);
const [disableButton, setDisableButton] = useState(false);
const [countdown, setCountdown] = useState(30);
const [systemToken, setSystemToken] = useState('');
const [notificationSettings, setNotificationSettings] = useState({
warningType: 'email',
warningThreshold: 100000,
webhookUrl: '',
webhookSecret: '',
notificationEmail: '',
acceptUnsetModelRatioModel: false,
recordIpLog: false,
});
useEffect(() => {
let status = localStorage.getItem('status');
if (status) {
status = JSON.parse(status);
setStatus(status);
if (status.turnstile_check) {
setTurnstileEnabled(true);
setTurnstileSiteKey(status.turnstile_site_key);
}
}
getUserData().then((res) => {
console.log(userState);
});
}, []);
useEffect(() => {
let countdownInterval = null;
if (disableButton && countdown > 0) {
countdownInterval = setInterval(() => {
setCountdown(countdown - 1);
}, 1000);
} else if (countdown === 0) {
setDisableButton(false);
setCountdown(30);
}
return () => clearInterval(countdownInterval); // Clean up on unmount
}, [disableButton, countdown]);
useEffect(() => {
if (userState?.user?.setting) {
const settings = JSON.parse(userState.user.setting);
setNotificationSettings({
warningType: settings.notify_type || 'email',
warningThreshold: settings.quota_warning_threshold || 500000,
webhookUrl: settings.webhook_url || '',
webhookSecret: settings.webhook_secret || '',
notificationEmail: settings.notification_email || '',
acceptUnsetModelRatioModel:
settings.accept_unset_model_ratio_model || false,
recordIpLog: settings.record_ip_log || false,
});
}
}, [userState?.user?.setting]);
const handleInputChange = (name, value) => {
setInputs((inputs) => ({ ...inputs, [name]: value }));
};
const generateAccessToken = async () => {
const res = await API.get('/api/user/token');
const { success, message, data } = res.data;
if (success) {
setSystemToken(data);
await copy(data);
showSuccess(t('令牌已重置并已复制到剪贴板'));
} else {
showError(message);
}
};
const getUserData = async () => {
let res = await API.get(`/api/user/self`);
const { success, message, data } = res.data;
if (success) {
userDispatch({ type: 'login', payload: data });
} else {
showError(message);
}
};
const handleSystemTokenClick = async (e) => {
e.target.select();
await copy(e.target.value);
showSuccess(t('系统令牌已复制到剪切板'));
};
const deleteAccount = async () => {
if (inputs.self_account_deletion_confirmation !== userState.user.username) {
showError(t('请输入你的账户名以确认删除!'));
return;
}
const res = await API.delete('/api/user/self');
const { success, message } = res.data;
if (success) {
showSuccess(t('账户已删除!'));
await API.get('/api/user/logout');
userDispatch({ type: 'logout' });
localStorage.removeItem('user');
navigate('/login');
} else {
showError(message);
}
};
const bindWeChat = async () => {
if (inputs.wechat_verification_code === '') return;
const res = await API.get(
`/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`,
);
const { success, message } = res.data;
if (success) {
showSuccess(t('微信账户绑定成功!'));
setShowWeChatBindModal(false);
} else {
showError(message);
}
};
const changePassword = async () => {
if (inputs.original_password === '') {
showError(t('请输入原密码!'));
return;
}
if (inputs.set_new_password === '') {
showError(t('请输入新密码!'));
return;
}
if (inputs.original_password === inputs.set_new_password) {
showError(t('新密码需要和原密码不一致!'));
return;
}
if (inputs.set_new_password !== inputs.set_new_password_confirmation) {
showError(t('两次输入的密码不一致!'));
return;
}
const res = await API.put(`/api/user/self`, {
original_password: inputs.original_password,
password: inputs.set_new_password,
});
const { success, message } = res.data;
if (success) {
showSuccess(t('密码修改成功!'));
setShowWeChatBindModal(false);
} else {
showError(message);
}
setShowChangePasswordModal(false);
};
const sendVerificationCode = async () => {
if (inputs.email === '') {
showError(t('请输入邮箱!'));
return;
}
setDisableButton(true);
if (turnstileEnabled && turnstileToken === '') {
showInfo(t('请稍后几秒重试Turnstile 正在检查用户环境!'));
return;
}
setLoading(true);
const res = await API.get(
`/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`,
);
const { success, message } = res.data;
if (success) {
showSuccess(t('验证码发送成功,请检查邮箱!'));
} else {
showError(message);
}
setLoading(false);
};
const bindEmail = async () => {
if (inputs.email_verification_code === '') {
showError(t('请输入邮箱验证码!'));
return;
}
setLoading(true);
const res = await API.get(
`/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`,
);
const { success, message } = res.data;
if (success) {
showSuccess(t('邮箱账户绑定成功!'));
setShowEmailBindModal(false);
userState.user.email = inputs.email;
} else {
showError(message);
}
setLoading(false);
};
const copyText = async (text) => {
if (await copy(text)) {
showSuccess(t('已复制:') + text);
} else {
// setSearchKeyword(text);
Modal.error({ title: t('无法复制到剪贴板,请手动复制'), content: text });
}
};
const handleNotificationSettingChange = (type, value) => {
setNotificationSettings((prev) => ({
...prev,
[type]: value.target ? value.target.value !== undefined ? value.target.value : value.target.checked : value, // handle checkbox properly
}));
};
const saveNotificationSettings = async () => {
try {
const res = await API.put('/api/user/setting', {
notify_type: notificationSettings.warningType,
quota_warning_threshold: parseFloat(
notificationSettings.warningThreshold,
),
webhook_url: notificationSettings.webhookUrl,
webhook_secret: notificationSettings.webhookSecret,
notification_email: notificationSettings.notificationEmail,
accept_unset_model_ratio_model:
notificationSettings.acceptUnsetModelRatioModel,
record_ip_log: notificationSettings.recordIpLog,
});
if (res.data.success) {
showSuccess(t('设置保存成功'));
await getUserData();
} else {
showError(res.data.message);
}
} catch (error) {
showError(t('设置保存失败'));
}
};
return (
<div className="mt-[60px]">
<div className="flex justify-center">
<div className="w-full max-w-7xl mx-auto px-2">
{/* 顶部用户信息区域 */}
<UserInfoHeader t={t} userState={userState} />
{/* 账户管理和其他设置 */}
<div className="grid grid-cols-1 xl:grid-cols-2 items-start gap-4 md:gap-6 mt-4 md:mt-6">
{/* 左侧:账户管理设置 */}
<AccountManagement
t={t}
userState={userState}
status={status}
systemToken={systemToken}
setShowEmailBindModal={setShowEmailBindModal}
setShowWeChatBindModal={setShowWeChatBindModal}
generateAccessToken={generateAccessToken}
handleSystemTokenClick={handleSystemTokenClick}
setShowChangePasswordModal={setShowChangePasswordModal}
setShowAccountDeleteModal={setShowAccountDeleteModal}
/>
{/* 右侧:其他设置 */}
<NotificationSettings
t={t}
notificationSettings={notificationSettings}
handleNotificationSettingChange={handleNotificationSettingChange}
saveNotificationSettings={saveNotificationSettings}
/>
</div>
</div>
</div>
{/* 模态框组件 */}
<EmailBindModal
t={t}
showEmailBindModal={showEmailBindModal}
setShowEmailBindModal={setShowEmailBindModal}
inputs={inputs}
handleInputChange={handleInputChange}
sendVerificationCode={sendVerificationCode}
bindEmail={bindEmail}
disableButton={disableButton}
loading={loading}
countdown={countdown}
turnstileEnabled={turnstileEnabled}
turnstileSiteKey={turnstileSiteKey}
setTurnstileToken={setTurnstileToken}
/>
<WeChatBindModal
t={t}
showWeChatBindModal={showWeChatBindModal}
setShowWeChatBindModal={setShowWeChatBindModal}
inputs={inputs}
handleInputChange={handleInputChange}
bindWeChat={bindWeChat}
status={status}
/>
<AccountDeleteModal
t={t}
showAccountDeleteModal={showAccountDeleteModal}
setShowAccountDeleteModal={setShowAccountDeleteModal}
inputs={inputs}
handleInputChange={handleInputChange}
deleteAccount={deleteAccount}
userState={userState}
turnstileEnabled={turnstileEnabled}
turnstileSiteKey={turnstileSiteKey}
setTurnstileToken={setTurnstileToken}
/>
<ChangePasswordModal
t={t}
showChangePasswordModal={showChangePasswordModal}
setShowChangePasswordModal={setShowChangePasswordModal}
inputs={inputs}
handleInputChange={handleInputChange}
changePassword={changePassword}
turnstileEnabled={turnstileEnabled}
turnstileSiteKey={turnstileSiteKey}
setTurnstileToken={setTurnstileToken}
/>
</div>
);
};
export default PersonalSetting;