mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 03:32:23 +08:00
feat: marketplace install
This commit is contained in:
parent
cd27ae4319
commit
c777d55a1c
|
@ -0,0 +1,55 @@
|
|||
import { checkTaskStatus as fetchCheckTaskStatus } from '@/service/plugins'
|
||||
import type { PluginStatus } from '../../types'
|
||||
import { TaskStatus } from '../../types'
|
||||
|
||||
const INTERVAL = 10 * 1000 // 10 seconds
|
||||
|
||||
interface Params {
|
||||
taskId: string
|
||||
pluginUniqueIdentifier: string
|
||||
}
|
||||
|
||||
function checkTaskStatus() {
|
||||
let nextStatus = TaskStatus.running
|
||||
let isStop = false
|
||||
|
||||
const doCheckStatus = async ({
|
||||
taskId,
|
||||
pluginUniqueIdentifier,
|
||||
}: Params) => {
|
||||
if (isStop) return
|
||||
const { plugins } = await fetchCheckTaskStatus(taskId)
|
||||
const plugin = plugins.find((p: PluginStatus) => p.plugin_unique_identifier === pluginUniqueIdentifier)
|
||||
if (!plugin) {
|
||||
nextStatus = TaskStatus.failed
|
||||
Promise.reject(new Error('Plugin package not found'))
|
||||
return
|
||||
}
|
||||
nextStatus = plugin.status
|
||||
if (nextStatus === TaskStatus.running) {
|
||||
setTimeout(async () => {
|
||||
await doCheckStatus({
|
||||
taskId,
|
||||
pluginUniqueIdentifier,
|
||||
})
|
||||
}, INTERVAL)
|
||||
return
|
||||
}
|
||||
if (nextStatus === TaskStatus.failed) {
|
||||
Promise.reject(plugin.message)
|
||||
return
|
||||
}
|
||||
return ({
|
||||
status: nextStatus,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
check: doCheckStatus,
|
||||
stop: () => {
|
||||
isStop = true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default checkTaskStatus
|
|
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'
|
|||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type InstallFromLocalPackageProps = {
|
||||
interface InstallFromLocalPackageProps {
|
||||
file: File
|
||||
onSuccess: () => void
|
||||
onClose: () => void
|
||||
|
@ -56,8 +56,10 @@ const InstallFromLocalPackage: React.FC<InstallFromLocalPackageProps> = ({
|
|||
setStep(InstallStep.installed)
|
||||
}, [])
|
||||
|
||||
const handleFailed = useCallback(() => {
|
||||
const handleFailed = useCallback((errorMsg?: string) => {
|
||||
setStep(InstallStep.installFailed)
|
||||
if (errorMsg)
|
||||
setErrorMsg(errorMsg)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,20 +5,20 @@ import type { PluginDeclaration } from '../../../types'
|
|||
import Card from '../../../card'
|
||||
import { pluginManifestToCardPluginProps } from '../../utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { sleep } from '@/utils'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { RiLoader2Line } from '@remixicon/react'
|
||||
import Badge, { BadgeState } from '@/app/components/base/badge/index'
|
||||
import { installPackageFromLocal } from '@/service/plugins'
|
||||
import checkTaskStatus from '../../base/check-task-status'
|
||||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
uniqueIdentifier: string
|
||||
payload: PluginDeclaration
|
||||
onCancel: () => void
|
||||
onInstalled: () => void
|
||||
onFailed: () => void
|
||||
onFailed: (message?: string) => void
|
||||
}
|
||||
|
||||
const Installed: FC<Props> = ({
|
||||
|
@ -30,18 +30,41 @@ const Installed: FC<Props> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isInstalling, setIsInstalling] = React.useState(false)
|
||||
const {
|
||||
check,
|
||||
stop,
|
||||
} = checkTaskStatus()
|
||||
|
||||
const handleCancel = () => {
|
||||
stop()
|
||||
onCancel()
|
||||
}
|
||||
|
||||
const handleInstall = async () => {
|
||||
if (isInstalling) return
|
||||
setIsInstalling(true)
|
||||
try {
|
||||
await installPackageFromLocal(uniqueIdentifier)
|
||||
const {
|
||||
all_installed: isInstalled,
|
||||
task_id: taskId,
|
||||
} = await installPackageFromLocal(uniqueIdentifier)
|
||||
if (isInstalled) {
|
||||
onInstalled()
|
||||
return
|
||||
}
|
||||
await check({
|
||||
taskId,
|
||||
pluginUniqueIdentifier: uniqueIdentifier,
|
||||
})
|
||||
onInstalled()
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e === 'string') {
|
||||
onFailed(e)
|
||||
return
|
||||
}
|
||||
onFailed()
|
||||
}
|
||||
await sleep(1500)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -67,7 +90,7 @@ const Installed: FC<Props> = ({
|
|||
{/* Action Buttons */}
|
||||
<div className='flex p-6 pt-5 justify-end items-center gap-2 self-stretch'>
|
||||
{!isInstalling && (
|
||||
<Button variant='secondary' className='min-w-[72px]' onClick={onCancel}>
|
||||
<Button variant='secondary' className='min-w-[72px]' onClick={handleCancel}>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -10,15 +10,15 @@ import { useTranslation } from 'react-i18next'
|
|||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type InstallFromMarketplaceProps = {
|
||||
packageId: string
|
||||
interface InstallFromMarketplaceProps {
|
||||
uniqueIdentifier: string
|
||||
manifest: PluginDeclaration
|
||||
onSuccess: () => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
||||
packageId,
|
||||
uniqueIdentifier,
|
||||
manifest,
|
||||
onSuccess,
|
||||
onClose,
|
||||
|
@ -26,6 +26,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
|||
const { t } = useTranslation()
|
||||
// readyToInstall -> check installed -> installed/failed
|
||||
const [step, setStep] = useState<InstallStep>(InstallStep.readyToInstall)
|
||||
const [errorMsg, setErrorMsg] = useState<string | null>(null)
|
||||
|
||||
// TODO: check installed in beta version.
|
||||
|
||||
|
@ -41,8 +42,10 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
|||
setStep(InstallStep.installed)
|
||||
}, [])
|
||||
|
||||
const handleFailed = useCallback(() => {
|
||||
const handleFailed = useCallback((errorMsg?: string) => {
|
||||
setStep(InstallStep.installFailed)
|
||||
if (errorMsg)
|
||||
setErrorMsg(errorMsg)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
@ -60,6 +63,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
|||
{
|
||||
step === InstallStep.readyToInstall && (
|
||||
<Install
|
||||
uniqueIdentifier={uniqueIdentifier}
|
||||
payload={manifest!}
|
||||
onCancel={onClose}
|
||||
onInstalled={handleInstalled}
|
||||
|
@ -72,6 +76,7 @@ const InstallFromMarketplace: React.FC<InstallFromMarketplaceProps> = ({
|
|||
<Installed
|
||||
payload={manifest!}
|
||||
isFailed={step === InstallStep.installFailed}
|
||||
errMsg={errorMsg}
|
||||
onCancel={onSuccess}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -6,21 +6,24 @@ import type { PluginDeclaration } from '../../../types'
|
|||
import Card from '../../../card'
|
||||
import { pluginManifestToCardPluginProps } from '../../utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { sleep } from '@/utils'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiLoader2Line } from '@remixicon/react'
|
||||
import Badge, { BadgeState } from '@/app/components/base/badge/index'
|
||||
import { installPackageFromMarketPlace } from '@/service/plugins'
|
||||
import checkTaskStatus from '../../base/check-task-status'
|
||||
|
||||
const i18nPrefix = 'plugin.installModal'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
uniqueIdentifier: string
|
||||
payload: PluginDeclaration
|
||||
onCancel: () => void
|
||||
onInstalled: () => void
|
||||
onFailed: () => void
|
||||
onFailed: (message?: string) => void
|
||||
}
|
||||
|
||||
const Installed: FC<Props> = ({
|
||||
uniqueIdentifier,
|
||||
payload,
|
||||
onCancel,
|
||||
onInstalled,
|
||||
|
@ -28,13 +31,42 @@ const Installed: FC<Props> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [isInstalling, setIsInstalling] = React.useState(false)
|
||||
const {
|
||||
check,
|
||||
stop,
|
||||
} = checkTaskStatus()
|
||||
|
||||
const handleCancel = () => {
|
||||
stop()
|
||||
onCancel()
|
||||
}
|
||||
|
||||
const handleInstall = async () => {
|
||||
if (isInstalling) return
|
||||
setIsInstalling(true)
|
||||
await sleep(1500)
|
||||
onInstalled()
|
||||
// onFailed()
|
||||
|
||||
try {
|
||||
const {
|
||||
all_installed: isInstalled,
|
||||
task_id: taskId,
|
||||
} = await installPackageFromMarketPlace(uniqueIdentifier)
|
||||
if (isInstalled) {
|
||||
onInstalled()
|
||||
return
|
||||
}
|
||||
await check({
|
||||
taskId,
|
||||
pluginUniqueIdentifier: uniqueIdentifier,
|
||||
})
|
||||
onInstalled()
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e === 'string') {
|
||||
onFailed(e)
|
||||
return
|
||||
}
|
||||
onFailed()
|
||||
}
|
||||
}
|
||||
|
||||
const toInstallVersion = '1.3.0'
|
||||
|
@ -77,7 +109,7 @@ const Installed: FC<Props> = ({
|
|||
{/* Action Buttons */}
|
||||
<div className='flex p-6 pt-5 justify-end items-center gap-2 self-stretch'>
|
||||
{!isInstalling && (
|
||||
<Button variant='secondary' className='min-w-[72px]' onClick={onCancel}>
|
||||
<Button variant='secondary' className='min-w-[72px]' onClick={handleCancel}>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -35,7 +35,7 @@ import { sleep } from '@/utils'
|
|||
|
||||
const PACKAGE_IDS_KEY = 'package-ids'
|
||||
|
||||
export type PluginPageProps = {
|
||||
export interface PluginPageProps {
|
||||
plugins: React.ReactNode
|
||||
marketplace: React.ReactNode
|
||||
}
|
||||
|
@ -74,6 +74,9 @@ const PluginPage = ({
|
|||
(async () => {
|
||||
await sleep(100)
|
||||
if (packageId) {
|
||||
// setManifest(toolNotionManifest)
|
||||
// TODO
|
||||
// const data = await fetchManifest(encodeURIComponent(packageId))
|
||||
setManifest(toolNotionManifest)
|
||||
showInstallFromMarketplace()
|
||||
}
|
||||
|
@ -229,7 +232,7 @@ const PluginPage = ({
|
|||
isShowInstallFromMarketplace && (
|
||||
<InstallFromMarketplace
|
||||
manifest={manifest!}
|
||||
packageId={packageId}
|
||||
uniqueIdentifier={packageId}
|
||||
onClose={hideInstallFromMarketplace}
|
||||
onSuccess={hideInstallFromMarketplace}
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,7 @@ export enum PluginSource {
|
|||
debugging = 'remote',
|
||||
}
|
||||
|
||||
export type PluginToolDeclaration = {
|
||||
export interface PluginToolDeclaration {
|
||||
identity: {
|
||||
author: string
|
||||
name: string
|
||||
|
@ -27,17 +27,17 @@ export type PluginToolDeclaration = {
|
|||
credentials_schema: ToolCredential[] // TODO
|
||||
}
|
||||
|
||||
export type PluginEndpointDeclaration = {
|
||||
export interface PluginEndpointDeclaration {
|
||||
settings: ToolCredential[]
|
||||
endpoints: EndpointItem[]
|
||||
}
|
||||
|
||||
export type EndpointItem = {
|
||||
export interface EndpointItem {
|
||||
path: string
|
||||
method: string
|
||||
}
|
||||
|
||||
export type EndpointListItem = {
|
||||
export interface EndpointListItem {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
@ -53,7 +53,7 @@ export type EndpointListItem = {
|
|||
}
|
||||
|
||||
// Plugin manifest
|
||||
export type PluginDeclaration = {
|
||||
export interface PluginDeclaration {
|
||||
version: string
|
||||
author: string
|
||||
icon: string
|
||||
|
@ -70,7 +70,7 @@ export type PluginDeclaration = {
|
|||
model: any // TODO
|
||||
}
|
||||
|
||||
export type PluginDetail = {
|
||||
export interface PluginDetail {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
|
@ -87,7 +87,7 @@ export type PluginDetail = {
|
|||
meta?: any
|
||||
}
|
||||
|
||||
export type Plugin = {
|
||||
export interface Plugin {
|
||||
type: PluginType
|
||||
org: string
|
||||
name: string
|
||||
|
@ -113,7 +113,7 @@ export enum PermissionType {
|
|||
noOne = 'noOne',
|
||||
}
|
||||
|
||||
export type Permissions = {
|
||||
export interface Permissions {
|
||||
canManagement: PermissionType
|
||||
canDebugger: PermissionType
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ export enum InstallStepFromGitHub {
|
|||
installed = 'installed',
|
||||
}
|
||||
|
||||
export type InstallState = {
|
||||
export interface InstallState {
|
||||
step: InstallStepFromGitHub
|
||||
repoUrl: string
|
||||
selectedVersion: string
|
||||
|
@ -133,34 +133,34 @@ export type InstallState = {
|
|||
releases: GitHubRepoReleaseResponse[]
|
||||
}
|
||||
|
||||
export type GitHubUrlInfo = {
|
||||
export interface GitHubUrlInfo {
|
||||
isValid: boolean
|
||||
owner?: string
|
||||
repo?: string
|
||||
}
|
||||
|
||||
// endpoint
|
||||
export type CreateEndpointRequest = {
|
||||
export interface CreateEndpointRequest {
|
||||
plugin_unique_identifier: string
|
||||
settings: Record<string, any>
|
||||
name: string
|
||||
}
|
||||
export type EndpointOperationResponse = {
|
||||
export interface EndpointOperationResponse {
|
||||
result: 'success' | 'error'
|
||||
}
|
||||
export type EndpointsRequest = {
|
||||
export interface EndpointsRequest {
|
||||
limit: number
|
||||
page: number
|
||||
plugin_id: string
|
||||
}
|
||||
export type EndpointsResponse = {
|
||||
export interface EndpointsResponse {
|
||||
endpoints: EndpointListItem[]
|
||||
has_more: boolean
|
||||
limit: number
|
||||
total: number
|
||||
page: number
|
||||
}
|
||||
export type UpdateEndpointRequest = {
|
||||
export interface UpdateEndpointRequest {
|
||||
endpoint_id: string
|
||||
settings: Record<string, any>
|
||||
name: string
|
||||
|
@ -175,23 +175,48 @@ export enum InstallStep {
|
|||
installFailed = 'failed',
|
||||
}
|
||||
|
||||
export type GitHubAsset = {
|
||||
export interface GitHubAsset {
|
||||
id: number
|
||||
name: string
|
||||
browser_download_url: string
|
||||
}
|
||||
|
||||
export type GitHubRepoReleaseResponse = {
|
||||
export interface GitHubRepoReleaseResponse {
|
||||
tag_name: string
|
||||
assets: GitHubAsset[]
|
||||
}
|
||||
|
||||
export type InstallPackageResponse = {
|
||||
export interface InstallPackageResponse {
|
||||
plugin_unique_identifier: string
|
||||
all_installed: boolean
|
||||
task_id: string
|
||||
}
|
||||
|
||||
export type DebugInfo = {
|
||||
export interface DebugInfo {
|
||||
key: string
|
||||
host: string
|
||||
port: number
|
||||
}
|
||||
|
||||
export enum TaskStatus {
|
||||
running = 'running',
|
||||
success = 'success',
|
||||
failed = 'failed',
|
||||
}
|
||||
|
||||
export interface PluginStatus {
|
||||
plugin_unique_identifier: string
|
||||
plugin_id: string
|
||||
status: TaskStatus
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface TaskStatusResponse {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
status: string
|
||||
total_plugins: number
|
||||
completed_plugins: number
|
||||
plugins: PluginStatus[]
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import type {
|
|||
EndpointsRequest,
|
||||
EndpointsResponse,
|
||||
InstallPackageResponse,
|
||||
PluginDeclaration,
|
||||
TaskStatusResponse,
|
||||
UpdateEndpointRequest,
|
||||
} from '@/app/components/plugins/types'
|
||||
import type { DebugInfo as DebugInfoTypes } from '@/app/components/plugins/types'
|
||||
|
@ -69,6 +71,16 @@ export const installPackageFromLocal = async (uniqueIdentifier: string) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const fetchManifest = async (uniqueIdentifier: string) => {
|
||||
return get<PluginDeclaration>(`/workspaces/current/plugin/fetch-manifest?plugin_unique_identifier=${uniqueIdentifier}`)
|
||||
}
|
||||
|
||||
export const installPackageFromMarketPlace = async (uniqueIdentifier: string) => {
|
||||
return post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', {
|
||||
body: { plugin_unique_identifiers: [uniqueIdentifier] },
|
||||
})
|
||||
}
|
||||
|
||||
export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => {
|
||||
return get<MarketplaceCollectionsResponse>(url)
|
||||
}
|
||||
|
@ -76,3 +88,7 @@ export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse
|
|||
export const fetchMarketplaceCollectionPlugins: Fetcher<MarketplaceCollectionPluginsResponse, { url: string }> = ({ url }) => {
|
||||
return get<MarketplaceCollectionPluginsResponse>(url)
|
||||
}
|
||||
|
||||
export const checkTaskStatus = async (taskId: string) => {
|
||||
return get<TaskStatusResponse>(`/workspaces/current/plugin/tasks/${taskId}`)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user