dify/web/service/use-plugins.ts

418 lines
12 KiB
TypeScript
Raw Normal View History

2024-11-12 17:58:14 +08:00
import { useCallback, useState } from 'react'
2024-11-08 18:21:39 +08:00
import type {
DebugInfo as DebugInfoTypes,
2024-11-13 15:48:06 +08:00
Dependency,
2024-11-19 16:55:36 +08:00
GitHubItemAndMarketPlaceDependency,
2024-11-08 18:25:15 +08:00
InstallPackageResponse,
2024-11-08 18:21:39 +08:00
InstalledPluginListResponse,
2024-11-19 16:55:36 +08:00
PackageDependency,
2024-11-08 18:21:39 +08:00
Permissions,
2024-11-20 16:20:23 +08:00
Plugin,
PluginDetail,
2024-11-12 17:58:14 +08:00
PluginTask,
2024-11-20 16:20:23 +08:00
PluginsFromMarketplaceByInfoResponse,
2024-11-08 18:21:39 +08:00
PluginsFromMarketplaceResponse,
2024-11-28 18:26:07 +08:00
VersionInfo,
2024-11-17 12:57:34 +08:00
VersionListResponse,
2024-11-14 16:54:23 +08:00
uploadGitHubResponse,
2024-11-08 18:21:39 +08:00
} from '@/app/components/plugins/types'
2024-11-15 15:50:14 +08:00
import { TaskStatus } from '@/app/components/plugins/types'
2024-11-08 18:21:39 +08:00
import type {
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types'
2024-11-17 12:57:34 +08:00
import { get, getMarketplace, post, postMarketplace } from './base'
import {
2024-11-08 16:11:50 +08:00
useMutation,
useQuery,
2024-11-08 18:21:39 +08:00
useQueryClient,
} from '@tanstack/react-query'
import { useInvalidateAllBuiltInTools } from './use-tools'
const NAME_SPACE = 'plugins'
const useInstalledPluginListKey = [NAME_SPACE, 'installedPluginList']
export const useCheckInstalled = ({
pluginIds,
enabled,
}: {
pluginIds: string[],
enabled: boolean
}) => {
return useQuery<{ plugins: PluginDetail[] }>({
queryKey: [NAME_SPACE, 'checkInstalled'],
queryFn: () => post<{ plugins: PluginDetail[] }>('/workspaces/current/plugin/list/installations/ids', {
body: {
plugin_ids: pluginIds,
},
}),
enabled,
staleTime: 0, // always fresh
})
}
2024-11-27 16:14:15 +08:00
export const useInstalledPluginList = (disable?: boolean) => {
return useQuery<InstalledPluginListResponse>({
queryKey: useInstalledPluginListKey,
queryFn: () => get<InstalledPluginListResponse>('/workspaces/current/plugin/list'),
2024-11-27 16:14:15 +08:00
enabled: !disable,
initialData: !disable ? undefined : { plugins: [] },
})
}
export const useInvalidateInstalledPluginList = () => {
const queryClient = useQueryClient()
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
return () => {
queryClient.invalidateQueries(
{
queryKey: useInstalledPluginListKey,
})
invalidateAllBuiltInTools()
}
}
2024-11-08 15:10:06 +08:00
2024-11-08 18:25:15 +08:00
export const useInstallPackageFromMarketPlace = () => {
return useMutation({
mutationFn: (uniqueIdentifier: string) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', { body: { plugin_unique_identifiers: [uniqueIdentifier] } })
},
})
}
export const useUpdatePackageFromMarketPlace = () => {
return useMutation({
mutationFn: (body: object) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/upgrade/marketplace', {
body,
})
},
})
}
2024-11-17 12:57:34 +08:00
export const useVersionListOfPlugin = (pluginID: string) => {
return useQuery<{ data: VersionListResponse }>({
queryKey: [NAME_SPACE, 'versions', pluginID],
queryFn: () => getMarketplace<{ data: VersionListResponse }>(`/plugins/${pluginID}/versions`, { params: { page: 1, page_size: 100 } }),
})
}
export const useInvalidateVersionListOfPlugin = () => {
const queryClient = useQueryClient()
return (pluginID: string) => {
queryClient.invalidateQueries({ queryKey: [NAME_SPACE, 'versions', pluginID] })
}
}
export const useInstallPackageFromLocal = () => {
return useMutation({
mutationFn: (uniqueIdentifier: string) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/install/pkg', {
body: { plugin_unique_identifiers: [uniqueIdentifier] },
})
},
})
}
2024-11-14 18:26:16 +08:00
export const useInstallPackageFromGitHub = () => {
return useMutation({
mutationFn: ({ repoUrl, selectedVersion, selectedPackage, uniqueIdentifier }: {
repoUrl: string
selectedVersion: string
selectedPackage: string
uniqueIdentifier: string
}) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/install/github', {
body: {
repo: repoUrl,
version: selectedVersion,
package: selectedPackage,
plugin_unique_identifier: uniqueIdentifier,
},
})
},
})
}
2024-11-14 16:54:23 +08:00
export const useUploadGitHub = (payload: {
repo: string
version: string
package: string
}) => {
return useQuery({
queryKey: [NAME_SPACE, 'uploadGitHub', payload],
queryFn: () => post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', {
body: payload,
}),
retry: 0,
2024-11-14 16:54:23 +08:00
})
}
2024-11-28 18:26:07 +08:00
export const useInstallOrUpdate = ({
2024-11-15 12:07:20 +08:00
onSuccess,
}: {
2024-11-15 13:09:19 +08:00
onSuccess?: (res: { success: boolean }[]) => void
2024-11-15 12:07:20 +08:00
}) => {
2024-11-28 18:26:07 +08:00
const { mutateAsync: updatePackageFromMarketPlace } = useUpdatePackageFromMarketPlace()
2024-11-15 12:07:20 +08:00
return useMutation({
2024-11-20 16:20:23 +08:00
mutationFn: (data: {
payload: Dependency[],
plugin: Plugin[],
2024-11-28 18:26:07 +08:00
installedInfo: Record<string, VersionInfo>
2024-11-20 16:20:23 +08:00
}) => {
2024-11-28 18:26:07 +08:00
const { payload, plugin, installedInfo } = data
2024-11-20 16:20:23 +08:00
return Promise.all(payload.map(async (item, i) => {
2024-11-15 12:07:20 +08:00
try {
const orgAndName = `${plugin[i]?.org || plugin[i]?.author}/${plugin[i]?.name}`
2024-11-28 18:26:07 +08:00
const installedPayload = installedInfo[orgAndName]
const isInstalled = !!installedPayload
let uniqueIdentifier = ''
2024-11-15 12:07:20 +08:00
if (item.type === 'github') {
2024-11-19 16:55:36 +08:00
const data = item as GitHubItemAndMarketPlaceDependency
2024-11-20 16:32:06 +08:00
// From local bundle don't have data.value.github_plugin_unique_identifier
if (!data.value.github_plugin_unique_identifier) {
const { unique_identifier } = await post<uploadGitHubResponse>('/workspaces/current/plugin/upload/github', {
body: {
repo: data.value.repo!,
version: data.value.release! || data.value.version!,
package: data.value.packages! || data.value.package!,
},
})
2024-11-28 18:26:07 +08:00
uniqueIdentifier = data.value.github_plugin_unique_identifier! || unique_identifier
// has the same version, but not installed
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
return {
success: true,
}
}
}
if (!isInstalled) {
await post<InstallPackageResponse>('/workspaces/current/plugin/install/github', {
body: {
repo: data.value.repo!,
version: data.value.release! || data.value.version!,
package: data.value.packages! || data.value.package!,
plugin_unique_identifier: uniqueIdentifier,
},
})
2024-11-20 16:32:06 +08:00
}
2024-11-19 16:55:36 +08:00
}
if (item.type === 'marketplace') {
const data = item as GitHubItemAndMarketPlaceDependency
2024-11-28 18:26:07 +08:00
uniqueIdentifier = data.value.plugin_unique_identifier! || plugin[i]?.plugin_id
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
return {
success: true,
}
}
if (!isInstalled) {
await post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', {
body: {
plugin_unique_identifiers: [uniqueIdentifier],
},
})
}
2024-11-19 16:55:36 +08:00
}
if (item.type === 'package') {
const data = item as PackageDependency
2024-11-28 18:26:07 +08:00
uniqueIdentifier = data.value.unique_identifier
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
return {
success: true,
}
}
if (!isInstalled) {
await post<InstallPackageResponse>('/workspaces/current/plugin/install/pkg', {
body: {
plugin_unique_identifiers: [uniqueIdentifier],
},
})
}
}
if (isInstalled) {
await updatePackageFromMarketPlace({
original_plugin_unique_identifier: installedPayload?.uniqueIdentifier,
new_plugin_unique_identifier: uniqueIdentifier,
2024-11-15 12:07:20 +08:00
})
}
return ({ success: true })
}
2024-11-15 14:40:04 +08:00
// eslint-disable-next-line unused-imports/no-unused-vars
2024-11-15 12:07:20 +08:00
catch (e) {
return Promise.resolve({ success: false })
}
}))
},
onSuccess,
})
}
2024-11-08 15:10:06 +08:00
export const useDebugKey = () => {
return useQuery({
queryKey: [NAME_SPACE, 'debugKey'],
queryFn: () => get<DebugInfoTypes>('/workspaces/current/plugin/debugging-key'),
})
}
2024-11-08 16:11:50 +08:00
const usePermissionsKey = [NAME_SPACE, 'permissions']
export const usePermissions = () => {
return useQuery({
queryKey: usePermissionsKey,
queryFn: () => get<Permissions>('/workspaces/current/plugin/permission/fetch'),
})
}
export const useInvalidatePermissions = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries(
{
queryKey: usePermissionsKey,
})
}
}
export const useMutationPermissions = ({
onSuccess,
}: {
onSuccess?: () => void
}) => {
return useMutation({
mutationFn: (payload: Permissions) => {
return post('/workspaces/current/plugin/permission/change', { body: payload })
},
onSuccess,
})
}
2024-11-08 18:21:39 +08:00
export const useMutationPluginsFromMarketplace = () => {
return useMutation({
mutationFn: (pluginsSearchParams: PluginsSearchParams) => {
const {
query,
sortBy,
sortOrder,
category,
tags,
2024-11-27 16:14:15 +08:00
exclude,
2024-12-03 14:34:23 +08:00
type,
2024-12-03 18:02:57 +08:00
page = 1,
2024-12-05 14:54:04 +08:00
pageSize = 20,
2024-11-08 18:21:39 +08:00
} = pluginsSearchParams
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
body: {
2024-12-03 18:02:57 +08:00
page,
2024-12-05 14:54:04 +08:00
page_size: pageSize,
2024-11-08 18:21:39 +08:00
query,
sort_by: sortBy,
sort_order: sortOrder,
category: category !== 'all' ? category : '',
tags,
2024-11-27 16:14:15 +08:00
exclude,
2024-12-03 14:34:23 +08:00
type,
2024-11-08 18:21:39 +08:00
},
})
},
})
}
2024-11-12 17:58:14 +08:00
2024-11-14 16:54:23 +08:00
export const useFetchPluginsInMarketPlaceByIds = (unique_identifiers: string[]) => {
return useQuery({
queryKey: [NAME_SPACE, 'fetchPluginsInMarketPlaceByIds', unique_identifiers],
queryFn: () => postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/identifier/batch', {
body: {
unique_identifiers,
},
}),
2024-11-20 16:20:23 +08:00
enabled: unique_identifiers?.filter(i => !!i).length > 0,
retry: 0,
})
}
export const useFetchPluginsInMarketPlaceByInfo = (infos: Record<string, any>[]) => {
return useQuery({
queryKey: [NAME_SPACE, 'fetchPluginsInMarketPlaceByInfo', infos],
queryFn: () => postMarketplace<{ data: PluginsFromMarketplaceByInfoResponse }>('/plugins/versions/batch', {
2024-11-20 16:20:23 +08:00
body: {
plugin_tuples: infos.map(info => ({
org: info.organization,
name: info.plugin,
version: info.version,
})),
},
}),
enabled: infos?.filter(i => !!i).length > 0,
retry: 0,
2024-11-14 16:54:23 +08:00
})
}
2024-11-12 17:58:14 +08:00
const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList']
export const usePluginTaskList = () => {
2024-11-13 11:33:39 +08:00
const [enabled, setEnabled] = useState(true)
2024-11-12 17:58:14 +08:00
const {
data,
isFetched,
refetch,
...rest
} = useQuery({
queryKey: usePluginTaskListKey,
queryFn: async () => {
const currentData = await get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100')
2024-11-15 15:50:14 +08:00
const taskDone = currentData.tasks.every(task => task.status === TaskStatus.success)
2024-11-12 17:58:14 +08:00
if (taskDone)
setEnabled(false)
return currentData
},
2024-11-15 15:50:14 +08:00
refetchInterval: 5000,
2024-11-12 17:58:14 +08:00
enabled,
})
const handleRefetch = useCallback(() => {
setEnabled(true)
refetch()
}, [refetch])
return {
data,
pluginTasks: data?.tasks || [],
isFetched,
handleRefetch,
...rest,
}
}
export const useMutationClearTaskPlugin = () => {
return useMutation({
mutationFn: ({ taskId, pluginId }: { taskId: string; pluginId: string }) => {
2024-11-13 11:33:39 +08:00
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${pluginId}`)
2024-11-12 17:58:14 +08:00
},
})
}
2024-11-13 15:48:06 +08:00
2024-11-25 17:54:28 +08:00
export const useMutationClearAllTaskPlugin = () => {
return useMutation({
mutationFn: () => {
return post<{ success: boolean }>('/workspaces/current/plugin/tasks/delete_all')
},
})
}
2024-11-21 14:24:43 +08:00
export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => {
return useQuery({
queryKey: [NAME_SPACE, 'downloadPlugin', info],
queryFn: () => getMarketplace<Blob>(`/plugins/${info.organization}/${info.pluginName}/${info.version}/download`),
enabled: needDownload,
retry: 0,
})
}
2024-12-10 14:11:34 +08:00
export const useMutationCheckDependecies = () => {
return useMutation({
mutationFn: (appId: string) => {
return get<{ leaked_dependencies: Dependency[] }>(`/apps/import/${appId}/check-dependencies`)
},
})
}