feat: Display registration token in webserver dashboard (#863)

* feat: display registration token

* Update page.tsx

---------

Co-authored-by: Meng Zhang <meng@tabbyml.com>
r0.6
aliang 2023-11-23 08:58:21 +08:00 committed by GitHub
parent f1e82d62e8
commit 24b14888ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 20 deletions

View File

@ -16,6 +16,9 @@ import { PropsWithChildren, useEffect, useState } from 'react'
import WorkerCard from './components/worker-card'
import { useWorkers } from '@/lib/hooks/use-workers'
import { WorkerKind } from '@/lib/gql/generates/graphql'
import { useGraphQL } from '@/lib/hooks/use-graphql'
import { getRegistrationTokenDocument } from '@/lib/gql/request-documents'
import { CopyButton } from '@/components/copy-button'
const COMMUNITY_DIALOG_SHOWN_KEY = 'community-dialog-shown'
@ -75,6 +78,9 @@ function toBadgeString(str: string) {
function MainPanel() {
const { data: healthInfo } = useHealth()
const workers = useWorkers(healthInfo)
const { data: registrationTokenRes } = useGraphQL(
getRegistrationTokenDocument
)
if (!healthInfo) return
@ -100,6 +106,16 @@ function MainPanel() {
<div className="mt-4 rounded-lg bg-zinc-100 p-4 dark:bg-zinc-800">
<span className="font-bold">Workers</span>
{!!registrationTokenRes?.registrationToken && (
<div className="flex items-center gap-1">
Registeration token: <span className="text-sm rounded-lg text-red-600">
{registrationTokenRes.registrationToken}
</span>
<CopyButton value={registrationTokenRes.registrationToken} />
</div>
)}
<div className="mt-4 flex flex-col gap-4 lg:flex-row lg:flex-wrap">
{!!workers?.[WorkerKind.Completion] && (
<>

View File

@ -1,3 +1,5 @@
'use client'
import React from 'react'
import { Button, ButtonProps } from '@/components/ui/button'
import { IconCheck, IconTrash } from '@/components/ui/icons'

View File

@ -3,16 +3,10 @@
import { type Message } from 'ai'
import { Button } from '@/components/ui/button'
import {
IconCheck,
IconCopy,
IconEdit,
IconRefresh,
IconTrash
} from '@/components/ui/icons'
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
import { IconEdit, IconRefresh, IconTrash } from '@/components/ui/icons'
import { cn } from '@/lib/utils'
import { MessageActionType } from '@/lib/types'
import { CopyButton } from '@/components/copy-button'
interface ChatMessageActionsProps extends React.ComponentProps<'div'> {
message: Message
@ -25,13 +19,6 @@ export function ChatMessageActions({
handleMessageAction,
...props
}: ChatMessageActionsProps) {
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
const onCopy = () => {
if (isCopied) return
copyToClipboard(message.content)
}
return (
<div
className={cn(
@ -67,10 +54,7 @@ export function ChatMessageActions({
<IconTrash />
<span className="sr-only">Delete message</span>
</Button>
<Button variant="ghost" size="icon" onClick={onCopy}>
{isCopied ? <IconCheck /> : <IconCopy />}
<span className="sr-only">Copy message</span>
</Button>
<CopyButton value={message.content} />
</div>
)
}

35
ee/tabby-ui/components/copy-button.tsx vendored Normal file
View File

@ -0,0 +1,35 @@
'use client'
import * as React from 'react'
import { Button, type ButtonProps } from '@/components/ui/button'
import { IconCheck, IconCopy } from './ui/icons'
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
interface CopyButtonProps extends ButtonProps {
value: string
}
export function CopyButton({ className, value, ...props }: CopyButtonProps) {
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
const onCopy = () => {
if (isCopied) return
copyToClipboard(value)
}
if (!value) return null
return (
<Button
variant="ghost"
size="icon"
className={className}
onClick={onCopy}
{...props}
>
{isCopied ? <IconCheck /> : <IconCopy />}
<span className="sr-only">Copy</span>
</Button>
)
}

View File

@ -14,7 +14,9 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
*/
const documents = {
'\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n':
types.GetWorkersDocument
types.GetWorkersDocument,
'\n query GetRegistrationToken {\n registrationToken\n }\n':
types.GetRegistrationTokenDocument
}
/**
@ -37,6 +39,12 @@ export function graphql(source: string): unknown
export function graphql(
source: '\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n'
): (typeof documents)['\n query GetWorkers {\n workers {\n kind\n name\n addr\n device\n arch\n cpuInfo\n cpuCount\n cudaDevices\n }\n }\n']
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(
source: '\n query GetRegistrationToken {\n registrationToken\n }\n'
): (typeof documents)['\n query GetRegistrationToken {\n registrationToken\n }\n']
export function graphql(source: string) {
return (documents as any)[source] ?? {}

View File

@ -74,6 +74,13 @@ export type GetWorkersQuery = {
}>
}
export type GetRegistrationTokenQueryVariables = Exact<{ [key: string]: never }>
export type GetRegistrationTokenQuery = {
__typename?: 'Query'
registrationToken: string
}
export const GetWorkersDocument = {
kind: 'Document',
definitions: [
@ -106,3 +113,22 @@ export const GetWorkersDocument = {
}
]
} as unknown as DocumentNode<GetWorkersQuery, GetWorkersQueryVariables>
export const GetRegistrationTokenDocument = {
kind: 'Document',
definitions: [
{
kind: 'OperationDefinition',
operation: 'query',
name: { kind: 'Name', value: 'GetRegistrationToken' },
selectionSet: {
kind: 'SelectionSet',
selections: [
{ kind: 'Field', name: { kind: 'Name', value: 'registrationToken' } }
]
}
}
]
} as unknown as DocumentNode<
GetRegistrationTokenQuery,
GetRegistrationTokenQueryVariables
>

View File

@ -14,3 +14,9 @@ export const getAllWorkersDocument = graphql(/* GraphQL */ `
}
}
`)
export const getRegistrationTokenDocument = graphql(/* GraphQL */ `
query GetRegistrationToken {
registrationToken
}
`)