import type { ActivePluginType } from './constants' import type { CollectionsAndPluginsSearchParams, MarketplaceCollection, PluginsSearchParams, } from '@/app/components/plugins/marketplace/types' import type { Plugin, PluginsFromMarketplaceResponse } from '@/app/components/plugins/types' import { PluginCategoryEnum } from '@/app/components/plugins/types' import { APP_VERSION, IS_MARKETPLACE, MARKETPLACE_API_PREFIX, } from '@/config' import { postMarketplace } from '@/service/base' import { getMarketplaceUrl } from '@/utils/var' import { PLUGIN_TYPE_SEARCH_MAP } from './constants' type MarketplaceFetchOptions = { signal?: AbortSignal } const getMarketplaceHeaders = () => new Headers({ 'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0', }) export const getPluginIconInMarketplace = (plugin: Plugin) => { if (plugin.type === 'bundle') return `${MARKETPLACE_API_PREFIX}/bundles/${plugin.org}/${plugin.name}/icon` return `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon` } export const getFormattedPlugin = (bundle: Plugin): Plugin => { if (bundle.type === 'bundle') { return { ...bundle, icon: getPluginIconInMarketplace(bundle), brief: bundle.description, // @ts-expect-error I do not have enough information label: bundle.labels, } } return { ...bundle, icon: getPluginIconInMarketplace(bundle), } } export const getPluginLinkInMarketplace = (plugin: Plugin, params?: Record) => { if (plugin.type === 'bundle') return getMarketplaceUrl(`/bundles/${plugin.org}/${plugin.name}`, params) return getMarketplaceUrl(`/plugins/${plugin.org}/${plugin.name}`, params) } export const getPluginDetailLinkInMarketplace = (plugin: Plugin) => { if (plugin.type === 'bundle') return `/bundles/${plugin.org}/${plugin.name}` return `/plugins/${plugin.org}/${plugin.name}` } export const getMarketplacePluginsByCollectionId = async ( collectionId: string, query?: CollectionsAndPluginsSearchParams, options?: MarketplaceFetchOptions, ) => { let plugins: Plugin[] = [] try { const url = `${MARKETPLACE_API_PREFIX}/collections/${collectionId}/plugins` const headers = getMarketplaceHeaders() const marketplaceCollectionPluginsData = await globalThis.fetch( url, { cache: 'no-store', method: 'POST', headers, signal: options?.signal, body: JSON.stringify({ category: query?.category, exclude: query?.exclude, type: query?.type, }), }, ) const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json() plugins = (marketplaceCollectionPluginsDataJson.data.plugins || []).map((plugin: Plugin) => getFormattedPlugin(plugin)) } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { plugins = [] } return plugins } export const getMarketplaceCollectionsAndPlugins = async ( query?: CollectionsAndPluginsSearchParams, options?: MarketplaceFetchOptions, ) => { let marketplaceCollections: MarketplaceCollection[] = [] let marketplaceCollectionPluginsMap: Record = {} try { let marketplaceUrl = `${MARKETPLACE_API_PREFIX}/collections?page=1&page_size=100` if (query?.condition) marketplaceUrl += `&condition=${query.condition}` if (query?.type) marketplaceUrl += `&type=${query.type}` const headers = getMarketplaceHeaders() const marketplaceCollectionsData = await globalThis.fetch( marketplaceUrl, { headers, cache: 'no-store', signal: options?.signal, }, ) const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json() marketplaceCollections = marketplaceCollectionsDataJson.data.collections || [] await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => { const plugins = await getMarketplacePluginsByCollectionId(collection.name, query, options) marketplaceCollectionPluginsMap[collection.name] = plugins })) } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { marketplaceCollections = [] marketplaceCollectionPluginsMap = {} } return { marketplaceCollections, marketplaceCollectionPluginsMap, } } export const getMarketplacePlugins = async ( queryParams: PluginsSearchParams | undefined, pageParam: number, signal?: AbortSignal, ) => { if (!queryParams) { return { plugins: [] as Plugin[], total: 0, page: 1, pageSize: 40, } } const { query, sortBy, sortOrder, category, tags, type, pageSize = 40, } = queryParams const pluginOrBundle = type === 'bundle' ? 'bundles' : 'plugins' try { const res = await postMarketplace<{ data: PluginsFromMarketplaceResponse }>(`/${pluginOrBundle}/search/advanced`, { body: { page: pageParam, page_size: pageSize, query, sort_by: sortBy, sort_order: sortOrder, category: category !== 'all' ? category : '', tags, type, }, signal, }) const resPlugins = res.data.bundles || res.data.plugins || [] return { plugins: resPlugins.map(plugin => getFormattedPlugin(plugin)), total: res.data.total, page: pageParam, pageSize, } } catch { return { plugins: [], total: 0, page: pageParam, pageSize, } } } export const getMarketplaceListCondition = (pluginType: string) => { if ([PluginCategoryEnum.tool, PluginCategoryEnum.agent, PluginCategoryEnum.model, PluginCategoryEnum.datasource, PluginCategoryEnum.trigger].includes(pluginType as PluginCategoryEnum)) return `category=${pluginType}` if (pluginType === PluginCategoryEnum.extension) return 'category=endpoint' if (pluginType === 'bundle') return 'type=bundle' return '' } export const getMarketplaceListFilterType = (category: ActivePluginType) => { if (category === PLUGIN_TYPE_SEARCH_MAP.all) return undefined if (category === PLUGIN_TYPE_SEARCH_MAP.bundle) return 'bundle' return 'plugin' } export function getCollectionsParams(category: ActivePluginType): CollectionsAndPluginsSearchParams { if (category === PLUGIN_TYPE_SEARCH_MAP.all) { return {} } return { category, condition: getMarketplaceListCondition(category), type: getMarketplaceListFilterType(category), } }