feat(webui): 修改token

This commit is contained in:
bietiaop
2025-01-29 22:08:45 +08:00
parent f4a71159fd
commit 4a95b17a47
6 changed files with 315 additions and 239 deletions

View File

@@ -5,7 +5,7 @@ import { IoMdRefresh } from 'react-icons/io'
export interface SaveButtonsProps { export interface SaveButtonsProps {
onSubmit: () => void onSubmit: () => void
reset: () => void reset: () => void
refresh: () => void refresh?: () => void
isSubmitting: boolean isSubmitting: boolean
} }
@@ -27,12 +27,13 @@ const SaveButtons: React.FC<SaveButtonsProps> = ({
</Button> </Button>
<Button <Button
color="primary" color="danger"
isLoading={isSubmitting} isLoading={isSubmitting}
onPress={() => onSubmit()} onPress={() => onSubmit()}
> >
</Button> </Button>
{refresh && (
<Button <Button
isIconOnly isIconOnly
color="secondary" color="secondary"
@@ -42,6 +43,7 @@ const SaveButtons: React.FC<SaveButtonsProps> = ({
> >
<IoMdRefresh size={24} /> <IoMdRefresh size={24} />
</Button> </Button>
)}
</div> </div>
</div> </div>
) )

View File

@@ -24,6 +24,14 @@ export default class WebUIManager {
return data.data.Credential return data.data.Credential
} }
public static async changePassword(oldToken: string, newToken: string) {
const { data } = await serverRequest.post<ServerResponse<boolean>>(
'/auth/update_token',
{ oldToken, newToken }
)
return data.data
}
public static async proxy<T>(url = '') { public static async proxy<T>(url = '') {
const data = await serverRequest.get<ServerResponse<string>>( const data = await serverRequest.get<ServerResponse<string>>(
'/base/proxy?url=' + encodeURIComponent(url) '/base/proxy?url=' + encodeURIComponent(url)

View File

@@ -0,0 +1,81 @@
import { Input } from '@heroui/input'
import { useLocalStorage } from '@uidotdev/usehooks'
import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import { useNavigate } from 'react-router-dom'
import key from '@/const/key'
import SaveButtons from '@/components/button/save_buttons'
import WebUIManager from '@/controllers/webui_manager'
const ChangePasswordCard = () => {
const {
control,
handleSubmit: handleWebuiSubmit,
formState: { isSubmitting },
reset
} = useForm<{
oldToken: string
newToken: string
}>({
defaultValues: {
oldToken: '',
newToken: ''
}
})
const navigate = useNavigate()
const [_, setToken] = useLocalStorage(key.token, '')
const onSubmit = handleWebuiSubmit(async (data) => {
try {
await WebUIManager.changePassword(data.oldToken, data.newToken)
toast.success('修改成功')
setToken('')
localStorage.removeItem(key.token)
navigate('/web_login')
} catch (error) {
const msg = (error as Error).message
toast.error(`修改失败: ${msg}`)
}
})
return (
<>
<title> - NapCat WebUI</title>
<Controller
control={control}
name="oldToken"
render={({ field }) => (
<Input
{...field}
label="旧密码"
placeholder="请输入旧密码"
type="password"
/>
)}
/>
<Controller
control={control}
name="newToken"
render={({ field }) => (
<Input
{...field}
label="新密码"
placeholder="请输入新密码"
type="password"
/>
)}
/>
<SaveButtons
onSubmit={onSubmit}
reset={reset}
isSubmitting={isSubmitting}
/>
</>
)
}
export default ChangePasswordCard

View File

@@ -1,111 +1,27 @@
import { Card, CardBody } from '@heroui/card'
import { Tab, Tabs } from '@heroui/tabs' import { Tab, Tabs } from '@heroui/tabs'
import { useLocalStorage } from '@uidotdev/usehooks'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import { useMediaQuery } from 'react-responsive' import { useMediaQuery } from 'react-responsive'
import key from '@/const/key' import ChangePasswordCard from './change_password'
import PageLoading from '@/components/page_loading'
import useConfig from '@/hooks/use-config'
import useMusic from '@/hooks/use-music'
import OneBotConfigCard from './onebot' import OneBotConfigCard from './onebot'
import WebUIConfigCard from './webui' import WebUIConfigCard from './webui'
export default function ConfigPage() { export interface ConfigPageProps {
const { config, saveConfigWithoutNetwork, refreshConfig } = useConfig() children?: React.ReactNode
const [loading, setLoading] = useState(false)
const {
control: onebotControl,
handleSubmit: handleOnebotSubmit,
formState: { isSubmitting: isOnebotSubmitting },
setValue: setOnebotValue
} = useForm<IConfig['onebot']>({
defaultValues: {
musicSignUrl: '',
enableLocalFile2Url: false,
parseMultMsg: false
} }
})
const { const ConfingPageItem: React.FC<ConfigPageProps> = ({ children }) => {
control: webuiControl, return (
handleSubmit: handleWebuiSubmit, <Card className="bg-opacity-50 backdrop-blur-sm">
formState: { isSubmitting: isWebuiSubmitting }, <CardBody className="items-center py-5">
setValue: setWebuiValue <div className="w-96 max-w-full flex flex-col gap-2">{children}</div>
} = useForm<IConfig['webui']>({ </CardBody>
defaultValues: { </Card>
background: '',
musicListID: '',
customIcons: {}
}
})
const isMediumUp = useMediaQuery({ minWidth: 768 })
const [b64img, setB64img] = useLocalStorage(key.backgroundImage, '')
const [customIcons, setCustomIcons] = useLocalStorage<Record<string, string>>(
key.customIcons,
{}
) )
const { setListId, listId } = useMusic()
const resetOneBot = () => {
setOnebotValue('musicSignUrl', config.musicSignUrl)
setOnebotValue('enableLocalFile2Url', config.enableLocalFile2Url)
setOnebotValue('parseMultMsg', config.parseMultMsg)
} }
const resetWebUI = () => { export default function ConfigPage() {
setWebuiValue('musicListID', listId) const isMediumUp = useMediaQuery({ minWidth: 768 })
setWebuiValue('customIcons', customIcons)
setWebuiValue('background', b64img)
}
const onOneBotSubmit = handleOnebotSubmit((data) => {
try {
saveConfigWithoutNetwork(data)
toast.success('保存成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`保存失败: ${msg}`)
}
})
const onWebuiSubmit = handleWebuiSubmit((data) => {
try {
setListId(data.musicListID)
setCustomIcons(data.customIcons)
setB64img(data.background)
toast.success('保存成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`保存失败: ${msg}`)
}
})
const onRefresh = async (shotTip = true) => {
try {
setLoading(true)
await refreshConfig()
if (shotTip) toast.success('刷新成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`刷新失败: ${msg}`)
} finally {
setLoading(false)
}
}
useEffect(() => {
resetOneBot()
resetWebUI()
}, [config])
useEffect(() => {
onRefresh(false)
}, [])
return ( return (
<section className="w-[1000px] max-w-full md:mx-auto gap-4 py-8 px-2 md:py-10"> <section className="w-[1000px] max-w-full md:mx-auto gap-4 py-8 px-2 md:py-10">
@@ -122,23 +38,20 @@ export default function ConfigPage() {
}} }}
> >
<Tab title="OneBot配置" key="onebot"> <Tab title="OneBot配置" key="onebot">
<PageLoading loading={loading} /> <ConfingPageItem>
<OneBotConfigCard <OneBotConfigCard />
isSubmitting={isOnebotSubmitting} </ConfingPageItem>
onRefresh={onRefresh}
onSubmit={onOneBotSubmit}
control={onebotControl}
reset={resetOneBot}
/>
</Tab> </Tab>
<Tab title="WebUI配置" key="webui"> <Tab title="WebUI配置" key="webui">
<WebUIConfigCard <ConfingPageItem>
isSubmitting={isWebuiSubmitting} <WebUIConfigCard />
onRefresh={onRefresh} </ConfingPageItem>
onSubmit={onWebuiSubmit} </Tab>
control={webuiControl}
reset={resetWebUI} <Tab title="修改密码" key="token">
/> <ConfingPageItem>
<ChangePasswordCard />
</ConfingPageItem>
</Tab> </Tab>
</Tabs> </Tabs>
</section> </section>

View File

@@ -1,26 +1,71 @@
import { Card, CardBody } from '@heroui/card'
import { Input } from '@heroui/input' import { Input } from '@heroui/input'
import { Controller } from 'react-hook-form' import { useEffect, useState } from 'react'
import type { Control } from 'react-hook-form' import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import SaveButtons from '@/components/button/save_buttons' import SaveButtons from '@/components/button/save_buttons'
import PageLoading from '@/components/page_loading'
import SwitchCard from '@/components/switch_card' import SwitchCard from '@/components/switch_card'
export interface OneBotConfigCardProps { import useConfig from '@/hooks/use-config'
control: Control<IConfig['onebot']>
onSubmit: () => void const OneBotConfigCard = () => {
reset: () => void const { config, saveConfigWithoutNetwork, refreshConfig } = useConfig()
isSubmitting: boolean const [loading, setLoading] = useState(false)
onRefresh: () => void const {
control,
handleSubmit: handleOnebotSubmit,
formState: { isSubmitting },
setValue: setOnebotValue
} = useForm<IConfig['onebot']>({
defaultValues: {
musicSignUrl: '',
enableLocalFile2Url: false,
parseMultMsg: false
} }
const OneBotConfigCard: React.FC<OneBotConfigCardProps> = (props) => { })
const { control, onSubmit, reset, isSubmitting, onRefresh } = props const reset = () => {
setOnebotValue('musicSignUrl', config.musicSignUrl)
setOnebotValue('enableLocalFile2Url', config.enableLocalFile2Url)
setOnebotValue('parseMultMsg', config.parseMultMsg)
}
const onSubmit = handleOnebotSubmit((data) => {
try {
saveConfigWithoutNetwork(data)
toast.success('保存成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`保存失败: ${msg}`)
}
})
const onRefresh = async (shotTip = true) => {
try {
setLoading(true)
await refreshConfig()
if (shotTip) toast.success('刷新成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`刷新失败: ${msg}`)
} finally {
setLoading(false)
}
}
useEffect(() => {
reset()
}, [config])
useEffect(() => {
onRefresh(false)
}, [])
if (loading) return <PageLoading loading={true} />
return ( return (
<> <>
<title>OneBot配置 - NapCat WebUI</title> <title>OneBot配置 - NapCat WebUI</title>
<Card className="bg-opacity-50 backdrop-blur-sm">
<CardBody className="items-center py-5">
<div className="w-96 max-w-full flex flex-col gap-2">
<Controller <Controller
control={control} control={control}
name="musicSignUrl" name="musicSignUrl"
@@ -60,9 +105,6 @@ const OneBotConfigCard: React.FC<OneBotConfigCardProps> = (props) => {
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
refresh={onRefresh} refresh={onRefresh}
/> />
</div>
</CardBody>
</Card>
</> </>
) )
} }

View File

@@ -1,28 +1,64 @@
import { Card, CardBody } from '@heroui/card'
import { Input } from '@heroui/input' import { Input } from '@heroui/input'
import { Controller } from 'react-hook-form' import { useLocalStorage } from '@uidotdev/usehooks'
import type { Control } from 'react-hook-form' import { useEffect } from 'react'
import { Controller, useForm } from 'react-hook-form'
import toast from 'react-hot-toast'
import key from '@/const/key'
import SaveButtons from '@/components/button/save_buttons' import SaveButtons from '@/components/button/save_buttons'
import ImageInput from '@/components/input/image_input' import ImageInput from '@/components/input/image_input'
import useMusic from '@/hooks/use-music'
import { siteConfig } from '@/config/site' import { siteConfig } from '@/config/site'
export interface WebUIConfigCardProps { const WebUIConfigCard = () => {
control: Control<IConfig['webui']> const {
onSubmit: () => void control,
reset: () => void handleSubmit: handleWebuiSubmit,
isSubmitting: boolean formState: { isSubmitting },
onRefresh: () => void setValue: setWebuiValue
} = useForm<IConfig['webui']>({
defaultValues: {
background: '',
musicListID: '',
customIcons: {}
} }
const WebUIConfigCard: React.FC<WebUIConfigCardProps> = (props) => { })
const { control, onSubmit, reset, isSubmitting, onRefresh } = props
const [b64img, setB64img] = useLocalStorage(key.backgroundImage, '')
const [customIcons, setCustomIcons] = useLocalStorage<Record<string, string>>(
key.customIcons,
{}
)
const { setListId, listId } = useMusic()
const reset = () => {
setWebuiValue('musicListID', listId)
setWebuiValue('customIcons', customIcons)
setWebuiValue('background', b64img)
}
const onSubmit = handleWebuiSubmit((data) => {
try {
setListId(data.musicListID)
setCustomIcons(data.customIcons)
setB64img(data.background)
toast.success('保存成功')
} catch (error) {
const msg = (error as Error).message
toast.error(`保存失败: ${msg}`)
}
})
useEffect(() => {
reset()
}, [listId, customIcons, b64img])
return ( return (
<> <>
<title>WebUI配置 - NapCat WebUI</title> <title>WebUI配置 - NapCat WebUI</title>
<Card className="bg-opacity-50 backdrop-blur-sm">
<CardBody className="items-center py-5">
<div className="w-96 max-w-full flex flex-col gap-2">
<Controller <Controller
control={control} control={control}
name="musicListID" name="musicListID"
@@ -49,9 +85,7 @@ const WebUIConfigCard: React.FC<WebUIConfigCardProps> = (props) => {
key={item.label} key={item.label}
control={control} control={control}
name={`customIcons.${item.label}`} name={`customIcons.${item.label}`}
render={({ field }) => ( render={({ field }) => <ImageInput {...field} label={item.label} />}
<ImageInput {...field} label={item.label} />
)}
/> />
))} ))}
</div> </div>
@@ -59,11 +93,7 @@ const WebUIConfigCard: React.FC<WebUIConfigCardProps> = (props) => {
onSubmit={onSubmit} onSubmit={onSubmit}
reset={reset} reset={reset}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
refresh={onRefresh}
/> />
</div>
</CardBody>
</Card>
</> </>
) )
} }