mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
feat(webui): 修改token
This commit is contained in:
@@ -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>
|
||||||
)
|
)
|
||||||
|
@@ -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)
|
||||||
|
81
napcat.webui/src/pages/dashboard/config/change_password.tsx
Normal file
81
napcat.webui/src/pages/dashboard/config/change_password.tsx
Normal 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
|
@@ -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>
|
||||||
|
@@ -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>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user