mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-07-31 09:32:03 +08:00
refactor: add API endpoint to list latest plugin versions and query it in a asynchronous way (#17695)
This commit is contained in:
parent
2c2efe2e1e
commit
33324ee23d
@ -49,6 +49,23 @@ class PluginListApi(Resource):
|
|||||||
return jsonable_encoder({"plugins": plugins})
|
return jsonable_encoder({"plugins": plugins})
|
||||||
|
|
||||||
|
|
||||||
|
class PluginListLatestVersionsApi(Resource):
|
||||||
|
@setup_required
|
||||||
|
@login_required
|
||||||
|
@account_initialization_required
|
||||||
|
def post(self):
|
||||||
|
req = reqparse.RequestParser()
|
||||||
|
req.add_argument("plugin_ids", type=list, required=True, location="json")
|
||||||
|
args = req.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
versions = PluginService.list_latest_versions(args["plugin_ids"])
|
||||||
|
except PluginDaemonClientSideError as e:
|
||||||
|
raise ValueError(e)
|
||||||
|
|
||||||
|
return jsonable_encoder({"versions": versions})
|
||||||
|
|
||||||
|
|
||||||
class PluginListInstallationsFromIdsApi(Resource):
|
class PluginListInstallationsFromIdsApi(Resource):
|
||||||
@setup_required
|
@setup_required
|
||||||
@login_required
|
@login_required
|
||||||
@ -453,6 +470,7 @@ class PluginFetchPermissionApi(Resource):
|
|||||||
|
|
||||||
api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
|
api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
|
||||||
api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
|
api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
|
||||||
|
api.add_resource(PluginListLatestVersionsApi, "/workspaces/current/plugin/list/latest-versions")
|
||||||
api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
|
api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
|
||||||
api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
|
api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
|
||||||
api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
|
api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
|
||||||
|
@ -120,8 +120,6 @@ class PluginEntity(PluginInstallation):
|
|||||||
name: str
|
name: str
|
||||||
installation_id: str
|
installation_id: str
|
||||||
version: str
|
version: str
|
||||||
latest_version: Optional[str] = None
|
|
||||||
latest_unique_identifier: Optional[str] = None
|
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def set_plugin_id(self):
|
def set_plugin_id(self):
|
||||||
|
@ -94,6 +94,13 @@ class PluginService:
|
|||||||
manager = PluginDebuggingManager()
|
manager = PluginDebuggingManager()
|
||||||
return manager.get_debugging_key(tenant_id)
|
return manager.get_debugging_key(tenant_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list_latest_versions(plugin_ids: Sequence[str]) -> Mapping[str, Optional[LatestPluginCache]]:
|
||||||
|
"""
|
||||||
|
List the latest versions of the plugins
|
||||||
|
"""
|
||||||
|
return PluginService.fetch_latest_plugin_version(plugin_ids)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list(tenant_id: str) -> list[PluginEntity]:
|
def list(tenant_id: str) -> list[PluginEntity]:
|
||||||
"""
|
"""
|
||||||
@ -101,22 +108,6 @@ class PluginService:
|
|||||||
"""
|
"""
|
||||||
manager = PluginInstallationManager()
|
manager = PluginInstallationManager()
|
||||||
plugins = manager.list_plugins(tenant_id)
|
plugins = manager.list_plugins(tenant_id)
|
||||||
plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace]
|
|
||||||
try:
|
|
||||||
manifests = PluginService.fetch_latest_plugin_version(plugin_ids)
|
|
||||||
except Exception:
|
|
||||||
manifests = {}
|
|
||||||
logger.exception("failed to fetch plugin manifests")
|
|
||||||
|
|
||||||
for plugin in plugins:
|
|
||||||
if plugin.source == PluginInstallationSource.Marketplace:
|
|
||||||
if plugin.plugin_id in manifests:
|
|
||||||
latest_plugin_cache = manifests[plugin.plugin_id]
|
|
||||||
if latest_plugin_cache:
|
|
||||||
# set latest_version
|
|
||||||
plugin.latest_version = latest_plugin_cache.version
|
|
||||||
plugin.latest_unique_identifier = latest_plugin_cache.unique_identifier
|
|
||||||
|
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -3,17 +3,23 @@ import { useMemo } from 'react'
|
|||||||
import type { FilterState } from './filter-management'
|
import type { FilterState } from './filter-management'
|
||||||
import FilterManagement from './filter-management'
|
import FilterManagement from './filter-management'
|
||||||
import List from './list'
|
import List from './list'
|
||||||
import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
import { useInstalledLatestVersion, useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||||
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
|
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
|
||||||
import { usePluginPageContext } from './context'
|
import { usePluginPageContext } from './context'
|
||||||
import { useDebounceFn } from 'ahooks'
|
import { useDebounceFn } from 'ahooks'
|
||||||
import Empty from './empty'
|
import Empty from './empty'
|
||||||
import Loading from '../../base/loading'
|
import Loading from '../../base/loading'
|
||||||
|
import { PluginSource } from '../types'
|
||||||
|
|
||||||
const PluginsPanel = () => {
|
const PluginsPanel = () => {
|
||||||
const filters = usePluginPageContext(v => v.filters) as FilterState
|
const filters = usePluginPageContext(v => v.filters) as FilterState
|
||||||
const setFilters = usePluginPageContext(v => v.setFilters)
|
const setFilters = usePluginPageContext(v => v.setFilters)
|
||||||
const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList()
|
const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList()
|
||||||
|
const { data: installedLatestVersion } = useInstalledLatestVersion(
|
||||||
|
pluginList?.plugins
|
||||||
|
.filter(plugin => plugin.source === PluginSource.marketplace)
|
||||||
|
.map(plugin => plugin.plugin_id) ?? [],
|
||||||
|
)
|
||||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||||
const currentPluginID = usePluginPageContext(v => v.currentPluginID)
|
const currentPluginID = usePluginPageContext(v => v.currentPluginID)
|
||||||
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
|
||||||
@ -22,9 +28,17 @@ const PluginsPanel = () => {
|
|||||||
setFilters(filters)
|
setFilters(filters)
|
||||||
}, { wait: 500 })
|
}, { wait: 500 })
|
||||||
|
|
||||||
|
const pluginListWithLatestVersion = useMemo(() => {
|
||||||
|
return pluginList?.plugins.map(plugin => ({
|
||||||
|
...plugin,
|
||||||
|
latest_version: installedLatestVersion?.versions[plugin.plugin_id]?.version ?? '',
|
||||||
|
latest_unique_identifier: installedLatestVersion?.versions[plugin.plugin_id]?.unique_identifier ?? '',
|
||||||
|
})) || []
|
||||||
|
}, [pluginList, installedLatestVersion])
|
||||||
|
|
||||||
const filteredList = useMemo(() => {
|
const filteredList = useMemo(() => {
|
||||||
const { categories, searchQuery, tags } = filters
|
const { categories, searchQuery, tags } = filters
|
||||||
const filteredList = pluginList?.plugins.filter((plugin) => {
|
const filteredList = pluginListWithLatestVersion.filter((plugin) => {
|
||||||
return (
|
return (
|
||||||
(categories.length === 0 || categories.includes(plugin.declaration.category))
|
(categories.length === 0 || categories.includes(plugin.declaration.category))
|
||||||
&& (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
|
&& (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
|
||||||
@ -32,12 +46,12 @@ const PluginsPanel = () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
return filteredList
|
return filteredList
|
||||||
}, [pluginList, filters])
|
}, [pluginListWithLatestVersion, filters])
|
||||||
|
|
||||||
const currentPluginDetail = useMemo(() => {
|
const currentPluginDetail = useMemo(() => {
|
||||||
const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentPluginID)
|
const detail = pluginListWithLatestVersion.find(plugin => plugin.plugin_id === currentPluginID)
|
||||||
return detail
|
return detail
|
||||||
}, [currentPluginID, pluginList?.plugins])
|
}, [currentPluginID, pluginListWithLatestVersion])
|
||||||
|
|
||||||
const handleHide = () => setCurrentPluginID(undefined)
|
const handleHide = () => setCurrentPluginID(undefined)
|
||||||
|
|
||||||
|
@ -318,6 +318,15 @@ export type InstalledPluginListResponse = {
|
|||||||
plugins: PluginDetail[]
|
plugins: PluginDetail[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InstalledLatestVersionResponse = {
|
||||||
|
versions: {
|
||||||
|
[plugin_id: string]: {
|
||||||
|
unique_identifier: string
|
||||||
|
version: string
|
||||||
|
} | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type UninstallPluginResponse = {
|
export type UninstallPluginResponse = {
|
||||||
success: boolean
|
success: boolean
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import type {
|
|||||||
Dependency,
|
Dependency,
|
||||||
GitHubItemAndMarketPlaceDependency,
|
GitHubItemAndMarketPlaceDependency,
|
||||||
InstallPackageResponse,
|
InstallPackageResponse,
|
||||||
|
InstalledLatestVersionResponse,
|
||||||
InstalledPluginListResponse,
|
InstalledPluginListResponse,
|
||||||
PackageDependency,
|
PackageDependency,
|
||||||
Permissions,
|
Permissions,
|
||||||
@ -72,6 +73,19 @@ export const useInstalledPluginList = (disable?: boolean) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useInstalledLatestVersion = (pluginIds: string[]) => {
|
||||||
|
return useQuery<InstalledLatestVersionResponse>({
|
||||||
|
queryKey: [NAME_SPACE, 'installedLatestVersion', pluginIds],
|
||||||
|
queryFn: () => post<InstalledLatestVersionResponse>('/workspaces/current/plugin/list/latest-versions', {
|
||||||
|
body: {
|
||||||
|
plugin_ids: pluginIds,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
enabled: !!pluginIds.length,
|
||||||
|
initialData: pluginIds.length ? undefined : { versions: {} },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const useInvalidateInstalledPluginList = () => {
|
export const useInvalidateInstalledPluginList = () => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
|
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user