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
parent
f1e82d62e8
commit
24b14888ce
|
|
@ -16,6 +16,9 @@ import { PropsWithChildren, useEffect, useState } from 'react'
|
||||||
import WorkerCard from './components/worker-card'
|
import WorkerCard from './components/worker-card'
|
||||||
import { useWorkers } from '@/lib/hooks/use-workers'
|
import { useWorkers } from '@/lib/hooks/use-workers'
|
||||||
import { WorkerKind } from '@/lib/gql/generates/graphql'
|
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'
|
const COMMUNITY_DIALOG_SHOWN_KEY = 'community-dialog-shown'
|
||||||
|
|
||||||
|
|
@ -75,6 +78,9 @@ function toBadgeString(str: string) {
|
||||||
function MainPanel() {
|
function MainPanel() {
|
||||||
const { data: healthInfo } = useHealth()
|
const { data: healthInfo } = useHealth()
|
||||||
const workers = useWorkers(healthInfo)
|
const workers = useWorkers(healthInfo)
|
||||||
|
const { data: registrationTokenRes } = useGraphQL(
|
||||||
|
getRegistrationTokenDocument
|
||||||
|
)
|
||||||
|
|
||||||
if (!healthInfo) return
|
if (!healthInfo) return
|
||||||
|
|
||||||
|
|
@ -100,6 +106,16 @@ function MainPanel() {
|
||||||
|
|
||||||
<div className="mt-4 rounded-lg bg-zinc-100 p-4 dark:bg-zinc-800">
|
<div className="mt-4 rounded-lg bg-zinc-100 p-4 dark:bg-zinc-800">
|
||||||
<span className="font-bold">Workers</span>
|
<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">
|
<div className="mt-4 flex flex-col gap-4 lg:flex-row lg:flex-wrap">
|
||||||
{!!workers?.[WorkerKind.Completion] && (
|
{!!workers?.[WorkerKind.Completion] && (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
'use client'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Button, ButtonProps } from '@/components/ui/button'
|
import { Button, ButtonProps } from '@/components/ui/button'
|
||||||
import { IconCheck, IconTrash } from '@/components/ui/icons'
|
import { IconCheck, IconTrash } from '@/components/ui/icons'
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,10 @@
|
||||||
import { type Message } from 'ai'
|
import { type Message } from 'ai'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import {
|
import { IconEdit, IconRefresh, IconTrash } from '@/components/ui/icons'
|
||||||
IconCheck,
|
|
||||||
IconCopy,
|
|
||||||
IconEdit,
|
|
||||||
IconRefresh,
|
|
||||||
IconTrash
|
|
||||||
} from '@/components/ui/icons'
|
|
||||||
import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
|
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { MessageActionType } from '@/lib/types'
|
import { MessageActionType } from '@/lib/types'
|
||||||
|
import { CopyButton } from '@/components/copy-button'
|
||||||
|
|
||||||
interface ChatMessageActionsProps extends React.ComponentProps<'div'> {
|
interface ChatMessageActionsProps extends React.ComponentProps<'div'> {
|
||||||
message: Message
|
message: Message
|
||||||
|
|
@ -25,13 +19,6 @@ export function ChatMessageActions({
|
||||||
handleMessageAction,
|
handleMessageAction,
|
||||||
...props
|
...props
|
||||||
}: ChatMessageActionsProps) {
|
}: ChatMessageActionsProps) {
|
||||||
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
|
|
||||||
|
|
||||||
const onCopy = () => {
|
|
||||||
if (isCopied) return
|
|
||||||
copyToClipboard(message.content)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -67,10 +54,7 @@ export function ChatMessageActions({
|
||||||
<IconTrash />
|
<IconTrash />
|
||||||
<span className="sr-only">Delete message</span>
|
<span className="sr-only">Delete message</span>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="icon" onClick={onCopy}>
|
<CopyButton value={message.content} />
|
||||||
{isCopied ? <IconCheck /> : <IconCopy />}
|
|
||||||
<span className="sr-only">Copy message</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,9 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
|
||||||
*/
|
*/
|
||||||
const documents = {
|
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':
|
'\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(
|
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'
|
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']
|
): (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) {
|
export function graphql(source: string) {
|
||||||
return (documents as any)[source] ?? {}
|
return (documents as any)[source] ?? {}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,13 @@ export type GetWorkersQuery = {
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetRegistrationTokenQueryVariables = Exact<{ [key: string]: never }>
|
||||||
|
|
||||||
|
export type GetRegistrationTokenQuery = {
|
||||||
|
__typename?: 'Query'
|
||||||
|
registrationToken: string
|
||||||
|
}
|
||||||
|
|
||||||
export const GetWorkersDocument = {
|
export const GetWorkersDocument = {
|
||||||
kind: 'Document',
|
kind: 'Document',
|
||||||
definitions: [
|
definitions: [
|
||||||
|
|
@ -106,3 +113,22 @@ export const GetWorkersDocument = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} as unknown as DocumentNode<GetWorkersQuery, GetWorkersQueryVariables>
|
} 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
|
||||||
|
>
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,9 @@ export const getAllWorkersDocument = graphql(/* GraphQL */ `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
export const getRegistrationTokenDocument = graphql(/* GraphQL */ `
|
||||||
|
query GetRegistrationToken {
|
||||||
|
registrationToken
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue