feat(tabby-ui): support user auth token reset (#1003)

r0.7
Meng Zhang 2023-12-10 12:16:31 +08:00 committed by GitHub
parent 03c418340a
commit ae4dc5f8d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 22 deletions

View File

@ -6,7 +6,8 @@ import { useHealth } from '@/lib/hooks/use-health'
import { useWorkers } from '@/lib/hooks/use-workers' import { useWorkers } from '@/lib/hooks/use-workers'
import { useAuthenticatedGraphQLQuery, useGraphQLForm } from '@/lib/tabby/gql' import { useAuthenticatedGraphQLQuery, useGraphQLForm } from '@/lib/tabby/gql'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { IconRefresh } from '@/components/ui/icons' import { IconRotate } from '@/components/ui/icons'
import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator' import { Separator } from '@/components/ui/separator'
import { CopyButton } from '@/components/copy-button' import { CopyButton } from '@/components/copy-button'
@ -66,16 +67,17 @@ export default function Workers() {
{!!registrationTokenRes?.registrationToken && ( {!!registrationTokenRes?.registrationToken && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
Registration token: Registration token:
<code className="rounded-lg text-sm text-red-600"> <Input
{registrationTokenRes.registrationToken} className="max-w-[320px] font-mono text-red-600"
</code> value={registrationTokenRes.registrationToken}
/>
<Button <Button
title="Reset" title="Rotate"
size="icon" size="icon"
variant="hover-destructive" variant="hover-destructive"
onClick={() => resetRegistrationToken()} onClick={() => resetRegistrationToken()}
> >
<IconRefresh /> <IconRotate />
</Button> </Button>
<CopyButton value={registrationTokenRes.registrationToken} /> <CopyButton value={registrationTokenRes.registrationToken} />
</div> </div>

View File

@ -4,14 +4,17 @@ import { useEffect, useState } from 'react'
import { graphql } from '@/lib/gql/generates' import { graphql } from '@/lib/gql/generates'
import { useHealth } from '@/lib/hooks/use-health' import { useHealth } from '@/lib/hooks/use-health'
import { useAuthenticatedGraphQLQuery } from '@/lib/tabby/gql' import { useAuthenticatedGraphQLQuery, useGraphQLForm } from '@/lib/tabby/gql'
import { Button } from '@/components/ui/button'
import { import {
CardContent, CardContent,
CardFooter, CardFooter,
CardHeader, CardHeader,
CardTitle CardTitle
} from '@/components/ui/card' } from '@/components/ui/card'
import { IconRotate } from '@/components/ui/icons'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { CopyButton } from '@/components/copy-button' import { CopyButton } from '@/components/copy-button'
import SlackDialog from '@/components/slack-dialog' import SlackDialog from '@/components/slack-dialog'
@ -32,14 +35,27 @@ const meQuery = graphql(/* GraphQL */ `
} }
`) `)
const resetUserAuthTokenDocument = graphql(/* GraphQL */ `
mutation ResetUserAuthToken {
resetUserAuthToken
}
`)
function MainPanel() { function MainPanel() {
const { data: healthInfo } = useHealth() const { data: healthInfo } = useHealth()
const { data } = useAuthenticatedGraphQLQuery(meQuery) const { data, mutate } = useAuthenticatedGraphQLQuery(meQuery)
const [origin, setOrigin] = useState('') const [origin, setOrigin] = useState('')
useEffect(() => { useEffect(() => {
setOrigin(new URL(window.location.href).origin) setOrigin(new URL(window.location.href).origin)
}, []) }, [])
const { onSubmit: resetUserAuthToken } = useGraphQLForm(
resetUserAuthTokenDocument,
{
onSuccess: () => mutate()
}
)
if (!healthInfo || !data) return if (!healthInfo || !data) return
return ( return (
@ -47,21 +63,28 @@ function MainPanel() {
<CardHeader> <CardHeader>
<CardTitle>Getting Started</CardTitle> <CardTitle>Getting Started</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex max-w-[420px] flex-col gap-2"> <CardContent className="flex flex-col gap-4">
<span className="flex items-center justify-between gap-2"> <Label>Endpoint URL</Label>
<span>Endpoint URL</span> <span className="flex items-center gap-1">
<span className="flex items-center"> <Input value={origin} className="max-w-[320px]" />
<Input value={origin} /> <CopyButton value={origin} />
<CopyButton value={origin} />
</span>
</span> </span>
<span className="flex items-center justify-between gap-2"> <Label>Token</Label>
<span>Token</span> <span className="flex items-center gap-1">
<span className="flex items-center"> <Input
<Input value={data.me.authToken} /> className="max-w-[320px] font-mono text-red-600"
<CopyButton value={data.me.authToken} /> value={data.me.authToken}
</span> />
<Button
title="Rotate"
size="icon"
variant="hover-destructive"
onClick={() => resetUserAuthToken()}
>
<IconRotate />
</Button>
<CopyButton value={data.me.authToken} />
</span> </span>
</CardContent> </CardContent>
<CardFooter> <CardFooter>

View File

@ -660,6 +660,27 @@ function IconNetwork({ className, ...props }: React.ComponentProps<'svg'>) {
) )
} }
function IconRotate({ className, ...props }: React.ComponentProps<'svg'>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={cn('h-4 w-4', className)}
{...props}
>
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
<path d="M3 3v5h5" />
</svg>
)
}
export { export {
IconEdit, IconEdit,
IconNextChat, IconNextChat,
@ -694,5 +715,6 @@ export {
IconLogout, IconLogout,
IconUnlock, IconUnlock,
IconHome, IconHome,
IconNetwork IconNetwork,
IconRotate
} }