mirror of
https://github.com/langgenius/dify.git
synced 2026-07-15 01:17:04 +08:00
1393 lines
47 KiB
TypeScript
1393 lines
47 KiB
TypeScript
import type { PluginInstallationItemResponse } from '@dify/contracts/api/console/workspaces/types.gen'
|
|
import type {
|
|
PluginInfoFromMarketPlace,
|
|
PluginsFromMarketplaceByInfoResponse,
|
|
PluginsFromMarketplaceResponse,
|
|
} from '@dify/contracts/marketplace'
|
|
import type { MutateOptions, QueryClient, QueryOptions } from '@tanstack/react-query'
|
|
import type {
|
|
FormOption,
|
|
ModelProvider,
|
|
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
|
import type {
|
|
AutoUpdateConfig,
|
|
} from '@/app/components/plugins/reference-setting-modal/auto-update-setting/types'
|
|
import type {
|
|
DebugInfo as DebugInfoTypes,
|
|
Dependency,
|
|
GitHubItemAndMarketPlaceDependency,
|
|
InstalledPluginCategoryListResponse,
|
|
InstalledPluginListWithTotalResponse,
|
|
InstallPackageResponse,
|
|
InstallStatusResponse,
|
|
MetaData,
|
|
PackageDependency,
|
|
Permissions,
|
|
Plugin,
|
|
PluginDeclaration,
|
|
PluginDetail,
|
|
PluginTask,
|
|
PluginTaskStart,
|
|
ReferenceSetting,
|
|
uploadGitHubResponse,
|
|
VersionInfo,
|
|
VersionListResponse,
|
|
} from '@/app/components/plugins/types'
|
|
import {
|
|
useInfiniteQuery,
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
} from '@tanstack/react-query'
|
|
import { cloneDeep } from 'es-toolkit/object'
|
|
import { useCallback, useEffect, useRef } from 'react'
|
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
|
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
|
|
import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils'
|
|
import { PluginCategoryEnum, PluginSource, TaskStatus } from '@/app/components/plugins/types'
|
|
import { useAppContext } from '@/context/app-context'
|
|
import { fetchModelProviderModelList } from '@/service/common'
|
|
import { fetchPluginInfoFromMarketPlace, uninstallPlugin } from '@/service/plugins'
|
|
import { hasPermission } from '@/utils/permission'
|
|
// eslint-disable-next-line no-restricted-imports
|
|
import { get, getMarketplace, post, postMarketplace } from './base'
|
|
import { consoleQuery } from './client'
|
|
import { useInvalidateAllBuiltInTools } from './use-tools'
|
|
|
|
const NAME_SPACE = 'plugins'
|
|
const useInstalledPluginListKey = [NAME_SPACE, 'installedPluginList']
|
|
const usePluginTaskListKey = [NAME_SPACE, 'pluginTaskList']
|
|
|
|
type PluginTaskListResponse = {
|
|
tasks: PluginTask[]
|
|
}
|
|
|
|
const getString = (value: unknown) => {
|
|
return typeof value === 'string' ? value : ''
|
|
}
|
|
|
|
const getBoolean = (value: unknown) => {
|
|
return value === true
|
|
}
|
|
|
|
const getNumber = (value: unknown) => {
|
|
return typeof value === 'number' ? value : undefined
|
|
}
|
|
|
|
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
|
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
}
|
|
|
|
const getRecord = (value: unknown, key: string) => {
|
|
if (!isRecord(value))
|
|
return undefined
|
|
|
|
const child = value[key]
|
|
return isRecord(child) ? child : undefined
|
|
}
|
|
|
|
const getRecordArray = (value: unknown, key: string) => {
|
|
if (!isRecord(value))
|
|
return []
|
|
|
|
const child = value[key]
|
|
return Array.isArray(child) ? child.filter(isRecord) : []
|
|
}
|
|
|
|
const getStringArray = (value: unknown) => {
|
|
return Array.isArray(value) ? value.filter(item => typeof item === 'string') : []
|
|
}
|
|
|
|
const getI18nValue = (value: object | null | undefined, key: string) => {
|
|
return value ? getString(Object.entries(value).find(([itemKey]) => itemKey === key)?.[1]) : ''
|
|
}
|
|
|
|
const normalizeI18nObject = (value: object | null | undefined, fallback = ''): PluginDeclaration['label'] => {
|
|
const en = getI18nValue(value, 'en_US') || getI18nValue(value, 'en-US') || fallback
|
|
const zhHans = getI18nValue(value, 'zh_Hans') || getI18nValue(value, 'zh-Hans') || en
|
|
const ja = getI18nValue(value, 'ja_JP') || getI18nValue(value, 'ja-JP') || en
|
|
const ptBr = getI18nValue(value, 'pt_BR') || getI18nValue(value, 'pt-BR') || en
|
|
|
|
return {
|
|
'en-US': en,
|
|
'zh-Hans': zhHans,
|
|
'zh-Hant': en,
|
|
'pt-BR': ptBr,
|
|
'es-ES': en,
|
|
'fr-FR': en,
|
|
'de-DE': en,
|
|
'ja-JP': ja,
|
|
'ko-KR': en,
|
|
'ru-RU': en,
|
|
'it-IT': en,
|
|
'th-TH': en,
|
|
'uk-UA': en,
|
|
'vi-VN': en,
|
|
'ro-RO': en,
|
|
'pl-PL': en,
|
|
'hi-IN': en,
|
|
'tr-TR': en,
|
|
'fa-IR': en,
|
|
'sl-SI': en,
|
|
'id-ID': en,
|
|
'nl-NL': en,
|
|
'ar-TN': en,
|
|
'en_US': en,
|
|
'zh_Hans': zhHans,
|
|
'ja_JP': ja,
|
|
}
|
|
}
|
|
|
|
const normalizePluginCategory = (category: PluginInstallationItemResponse['declaration']['category']): PluginCategoryEnum => {
|
|
switch (category) {
|
|
case PluginCategoryEnum.tool:
|
|
return PluginCategoryEnum.tool
|
|
case PluginCategoryEnum.model:
|
|
return PluginCategoryEnum.model
|
|
case PluginCategoryEnum.datasource:
|
|
return PluginCategoryEnum.datasource
|
|
case PluginCategoryEnum.trigger:
|
|
return PluginCategoryEnum.trigger
|
|
case PluginCategoryEnum.agent:
|
|
return PluginCategoryEnum.agent
|
|
case PluginCategoryEnum.extension:
|
|
return PluginCategoryEnum.extension
|
|
}
|
|
return PluginCategoryEnum.extension
|
|
}
|
|
|
|
const normalizePluginSource = (source: PluginInstallationItemResponse['source']): PluginSource => {
|
|
switch (source) {
|
|
case PluginSource.github:
|
|
return PluginSource.github
|
|
case PluginSource.marketplace:
|
|
return PluginSource.marketplace
|
|
case PluginSource.local:
|
|
return PluginSource.local
|
|
case PluginSource.debugging:
|
|
return PluginSource.debugging
|
|
}
|
|
return PluginSource.marketplace
|
|
}
|
|
|
|
const normalizePluginMeta = (meta: Record<string, unknown>): MetaData => {
|
|
return {
|
|
repo: getString(meta.repo),
|
|
version: getString(meta.version),
|
|
package: getString(meta.package),
|
|
}
|
|
}
|
|
|
|
const normalizeToolCredentialOption = (
|
|
option: Record<string, unknown>,
|
|
): NonNullable<NonNullable<PluginDeclaration['tool']>['credentials_schema'][number]['options']>[number] => ({
|
|
label: normalizeI18nObject(getRecord(option, 'label'), getString(option.value)),
|
|
value: getString(option.value),
|
|
})
|
|
|
|
const normalizeToolCredential = (
|
|
credential: Record<string, unknown>,
|
|
): NonNullable<PluginDeclaration['tool']>['credentials_schema'][number] => ({
|
|
name: getString(credential.name),
|
|
label: normalizeI18nObject(getRecord(credential, 'label'), getString(credential.name)),
|
|
help: credential.help === null ? null : normalizeI18nObject(getRecord(credential, 'help')),
|
|
placeholder: normalizeI18nObject(getRecord(credential, 'placeholder')),
|
|
type: getString(credential.type),
|
|
required: getBoolean(credential.required),
|
|
default: getString(credential.default),
|
|
options: getRecordArray(credential, 'options').map(normalizeToolCredentialOption),
|
|
})
|
|
|
|
const normalizePluginToolDeclaration = (value: unknown): PluginDeclaration['tool'] => {
|
|
if (!isRecord(value))
|
|
return undefined
|
|
|
|
const identity = getRecord(value, 'identity')
|
|
if (!identity)
|
|
return undefined
|
|
|
|
const name = getString(identity.name)
|
|
|
|
return {
|
|
identity: {
|
|
author: getString(identity.author),
|
|
name,
|
|
description: normalizeI18nObject(getRecord(identity, 'description')),
|
|
icon: getString(identity.icon),
|
|
label: normalizeI18nObject(getRecord(identity, 'label'), name),
|
|
tags: getStringArray(identity.tags),
|
|
},
|
|
credentials_schema: getRecordArray(value, 'credentials_schema').map(normalizeToolCredential),
|
|
}
|
|
}
|
|
|
|
const normalizePluginEndpointDeclaration = (value: unknown): PluginDeclaration['endpoint'] => {
|
|
if (!isRecord(value))
|
|
return undefined
|
|
|
|
return {
|
|
settings: getRecordArray(value, 'settings').map(normalizeToolCredential),
|
|
endpoints: getRecordArray(value, 'endpoints').map(endpoint => ({
|
|
path: getString(endpoint.path),
|
|
method: getString(endpoint.method),
|
|
hidden: endpoint.hidden === undefined ? undefined : getBoolean(endpoint.hidden),
|
|
})),
|
|
}
|
|
}
|
|
|
|
const normalizeParameterDefault = (value: unknown) => {
|
|
if (Array.isArray(value))
|
|
return value.filter(item => typeof item === 'string')
|
|
if (typeof value === 'string')
|
|
return value
|
|
if (value === undefined || value === null)
|
|
return undefined
|
|
return String(value)
|
|
}
|
|
|
|
const normalizeFormType = (type: unknown): FormTypeEnum => {
|
|
switch (type) {
|
|
case FormTypeEnum.appSelector:
|
|
return FormTypeEnum.appSelector
|
|
case FormTypeEnum.boolean:
|
|
return FormTypeEnum.boolean
|
|
case FormTypeEnum.checkbox:
|
|
return FormTypeEnum.checkbox
|
|
case FormTypeEnum.dynamicSelect:
|
|
return FormTypeEnum.dynamicSelect
|
|
case FormTypeEnum.file:
|
|
return FormTypeEnum.file
|
|
case FormTypeEnum.files:
|
|
return FormTypeEnum.files
|
|
case FormTypeEnum.modelSelector:
|
|
return FormTypeEnum.modelSelector
|
|
case FormTypeEnum.multiToolSelector:
|
|
return FormTypeEnum.multiToolSelector
|
|
case FormTypeEnum.radio:
|
|
return FormTypeEnum.radio
|
|
case FormTypeEnum.secretInput:
|
|
return FormTypeEnum.secretInput
|
|
case FormTypeEnum.select:
|
|
return FormTypeEnum.select
|
|
case FormTypeEnum.textNumber:
|
|
case 'number':
|
|
return FormTypeEnum.textNumber
|
|
case FormTypeEnum.textInput:
|
|
case 'string':
|
|
case 'array':
|
|
case 'object':
|
|
return FormTypeEnum.textInput
|
|
}
|
|
return FormTypeEnum.textInput
|
|
}
|
|
|
|
const normalizeParameterOption = (
|
|
option: Record<string, unknown>,
|
|
): NonNullable<PluginDeclaration['trigger']['subscription_schema'][number]['options']>[number] => ({
|
|
value: getString(option.value),
|
|
label: normalizeI18nObject(getRecord(option, 'label'), getString(option.value)),
|
|
icon: getString(option.icon) || undefined,
|
|
})
|
|
|
|
const normalizeParameterSchema = (
|
|
parameter: Record<string, unknown>,
|
|
): PluginDeclaration['trigger']['subscription_schema'][number] => ({
|
|
name: getString(parameter.name),
|
|
label: normalizeI18nObject(getRecord(parameter, 'label'), getString(parameter.name)),
|
|
type: normalizeFormType(parameter.type),
|
|
auto_generate: parameter.auto_generate ?? undefined,
|
|
template: parameter.template ?? undefined,
|
|
scope: parameter.scope ?? undefined,
|
|
required: getBoolean(parameter.required),
|
|
multiple: getBoolean(parameter.multiple),
|
|
default: normalizeParameterDefault(parameter.default),
|
|
min: parameter.min ?? undefined,
|
|
max: parameter.max ?? undefined,
|
|
precision: parameter.precision ?? undefined,
|
|
options: getRecordArray(parameter, 'options').map(normalizeParameterOption),
|
|
description: normalizeI18nObject(getRecord(parameter, 'description')),
|
|
})
|
|
|
|
const normalizeCredentialSchema = (
|
|
schema: Record<string, unknown>,
|
|
): PluginDeclaration['trigger']['subscription_constructor']['credentials_schema'][number] => ({
|
|
name: getString(schema.name),
|
|
label: normalizeI18nObject(getRecord(schema, 'label'), getString(schema.name)),
|
|
description: normalizeI18nObject(getRecord(schema, 'description')),
|
|
type: normalizeFormType(schema.type),
|
|
scope: schema.scope ?? undefined,
|
|
required: getBoolean(schema.required),
|
|
default: schema.default ?? undefined,
|
|
options: getRecordArray(schema, 'options').map(normalizeParameterOption),
|
|
help: normalizeI18nObject(getRecord(schema, 'help')),
|
|
url: getString(schema.url),
|
|
placeholder: normalizeI18nObject(getRecord(schema, 'placeholder')),
|
|
})
|
|
|
|
const normalizeTriggerEventParameter = (
|
|
parameter: Record<string, unknown>,
|
|
): PluginDeclaration['trigger']['events'][number]['parameters'][number] => ({
|
|
name: getString(parameter.name),
|
|
label: normalizeI18nObject(getRecord(parameter, 'label'), getString(parameter.name)),
|
|
type: getString(parameter.type),
|
|
auto_generate: parameter.auto_generate ?? undefined,
|
|
template: parameter.template ?? undefined,
|
|
scope: parameter.scope ?? undefined,
|
|
required: getBoolean(parameter.required),
|
|
multiple: getBoolean(parameter.multiple),
|
|
default: parameter.default ?? '',
|
|
min: getNumber(parameter.min),
|
|
max: getNumber(parameter.max),
|
|
precision: getNumber(parameter.precision),
|
|
options: getRecordArray(parameter, 'options').map(normalizeParameterOption),
|
|
description: normalizeI18nObject(getRecord(parameter, 'description')),
|
|
})
|
|
|
|
const normalizeTriggerEvent = (
|
|
event: Record<string, unknown>,
|
|
): PluginDeclaration['trigger']['events'][number] => {
|
|
const identity = getRecord(event, 'identity')
|
|
const name = getString(event.name)
|
|
|
|
return {
|
|
name,
|
|
identity: {
|
|
author: getString(identity?.author),
|
|
name: getString(identity?.name) || name,
|
|
label: normalizeI18nObject(getRecord(identity, 'label'), name),
|
|
provider: getString(identity?.provider) || undefined,
|
|
},
|
|
description: normalizeI18nObject(getRecord(event, 'description'), name),
|
|
parameters: getRecordArray(event, 'parameters').map(normalizeTriggerEventParameter),
|
|
output_schema: getRecord(event, 'output_schema') ?? {},
|
|
}
|
|
}
|
|
|
|
const createEmptyTrigger = (name: string): PluginDeclaration['trigger'] => ({
|
|
events: [],
|
|
identity: {
|
|
author: '',
|
|
name,
|
|
label: normalizeI18nObject(undefined, name),
|
|
description: normalizeI18nObject(undefined),
|
|
icon: '',
|
|
tags: [],
|
|
},
|
|
subscription_constructor: {
|
|
credentials_schema: [],
|
|
oauth_schema: {
|
|
client_schema: [],
|
|
credentials_schema: [],
|
|
},
|
|
parameters: [],
|
|
},
|
|
subscription_schema: [],
|
|
})
|
|
|
|
const normalizePluginTriggerDeclaration = (
|
|
value: unknown,
|
|
fallbackName: string,
|
|
): PluginDeclaration['trigger'] => {
|
|
if (!isRecord(value))
|
|
return createEmptyTrigger(fallbackName)
|
|
|
|
const identity = getRecord(value, 'identity')
|
|
const subscriptionConstructor = getRecord(value, 'subscription_constructor')
|
|
const oauthSchema = getRecord(subscriptionConstructor, 'oauth_schema')
|
|
|
|
return {
|
|
events: getRecordArray(value, 'events').map(normalizeTriggerEvent),
|
|
identity: {
|
|
author: getString(identity?.author),
|
|
name: getString(identity?.name) || fallbackName,
|
|
label: normalizeI18nObject(getRecord(identity, 'label'), fallbackName),
|
|
description: normalizeI18nObject(getRecord(identity, 'description')),
|
|
icon: getString(identity?.icon),
|
|
tags: getStringArray(identity?.tags),
|
|
},
|
|
subscription_constructor: {
|
|
credentials_schema: getRecordArray(subscriptionConstructor, 'credentials_schema').map(normalizeCredentialSchema),
|
|
oauth_schema: {
|
|
client_schema: getRecordArray(oauthSchema, 'client_schema').map(normalizeCredentialSchema),
|
|
credentials_schema: getRecordArray(oauthSchema, 'credentials_schema').map(normalizeCredentialSchema),
|
|
},
|
|
parameters: getRecordArray(subscriptionConstructor, 'parameters').map(normalizeParameterSchema),
|
|
},
|
|
subscription_schema: getRecordArray(value, 'subscription_schema').map(normalizeParameterSchema),
|
|
}
|
|
}
|
|
|
|
const normalizePluginDeclaration = (plugin: PluginInstallationItemResponse): PluginDeclaration => {
|
|
const { declaration } = plugin
|
|
return {
|
|
plugin_unique_identifier: plugin.plugin_unique_identifier,
|
|
version: declaration.version,
|
|
author: declaration.author ?? '',
|
|
icon: declaration.icon,
|
|
icon_dark: declaration.icon_dark ?? undefined,
|
|
name: declaration.name,
|
|
category: normalizePluginCategory(declaration.category),
|
|
label: normalizeI18nObject(declaration.label, declaration.name),
|
|
description: normalizeI18nObject(declaration.description, declaration.name),
|
|
created_at: declaration.created_at,
|
|
resource: declaration.resource,
|
|
plugins: declaration.plugins,
|
|
verified: declaration.verified ?? false,
|
|
endpoint: normalizePluginEndpointDeclaration(declaration.endpoint),
|
|
tool: normalizePluginToolDeclaration(declaration.tool),
|
|
datasource: normalizePluginToolDeclaration(declaration.datasource),
|
|
model: declaration.model,
|
|
tags: declaration.tags ?? [],
|
|
agent_strategy: declaration.agent_strategy,
|
|
meta: {
|
|
version: getString(declaration.meta.version) || declaration.version,
|
|
minimum_dify_version: getString(declaration.meta.minimum_dify_version) || undefined,
|
|
},
|
|
trigger: normalizePluginTriggerDeclaration(declaration.trigger, declaration.name),
|
|
}
|
|
}
|
|
|
|
export const normalizeInstalledPluginDetail = (plugin: PluginInstallationItemResponse): PluginDetail => {
|
|
const declaration = normalizePluginDeclaration(plugin)
|
|
|
|
return {
|
|
id: plugin.id,
|
|
created_at: plugin.created_at,
|
|
updated_at: plugin.updated_at,
|
|
name: declaration.name,
|
|
plugin_id: plugin.plugin_id,
|
|
plugin_unique_identifier: plugin.plugin_unique_identifier,
|
|
declaration,
|
|
installation_id: plugin.id,
|
|
tenant_id: plugin.tenant_id,
|
|
endpoints_setups: plugin.endpoints_setups,
|
|
endpoints_active: plugin.endpoints_active,
|
|
version: plugin.version,
|
|
latest_version: plugin.version,
|
|
latest_unique_identifier: plugin.plugin_unique_identifier,
|
|
source: normalizePluginSource(plugin.source),
|
|
meta: normalizePluginMeta(plugin.meta),
|
|
status: 'active',
|
|
deprecated_reason: '',
|
|
alternative_plugin_id: '',
|
|
}
|
|
}
|
|
|
|
const isUnfinishedPluginTask = (task: PluginTask) => task.status === TaskStatus.pending || task.status === TaskStatus.running
|
|
|
|
const normalizeStartedPluginTask = (task: PluginTaskStart): PluginTask => ({
|
|
...task,
|
|
plugins: task.plugins.map(plugin => ({
|
|
...plugin,
|
|
taskId: plugin.taskId || task.id,
|
|
})),
|
|
})
|
|
|
|
const upsertStartedPluginTask = (queryClient: QueryClient, response: InstallPackageResponse) => {
|
|
if (!response.task)
|
|
return
|
|
|
|
const startedTask = normalizeStartedPluginTask(response.task)
|
|
|
|
queryClient.setQueryData<PluginTaskListResponse>(usePluginTaskListKey, (previous) => {
|
|
if (!previous)
|
|
return { tasks: [startedTask] }
|
|
|
|
const existingTaskIndex = previous.tasks.findIndex(task => task.id === startedTask.id)
|
|
if (existingTaskIndex === -1)
|
|
return { tasks: [startedTask, ...previous.tasks] }
|
|
|
|
return {
|
|
tasks: previous.tasks.map(task => task.id === startedTask.id ? startedTask : task),
|
|
}
|
|
})
|
|
}
|
|
|
|
const preserveLocalUnfinishedPluginTasks = (
|
|
previousData: PluginTaskListResponse | undefined,
|
|
nextData: PluginTaskListResponse,
|
|
) => {
|
|
if (!previousData)
|
|
return nextData
|
|
|
|
const nextTaskIds = new Set(nextData.tasks.map(task => task.id))
|
|
const missingUnfinishedTasks = previousData.tasks.filter(task => !nextTaskIds.has(task.id) && isUnfinishedPluginTask(task))
|
|
if (!missingUnfinishedTasks.length)
|
|
return nextData
|
|
|
|
return {
|
|
...nextData,
|
|
tasks: [
|
|
...missingUnfinishedTasks,
|
|
...nextData.tasks,
|
|
],
|
|
}
|
|
}
|
|
|
|
export const useCheckInstalled = ({
|
|
pluginIds,
|
|
enabled,
|
|
}: {
|
|
pluginIds: string[]
|
|
enabled: boolean
|
|
}) => {
|
|
return useQuery(consoleQuery.workspaces.current.plugin.list.installations.ids.post.queryOptions({
|
|
input: { body: { plugin_ids: pluginIds } },
|
|
enabled,
|
|
staleTime: 0,
|
|
select: response => ({
|
|
plugins: response.plugins.map(normalizeInstalledPluginDetail),
|
|
}),
|
|
}))
|
|
}
|
|
|
|
export const useInvalidateCheckInstalled = () => {
|
|
const queryClient = useQueryClient()
|
|
return () => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: consoleQuery.workspaces.current.plugin.list.installations.ids.post.key(),
|
|
})
|
|
}
|
|
}
|
|
|
|
const useRecommendedMarketplacePluginsKey = [NAME_SPACE, 'recommendedMarketplacePlugins']
|
|
const useRecommendedMarketplacePlugins = ({
|
|
collection = '__recommended-plugins-tools',
|
|
enabled = true,
|
|
limit = 15,
|
|
}: {
|
|
collection?: string
|
|
enabled?: boolean
|
|
limit?: number
|
|
} = {}) => {
|
|
return useQuery<Plugin[]>({
|
|
queryKey: [...useRecommendedMarketplacePluginsKey, collection, limit],
|
|
queryFn: async () => {
|
|
const response = await postMarketplace<{ data: { plugins: Plugin[] } }>(
|
|
`/collections/${collection}/plugins`,
|
|
{
|
|
body: {
|
|
limit,
|
|
},
|
|
},
|
|
)
|
|
return response.data.plugins.map(plugin => getFormattedPlugin(plugin))
|
|
},
|
|
enabled,
|
|
staleTime: 60 * 1000,
|
|
})
|
|
}
|
|
|
|
export const useFeaturedToolsRecommendations = (enabled: boolean, limit = 15) => {
|
|
const {
|
|
data: plugins = [],
|
|
isLoading,
|
|
} = useRecommendedMarketplacePlugins({
|
|
collection: '__recommended-plugins-tools',
|
|
enabled,
|
|
limit,
|
|
})
|
|
|
|
return {
|
|
plugins,
|
|
isLoading,
|
|
}
|
|
}
|
|
|
|
export const useFeaturedTriggersRecommendations = (enabled: boolean, limit = 15) => {
|
|
const {
|
|
data: plugins = [],
|
|
isLoading,
|
|
} = useRecommendedMarketplacePlugins({
|
|
collection: '__recommended-plugins-triggers',
|
|
enabled,
|
|
limit,
|
|
})
|
|
|
|
return {
|
|
plugins,
|
|
isLoading,
|
|
}
|
|
}
|
|
|
|
type UseInstalledPluginListOptions = {
|
|
category?: PluginCategoryEnum
|
|
refetchOnMount?: boolean | 'always'
|
|
}
|
|
|
|
export const useInstalledPluginList = (disable?: boolean, pageSize = 100, options?: UseInstalledPluginListOptions) => {
|
|
const category = options?.category
|
|
const fetchPlugins = async ({ pageParam = 1 }) => {
|
|
const path = category
|
|
? `/workspaces/current/plugin/${category}/list`
|
|
: '/workspaces/current/plugin/list'
|
|
|
|
if (category)
|
|
return get<InstalledPluginCategoryListResponse>(`${path}?page=${pageParam}&page_size=${pageSize}`)
|
|
|
|
return get<InstalledPluginListWithTotalResponse>(`${path}?page=${pageParam}&page_size=${pageSize}`)
|
|
}
|
|
|
|
const {
|
|
data,
|
|
error,
|
|
fetchNextPage,
|
|
hasNextPage,
|
|
isFetchingNextPage,
|
|
isLoading,
|
|
isSuccess,
|
|
} = useInfiniteQuery({
|
|
enabled: !disable,
|
|
queryKey: category ? [...useInstalledPluginListKey, category] : useInstalledPluginListKey,
|
|
queryFn: fetchPlugins,
|
|
getNextPageParam: (lastPage, pages) => {
|
|
if (category)
|
|
return 'has_more' in lastPage && lastPage.has_more ? pages.length + 1 : undefined
|
|
|
|
const totalItems = 'total' in lastPage ? lastPage.total : 0
|
|
const currentPage = pages.length
|
|
const itemsLoaded = currentPage * pageSize
|
|
|
|
if (itemsLoaded >= totalItems)
|
|
return
|
|
|
|
return currentPage + 1
|
|
},
|
|
initialPageParam: 1,
|
|
refetchOnMount: options?.refetchOnMount,
|
|
})
|
|
|
|
const plugins = data?.pages.flatMap(page => page.plugins) ?? []
|
|
const firstPage = data?.pages[0]
|
|
const builtinTools = firstPage && 'builtin_tools' in firstPage ? firstPage.builtin_tools : []
|
|
const total = data?.pages[0] && 'total' in data.pages[0] ? data.pages[0].total : plugins.length
|
|
|
|
return {
|
|
data: disable
|
|
? undefined
|
|
: {
|
|
plugins,
|
|
builtin_tools: builtinTools,
|
|
total,
|
|
},
|
|
isLastPage: !hasNextPage,
|
|
loadNextPage: () => {
|
|
fetchNextPage()
|
|
},
|
|
isLoading,
|
|
isFetching: isFetchingNextPage,
|
|
error,
|
|
isSuccess,
|
|
}
|
|
}
|
|
|
|
export const useInvalidateInstalledPluginList = () => {
|
|
const queryClient = useQueryClient()
|
|
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
|
|
return () => {
|
|
queryClient.invalidateQueries(
|
|
{
|
|
queryKey: useInstalledPluginListKey,
|
|
},
|
|
)
|
|
invalidateAllBuiltInTools()
|
|
}
|
|
}
|
|
|
|
export const useInstallPackageFromMarketPlace = (options?: MutateOptions<InstallPackageResponse, Error, string>) => {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
...options,
|
|
mutationFn: (uniqueIdentifier: string) => {
|
|
return post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', { body: { plugin_unique_identifiers: [uniqueIdentifier] } })
|
|
},
|
|
onSuccess: (data, variables, context, mutation) => {
|
|
upsertStartedPluginTask(queryClient, data)
|
|
options?.onSuccess?.(data, variables, context, mutation)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useUpdatePackageFromMarketPlace = (options?: MutateOptions<InstallPackageResponse, Error, object>) => {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
...options,
|
|
mutationFn: (body: object) => {
|
|
return post<InstallPackageResponse>('/workspaces/current/plugin/upgrade/marketplace', {
|
|
body,
|
|
})
|
|
},
|
|
onSuccess: (data, variables, context, mutation) => {
|
|
upsertStartedPluginTask(queryClient, data)
|
|
options?.onSuccess?.(data, variables, context, mutation)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const usePluginDeclarationFromMarketPlace = (pluginUniqueIdentifier: string) => {
|
|
return useQuery({
|
|
queryKey: [NAME_SPACE, 'pluginDeclaration', pluginUniqueIdentifier],
|
|
queryFn: () => get<{ manifest: PluginDeclaration }>('/workspaces/current/plugin/marketplace/pkg', { params: { plugin_unique_identifier: pluginUniqueIdentifier } }),
|
|
enabled: !!pluginUniqueIdentifier,
|
|
})
|
|
}
|
|
|
|
export const useVersionListOfPlugin = (pluginID: string) => {
|
|
return useQuery<{ data: VersionListResponse }>({
|
|
enabled: !!pluginID,
|
|
queryKey: [NAME_SPACE, 'versions', pluginID],
|
|
queryFn: () => getMarketplace<{ data: VersionListResponse }>(`/plugins/${pluginID}/versions`, { params: { page: 1, page_size: 100 } }),
|
|
})
|
|
}
|
|
|
|
export const useInstallPackageFromLocal = () => {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
mutationFn: (uniqueIdentifier: string) => {
|
|
return post<InstallPackageResponse>('/workspaces/current/plugin/install/pkg', {
|
|
body: { plugin_unique_identifiers: [uniqueIdentifier] },
|
|
})
|
|
},
|
|
onSuccess: (data) => {
|
|
upsertStartedPluginTask(queryClient, data)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useInstallPackageFromGitHub = () => {
|
|
const queryClient = useQueryClient()
|
|
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,
|
|
},
|
|
})
|
|
},
|
|
onSuccess: (data) => {
|
|
upsertStartedPluginTask(queryClient, data)
|
|
},
|
|
})
|
|
}
|
|
|
|
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,
|
|
})
|
|
}
|
|
|
|
export const useInstallOrUpdate = ({
|
|
onSuccess,
|
|
}: {
|
|
onSuccess?: (res: InstallStatusResponse[]) => void
|
|
}) => {
|
|
const queryClient = useQueryClient()
|
|
const { mutateAsync: updatePackageFromMarketPlace } = useUpdatePackageFromMarketPlace()
|
|
|
|
return useMutation({
|
|
mutationFn: (data: {
|
|
payload: Dependency[]
|
|
plugin: Plugin[]
|
|
installedInfo: Record<string, VersionInfo>
|
|
}) => {
|
|
const { payload, plugin, installedInfo } = data
|
|
|
|
return Promise.all(payload.map(async (item, i) => {
|
|
try {
|
|
const orgAndName = `${plugin[i]?.org || plugin[i]?.author}/${plugin[i]?.name}`
|
|
const installedPayload = installedInfo[orgAndName]
|
|
const isInstalled = !!installedPayload
|
|
let uniqueIdentifier = ''
|
|
let taskId = ''
|
|
let isFinishedInstallation = false
|
|
|
|
if (item.type === 'github') {
|
|
const data = item as GitHubItemAndMarketPlaceDependency
|
|
// From local bundle don't have data.value.github_plugin_unique_identifier
|
|
uniqueIdentifier = data.value.github_plugin_unique_identifier!
|
|
if (!uniqueIdentifier) {
|
|
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!,
|
|
},
|
|
})
|
|
uniqueIdentifier = data.value.github_plugin_unique_identifier! || unique_identifier
|
|
// has the same version, but not installed
|
|
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
|
|
return {
|
|
status: TaskStatus.success,
|
|
taskId: '',
|
|
uniqueIdentifier: '',
|
|
}
|
|
}
|
|
}
|
|
if (!isInstalled) {
|
|
const response = 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,
|
|
},
|
|
})
|
|
upsertStartedPluginTask(queryClient, response)
|
|
const { task_id, all_installed } = response
|
|
taskId = task_id
|
|
isFinishedInstallation = all_installed
|
|
}
|
|
}
|
|
if (item.type === 'marketplace') {
|
|
const data = item as GitHubItemAndMarketPlaceDependency
|
|
uniqueIdentifier = data.value.marketplace_plugin_unique_identifier! || (plugin[i]?.latest_package_identifier ?? '') || (plugin[i]?.plugin_id ?? '')
|
|
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
|
|
return {
|
|
status: TaskStatus.success,
|
|
taskId: '',
|
|
uniqueIdentifier: '',
|
|
}
|
|
}
|
|
if (!isInstalled) {
|
|
const response = await post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', {
|
|
body: {
|
|
plugin_unique_identifiers: [uniqueIdentifier],
|
|
},
|
|
})
|
|
upsertStartedPluginTask(queryClient, response)
|
|
const { task_id, all_installed } = response
|
|
taskId = task_id
|
|
isFinishedInstallation = all_installed
|
|
}
|
|
}
|
|
if (item.type === 'package') {
|
|
const data = item as PackageDependency
|
|
uniqueIdentifier = data.value.unique_identifier
|
|
if (uniqueIdentifier === installedPayload?.uniqueIdentifier) {
|
|
return {
|
|
status: TaskStatus.success,
|
|
taskId: '',
|
|
uniqueIdentifier: '',
|
|
}
|
|
}
|
|
if (!isInstalled) {
|
|
const response = await post<InstallPackageResponse>('/workspaces/current/plugin/install/pkg', {
|
|
body: {
|
|
plugin_unique_identifiers: [uniqueIdentifier],
|
|
},
|
|
})
|
|
upsertStartedPluginTask(queryClient, response)
|
|
const { task_id, all_installed } = response
|
|
taskId = task_id
|
|
isFinishedInstallation = all_installed
|
|
}
|
|
}
|
|
if (isInstalled) {
|
|
if (item.type === 'package') {
|
|
await uninstallPlugin(installedPayload.installedId)
|
|
const response = await post<InstallPackageResponse>('/workspaces/current/plugin/install/pkg', {
|
|
body: {
|
|
plugin_unique_identifiers: [uniqueIdentifier],
|
|
},
|
|
})
|
|
upsertStartedPluginTask(queryClient, response)
|
|
const { task_id, all_installed } = response
|
|
taskId = task_id
|
|
isFinishedInstallation = all_installed
|
|
}
|
|
else {
|
|
const response = await updatePackageFromMarketPlace({
|
|
original_plugin_unique_identifier: installedPayload?.uniqueIdentifier,
|
|
new_plugin_unique_identifier: uniqueIdentifier,
|
|
})
|
|
const { task_id, all_installed } = response
|
|
taskId = task_id
|
|
isFinishedInstallation = all_installed
|
|
}
|
|
}
|
|
if (isFinishedInstallation) {
|
|
return {
|
|
status: TaskStatus.success,
|
|
taskId: '',
|
|
uniqueIdentifier: '',
|
|
}
|
|
}
|
|
else {
|
|
return {
|
|
status: TaskStatus.running,
|
|
taskId,
|
|
uniqueIdentifier,
|
|
}
|
|
}
|
|
}
|
|
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
catch (e) {
|
|
return Promise.resolve({ status: TaskStatus.failed, taskId: '', uniqueIdentifier: '' })
|
|
}
|
|
}))
|
|
},
|
|
onSuccess,
|
|
})
|
|
}
|
|
|
|
export const useDebugKey = () => {
|
|
return useQuery({
|
|
queryKey: [NAME_SPACE, 'debugKey'],
|
|
queryFn: () => get<DebugInfoTypes>('/workspaces/current/plugin/debugging-key'),
|
|
})
|
|
}
|
|
|
|
type PluginAutoUpgradeSettingsResponse = {
|
|
category: PluginCategoryEnum
|
|
auto_upgrade: AutoUpdateConfig
|
|
}
|
|
|
|
type MarketplacePluginInfoRequest = {
|
|
organization?: string
|
|
plugin?: string
|
|
version?: string
|
|
}
|
|
|
|
const useReferenceSettingKey = [NAME_SPACE, 'referenceSettings']
|
|
const usePluginPermissionSettingsKey = [...useReferenceSettingKey, 'permission']
|
|
const usePluginAutoUpgradeSettingsKey = [...useReferenceSettingKey, 'autoUpgrade']
|
|
const pluginAutoUpgradeSettingsQueryKey = (category: PluginCategoryEnum) => [...usePluginAutoUpgradeSettingsKey, category]
|
|
|
|
const areStringArraysEqual = (left: string[], right: string[]) => {
|
|
return left.length === right.length && left.every((value, index) => value === right[index])
|
|
}
|
|
|
|
const arePermissionsEqual = (left: Permissions | undefined, right: Permissions | undefined) => {
|
|
return left?.install_permission === right?.install_permission
|
|
&& left?.debug_permission === right?.debug_permission
|
|
}
|
|
|
|
const areAutoUpgradeSettingsEqual = (left: AutoUpdateConfig | undefined, right: AutoUpdateConfig | undefined) => {
|
|
return left?.strategy_setting === right?.strategy_setting
|
|
&& left?.upgrade_time_of_day === right?.upgrade_time_of_day
|
|
&& left?.upgrade_mode === right?.upgrade_mode
|
|
&& areStringArraysEqual(left?.exclude_plugins ?? [], right?.exclude_plugins ?? [])
|
|
&& areStringArraysEqual(left?.include_plugins ?? [], right?.include_plugins ?? [])
|
|
}
|
|
|
|
// legacy plugin permission
|
|
// export const hasPluginPermission = (permission: PermissionType | undefined, isAdmin: boolean) => {
|
|
// if (!permission)
|
|
// return false
|
|
|
|
// if (permission === PermissionType.noOne)
|
|
// return false
|
|
|
|
// if (permission === PermissionType.everyone)
|
|
// return true
|
|
|
|
// return isAdmin
|
|
// }
|
|
|
|
export const usePluginPermissionSettings = () => {
|
|
return useQuery({
|
|
queryKey: usePluginPermissionSettingsKey,
|
|
queryFn: () => get<Permissions>('/workspaces/current/plugin/permission/fetch'),
|
|
})
|
|
}
|
|
|
|
export const usePluginAutoUpgradeSettings = (category: PluginCategoryEnum) => {
|
|
return useQuery({
|
|
queryKey: pluginAutoUpgradeSettingsQueryKey(category),
|
|
queryFn: () => get<PluginAutoUpgradeSettingsResponse>(
|
|
'/workspaces/current/plugin/auto-upgrade/fetch',
|
|
{ params: { category } },
|
|
),
|
|
staleTime: 60 * 1000,
|
|
})
|
|
}
|
|
|
|
export const useInvalidateReferenceSettings = () => {
|
|
const queryClient = useQueryClient()
|
|
return () => {
|
|
queryClient.invalidateQueries(
|
|
{
|
|
queryKey: useReferenceSettingKey,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
export const useMutationPluginPermissionSettings = ({
|
|
onSuccess,
|
|
}: {
|
|
onSuccess?: () => void
|
|
} = {}) => {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: Permissions) => {
|
|
return post('/workspaces/current/plugin/permission/change', { body: payload })
|
|
},
|
|
onMutate: async (payload) => {
|
|
await queryClient.cancelQueries({ queryKey: usePluginPermissionSettingsKey })
|
|
const previousPermission = queryClient.getQueryData<Permissions>(usePluginPermissionSettingsKey)
|
|
const hadPreviousPermission = previousPermission !== undefined
|
|
|
|
queryClient.setQueryData(usePluginPermissionSettingsKey, payload)
|
|
|
|
return { previousPermission, hadPreviousPermission }
|
|
},
|
|
onError: (_error, _payload, context) => {
|
|
if (context?.hadPreviousPermission)
|
|
queryClient.setQueryData(usePluginPermissionSettingsKey, context.previousPermission)
|
|
else
|
|
queryClient.removeQueries({ queryKey: usePluginPermissionSettingsKey })
|
|
},
|
|
onSuccess: () => {
|
|
onSuccess?.()
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useMutationPluginAutoUpgradeSettings = ({
|
|
category,
|
|
onSuccess,
|
|
}: {
|
|
category: PluginCategoryEnum
|
|
onSuccess?: () => void
|
|
}) => {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: AutoUpdateConfig) => {
|
|
return post('/workspaces/current/plugin/auto-upgrade/change', {
|
|
body: {
|
|
category,
|
|
auto_upgrade: payload,
|
|
},
|
|
})
|
|
},
|
|
onMutate: async (payload) => {
|
|
const queryKey = pluginAutoUpgradeSettingsQueryKey(category)
|
|
await queryClient.cancelQueries({ queryKey })
|
|
const previousAutoUpgrade = queryClient.getQueryData<PluginAutoUpgradeSettingsResponse>(queryKey)
|
|
const hadPreviousAutoUpgrade = previousAutoUpgrade !== undefined
|
|
|
|
queryClient.setQueryData(pluginAutoUpgradeSettingsQueryKey(category), {
|
|
category,
|
|
auto_upgrade: payload,
|
|
} satisfies PluginAutoUpgradeSettingsResponse)
|
|
|
|
return { previousAutoUpgrade, hadPreviousAutoUpgrade }
|
|
},
|
|
onError: (_error, _payload, context) => {
|
|
if (context?.hadPreviousAutoUpgrade)
|
|
queryClient.setQueryData(pluginAutoUpgradeSettingsQueryKey(category), context.previousAutoUpgrade)
|
|
else
|
|
queryClient.removeQueries({ queryKey: pluginAutoUpgradeSettingsQueryKey(category) })
|
|
},
|
|
onSuccess: () => {
|
|
onSuccess?.()
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useMutationReferenceSettings = ({
|
|
category,
|
|
currentReferenceSetting,
|
|
onSuccess,
|
|
}: {
|
|
category: PluginCategoryEnum
|
|
currentReferenceSetting?: ReferenceSetting
|
|
onSuccess?: () => void
|
|
}) => {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: ReferenceSetting) => {
|
|
const mutations: Array<Promise<unknown>> = []
|
|
|
|
if (!arePermissionsEqual(payload.permission, currentReferenceSetting?.permission))
|
|
mutations.push(post('/workspaces/current/plugin/permission/change', { body: payload.permission }))
|
|
|
|
if (!areAutoUpgradeSettingsEqual(payload.auto_upgrade, currentReferenceSetting?.auto_upgrade)) {
|
|
mutations.push(post('/workspaces/current/plugin/auto-upgrade/change', {
|
|
body: {
|
|
category,
|
|
auto_upgrade: payload.auto_upgrade,
|
|
},
|
|
}))
|
|
}
|
|
|
|
return Promise.all(mutations)
|
|
},
|
|
onMutate: async (payload) => {
|
|
const shouldUpdatePermission = !arePermissionsEqual(payload.permission, currentReferenceSetting?.permission)
|
|
const shouldUpdateAutoUpgrade = !areAutoUpgradeSettingsEqual(payload.auto_upgrade, currentReferenceSetting?.auto_upgrade)
|
|
const autoUpgradeQueryKey = pluginAutoUpgradeSettingsQueryKey(category)
|
|
|
|
await Promise.all([
|
|
shouldUpdatePermission ? queryClient.cancelQueries({ queryKey: usePluginPermissionSettingsKey }) : Promise.resolve(),
|
|
shouldUpdateAutoUpgrade ? queryClient.cancelQueries({ queryKey: autoUpgradeQueryKey }) : Promise.resolve(),
|
|
])
|
|
|
|
const previousPermission = queryClient.getQueryData<Permissions>(usePluginPermissionSettingsKey)
|
|
const previousAutoUpgrade = queryClient.getQueryData<PluginAutoUpgradeSettingsResponse>(autoUpgradeQueryKey)
|
|
const hadPreviousPermission = previousPermission !== undefined
|
|
const hadPreviousAutoUpgrade = previousAutoUpgrade !== undefined
|
|
|
|
if (shouldUpdatePermission)
|
|
queryClient.setQueryData(usePluginPermissionSettingsKey, payload.permission)
|
|
|
|
if (shouldUpdateAutoUpgrade) {
|
|
queryClient.setQueryData(autoUpgradeQueryKey, {
|
|
category,
|
|
auto_upgrade: payload.auto_upgrade,
|
|
} satisfies PluginAutoUpgradeSettingsResponse)
|
|
}
|
|
|
|
return {
|
|
previousPermission,
|
|
previousAutoUpgrade,
|
|
hadPreviousPermission,
|
|
hadPreviousAutoUpgrade,
|
|
shouldUpdatePermission,
|
|
shouldUpdateAutoUpgrade,
|
|
}
|
|
},
|
|
onError: (_error, _payload, context) => {
|
|
if (context?.shouldUpdatePermission && context.hadPreviousPermission)
|
|
queryClient.setQueryData(usePluginPermissionSettingsKey, context.previousPermission)
|
|
else if (context?.shouldUpdatePermission)
|
|
queryClient.removeQueries({ queryKey: usePluginPermissionSettingsKey })
|
|
|
|
if (context?.shouldUpdateAutoUpgrade && context.hadPreviousAutoUpgrade)
|
|
queryClient.setQueryData(pluginAutoUpgradeSettingsQueryKey(category), context.previousAutoUpgrade)
|
|
else if (context?.shouldUpdateAutoUpgrade)
|
|
queryClient.removeQueries({ queryKey: pluginAutoUpgradeSettingsQueryKey(category) })
|
|
},
|
|
onSuccess,
|
|
})
|
|
}
|
|
|
|
export const useRemoveAutoUpgrade = () => {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: (payload: { plugin_id: string, category: PluginCategoryEnum }) => {
|
|
return post('/workspaces/current/plugin/auto-upgrade/exclude', { body: payload })
|
|
},
|
|
onSuccess: (_data, payload) => {
|
|
queryClient.invalidateQueries(
|
|
{
|
|
queryKey: pluginAutoUpgradeSettingsQueryKey(payload.category),
|
|
},
|
|
)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useFetchPluginsInMarketPlaceByIds = (unique_identifiers: string[], options?: QueryOptions<{ data: PluginsFromMarketplaceResponse }>) => {
|
|
return useQuery({
|
|
...options,
|
|
queryKey: [NAME_SPACE, 'fetchPluginsInMarketPlaceByIds', unique_identifiers],
|
|
queryFn: () => postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/identifier/batch', {
|
|
body: {
|
|
unique_identifiers,
|
|
},
|
|
}),
|
|
enabled: unique_identifiers?.filter(i => !!i).length > 0,
|
|
retry: 0,
|
|
})
|
|
}
|
|
|
|
export const useFetchPluginsInMarketPlaceByInfo = (infos: MarketplacePluginInfoRequest[]) => {
|
|
return useQuery({
|
|
queryKey: [NAME_SPACE, 'fetchPluginsInMarketPlaceByInfo', infos],
|
|
queryFn: () => postMarketplace<{ data: PluginsFromMarketplaceByInfoResponse }>('/plugins/versions/batch', {
|
|
body: {
|
|
plugin_tuples: infos.map(info => ({
|
|
org: info.organization,
|
|
name: info.plugin,
|
|
version: info.version,
|
|
})),
|
|
},
|
|
}),
|
|
enabled: infos?.filter(i => !!i).length > 0,
|
|
retry: 0,
|
|
})
|
|
}
|
|
|
|
export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
|
|
const initializedRef = useRef(false)
|
|
const queryClient = useQueryClient()
|
|
const { workspacePermissionKeys } = useAppContext()
|
|
const canManagement = hasPermission(workspacePermissionKeys, 'plugin.install')
|
|
const { refreshPluginList } = useRefreshPluginList()
|
|
const query = useQuery<PluginTaskListResponse>({
|
|
enabled: canManagement,
|
|
queryKey: usePluginTaskListKey,
|
|
queryFn: () => get<{ tasks: PluginTask[] }>('/workspaces/current/plugin/tasks?page=1&page_size=100'),
|
|
structuralSharing: (previousData, nextData) => preserveLocalUnfinishedPluginTasks(
|
|
previousData as PluginTaskListResponse | undefined,
|
|
nextData as PluginTaskListResponse,
|
|
),
|
|
refetchInterval: (lastQuery) => {
|
|
const lastData = lastQuery.state.data
|
|
const taskDone = lastData?.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed)
|
|
return taskDone ? false : 5000
|
|
},
|
|
})
|
|
const { data, isFetched, isRefetching, refetch } = query
|
|
|
|
useEffect(() => {
|
|
// After first fetch, refresh plugin list each time all tasks are done
|
|
// Skip initialization period, because the query cache is not updated yet
|
|
if (!initializedRef.current) {
|
|
initializedRef.current = true
|
|
return
|
|
}
|
|
|
|
if (isRefetching)
|
|
return
|
|
|
|
const lastData = cloneDeep(data)
|
|
const taskDone = lastData?.tasks.every(task => task.status === TaskStatus.success || task.status === TaskStatus.failed)
|
|
const taskAllFailed = lastData?.tasks.every(task => task.status === TaskStatus.failed)
|
|
if (taskDone && lastData?.tasks.length && !taskAllFailed)
|
|
refreshPluginList(category ? { category } : undefined, !category)
|
|
}, [category, data, isRefetching, refreshPluginList])
|
|
|
|
const handleRefetch = useCallback(() => {
|
|
refetch()
|
|
}, [refetch])
|
|
|
|
const handleInstallTaskStart = useCallback((response: InstallPackageResponse) => {
|
|
if (response.all_installed)
|
|
return
|
|
|
|
upsertStartedPluginTask(queryClient, response)
|
|
refetch()
|
|
}, [queryClient, refetch])
|
|
|
|
return {
|
|
data,
|
|
pluginTasks: data?.tasks || [],
|
|
isFetched,
|
|
handleRefetch,
|
|
handleInstallTaskStart,
|
|
}
|
|
}
|
|
|
|
export const useMutationClearTaskPlugin = () => {
|
|
return useMutation({
|
|
mutationFn: ({ taskId, pluginId }: { taskId: string, pluginId: string }) => {
|
|
const encodedPluginId = encodeURIComponent(pluginId)
|
|
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${encodedPluginId}`)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const usePluginManifestInfo = (pluginUID: string) => {
|
|
return useQuery({
|
|
enabled: !!pluginUID,
|
|
queryKey: [NAME_SPACE, 'manifest', pluginUID],
|
|
queryFn: () => getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${pluginUID}`),
|
|
retry: 0,
|
|
})
|
|
}
|
|
|
|
export const useMutationCheckDependencies = () => {
|
|
return useMutation({
|
|
mutationFn: (appId: string) => {
|
|
return get<{ leaked_dependencies: Dependency[] }>(`/apps/imports/${appId}/check-dependencies`)
|
|
},
|
|
})
|
|
}
|
|
|
|
export const useModelInList = (currentProvider?: ModelProvider, modelId?: string) => {
|
|
const provider = currentProvider?.provider
|
|
return useQuery({
|
|
queryKey: ['modelInList', provider, modelId],
|
|
queryFn: async () => {
|
|
if (!modelId || !provider)
|
|
return false
|
|
try {
|
|
const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${provider}/models`)
|
|
return !!modelId && modelsData.data.some(item => item.model === modelId)
|
|
}
|
|
catch {
|
|
return false
|
|
}
|
|
},
|
|
enabled: !!modelId && !!provider,
|
|
})
|
|
}
|
|
|
|
export const usePluginInfo = (providerName?: string) => {
|
|
return useQuery({
|
|
queryKey: ['pluginInfo', providerName],
|
|
queryFn: async () => {
|
|
if (!providerName)
|
|
return null
|
|
const parts = providerName.split('/')
|
|
const org = parts[0]
|
|
const name = parts[1]
|
|
try {
|
|
const response = await fetchPluginInfoFromMarketPlace({ org: org!, name: name! })
|
|
return response.data.plugin.category === PluginCategoryEnum.model ? response.data.plugin : null
|
|
}
|
|
catch {
|
|
return null
|
|
}
|
|
},
|
|
enabled: !!providerName,
|
|
})
|
|
}
|
|
|
|
export const useFetchDynamicOptions = (plugin_id: string, provider: string, action: string, parameter: string, provider_type?: string, extra?: Record<string, unknown>) => {
|
|
return useMutation({
|
|
mutationFn: () => get<{ options: FormOption[] }>('/workspaces/current/plugin/parameters/dynamic-options', {
|
|
params: {
|
|
plugin_id,
|
|
provider,
|
|
action,
|
|
parameter,
|
|
provider_type,
|
|
...extra,
|
|
},
|
|
}),
|
|
})
|
|
}
|
|
|
|
export const usePluginReadme = ({ plugin_unique_identifier, language }: { plugin_unique_identifier: string, language?: string }) => {
|
|
return useQuery({
|
|
queryKey: ['pluginReadme', plugin_unique_identifier, language],
|
|
queryFn: () => get<{ readme: string }>('/workspaces/current/plugin/readme', { params: { plugin_unique_identifier, language } }, { silent: true }),
|
|
enabled: !!plugin_unique_identifier,
|
|
retry: 0,
|
|
})
|
|
}
|
|
|
|
export const usePluginReadmeAsset = ({ file_name, plugin_unique_identifier }: { file_name?: string, plugin_unique_identifier?: string }) => {
|
|
const normalizedFileName = file_name?.replace(/^\.\/_assets\//, '').replace(/^_assets\//, '')
|
|
const isAssetFile = file_name?.startsWith('./_assets') || file_name?.startsWith('_assets')
|
|
return useQuery({
|
|
queryKey: ['pluginReadmeAsset', plugin_unique_identifier, normalizedFileName],
|
|
queryFn: () => get<Blob>('/workspaces/current/plugin/asset', { params: { plugin_unique_identifier, file_name: normalizedFileName } }, { silent: true }),
|
|
enabled: !!plugin_unique_identifier && !!isAssetFile,
|
|
})
|
|
}
|