feat: 禁止默认密码

This commit is contained in:
手瓜一十雪 2025-09-06 11:41:06 +08:00
parent e4c1807f76
commit e406dca7ae
4 changed files with 94 additions and 20 deletions

View File

@ -33,6 +33,14 @@ export default class WebUIManager {
return data.data return data.data
} }
public static async changePasswordFromDefault(newToken: string) {
const { data } = await serverRequest.post<ServerResponse<boolean>>(
'/auth/update_token',
{ newToken, fromDefault: true }
)
return data.data
}
public static async checkUsingDefaultToken() { public static async checkUsingDefaultToken() {
const { data } = await serverRequest.get<ServerResponse<boolean>>( const { data } = await serverRequest.get<ServerResponse<boolean>>(
'/auth/check_using_default_token' '/auth/check_using_default_token'

View File

@ -1,5 +1,6 @@
import { Input } from '@heroui/input' import { Input } from '@heroui/input'
import { useLocalStorage } from '@uidotdev/usehooks' import { useLocalStorage } from '@uidotdev/usehooks'
import { useEffect, useState } from 'react'
import { Controller, useForm } from 'react-hook-form' import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
@ -11,6 +12,9 @@ import SaveButtons from '@/components/button/save_buttons'
import WebUIManager from '@/controllers/webui_manager' import WebUIManager from '@/controllers/webui_manager'
const ChangePasswordCard = () => { const ChangePasswordCard = () => {
const [isDefaultToken, setIsDefaultToken] = useState<boolean>(false)
const [isLoadingCheck, setIsLoadingCheck] = useState<boolean>(true)
const { const {
control, control,
handleSubmit: handleWebuiSubmit, handleSubmit: handleWebuiSubmit,
@ -29,9 +33,32 @@ const ChangePasswordCard = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [_, setToken] = useLocalStorage(key.token, '') const [_, setToken] = useLocalStorage(key.token, '')
// 检查是否使用默认密码
useEffect(() => {
const checkDefaultToken = async () => {
try {
const isDefault = await WebUIManager.checkUsingDefaultToken()
setIsDefaultToken(isDefault)
} catch (error) {
console.error('检查默认密码状态失败:', error)
} finally {
setIsLoadingCheck(false)
}
}
checkDefaultToken()
}, [])
const onSubmit = handleWebuiSubmit(async (data) => { const onSubmit = handleWebuiSubmit(async (data) => {
try { try {
if (isDefaultToken) {
// 从默认密码更新
await WebUIManager.changePasswordFromDefault(data.newToken)
} else {
// 正常密码更新
await WebUIManager.changePassword(data.oldToken, data.newToken) await WebUIManager.changePassword(data.oldToken, data.newToken)
}
toast.success('修改成功') toast.success('修改成功')
setToken('') setToken('')
localStorage.removeItem(key.token) localStorage.removeItem(key.token)
@ -42,9 +69,30 @@ const ChangePasswordCard = () => {
} }
}) })
if (isLoadingCheck) {
return ( return (
<> <>
<title> - NapCat WebUI</title> <title> - NapCat WebUI</title>
<div className="flex justify-center items-center h-32">
<div className="text-center">...</div>
</div>
</>
)
}
return (
<>
<title> - NapCat WebUI</title>
{isDefaultToken && (
<div className="mb-4 p-3 bg-warning-50 border border-warning-200 rounded-lg">
<p className="text-warning-700 text-sm">
使
</p>
</div>
)}
{!isDefaultToken && (
<Controller <Controller
control={control} control={control}
name="oldToken" name="oldToken"
@ -57,18 +105,21 @@ const ChangePasswordCard = () => {
/> />
)} )}
/> />
)}
<Controller <Controller
control={control} control={control}
name="newToken" name="newToken"
render={({ field }) => ( render={({ field }) => (
<Input <Input
{...field} {...field}
label="新密码" label={isDefaultToken ? "设置新密码" : "新密码"}
placeholder="请输入新密码" placeholder={isDefaultToken ? "请设置一个安全的新密码" : "请输入新密码"}
type="password" type="password"
/> />
)} )}
/> />
<SaveButtons <SaveButtons
onSubmit={onSubmit} onSubmit={onSubmit}
reset={reset} reset={reset}

View File

@ -25,7 +25,6 @@ export default function WebLoginPage() {
const [tokenValue, setTokenValue] = useState<string>(token || '') const [tokenValue, setTokenValue] = useState<string>(token || '')
const [isLoading, setIsLoading] = useState<boolean>(false) const [isLoading, setIsLoading] = useState<boolean>(false)
const [, setLocalToken] = useLocalStorage<string>(key.token, '') const [, setLocalToken] = useLocalStorage<string>(key.token, '')
const onSubmit = async () => { const onSubmit = async () => {
if (!tokenValue) { if (!tokenValue) {
toast.error('请输入token') toast.error('请输入token')

View File

@ -93,11 +93,16 @@ export const checkHandler: RequestHandler = async (req, res) => {
// 修改密码token // 修改密码token
export const UpdateTokenHandler: RequestHandler = async (req, res) => { export const UpdateTokenHandler: RequestHandler = async (req, res) => {
const { oldToken, newToken } = req.body; const { oldToken, newToken, fromDefault } = req.body;
const authorization = req.headers.authorization; const authorization = req.headers.authorization;
if (isEmpty(oldToken) || isEmpty(newToken)) { if (isEmpty(newToken)) {
return sendError(res, 'oldToken or newToken is empty'); return sendError(res, 'newToken is empty');
}
// 如果不是从默认密码更新,则需要验证旧密码
if (!fromDefault && isEmpty(oldToken)) {
return sendError(res, 'oldToken is required when not updating from default password');
} }
try { try {
@ -108,7 +113,18 @@ export const UpdateTokenHandler: RequestHandler = async (req, res) => {
AuthHelper.revokeCredential(Credential); AuthHelper.revokeCredential(Credential);
} }
if (fromDefault) {
// 从默认密码更新,直接设置新密码
const currentConfig = await WebUiConfig.GetWebUIConfig();
if (!currentConfig.defaultToken) {
return sendError(res, 'Current password is not default password');
}
await WebUiConfig.UpdateWebUIConfig({ token: newToken, defaultToken: false });
} else {
// 正常的密码更新流程
await WebUiConfig.UpdateToken(oldToken, newToken); await WebUiConfig.UpdateToken(oldToken, newToken);
}
return sendSuccess(res, 'Token updated successfully'); return sendSuccess(res, 'Token updated successfully');
} catch (e: any) { } catch (e: any) {
return sendError(res, `Failed to update token: ${e.message}`); return sendError(res, `Failed to update token: ${e.message}`);