Преглед изворни кода

feat: support upload bundle

Yeuoly пре 5 месеци
родитељ
комит
db68ae4a73

+ 5 - 0
api/configs/feature/__init__.py

@@ -166,6 +166,11 @@ class PluginConfig(BaseSettings):
         default=15728640,
     )
 
+    PLUGIN_MAX_BUNDLE_SIZE: PositiveInt = Field(
+        description="Maximum allowed size for plugin bundles in bytes",
+        default=15728640 * 12,
+    )
+
 
 class MarketplaceConfig(BaseSettings):
     """

+ 21 - 0
api/controllers/console/workspace/plugin.py

@@ -94,6 +94,26 @@ class PluginUploadFromGithubApi(Resource):
         return jsonable_encoder(response)
 
 
+class PluginUploadFromBundleApi(Resource):
+    @setup_required
+    @login_required
+    @account_initialization_required
+    @plugin_permission_required(install_required=True)
+    def post(self):
+        tenant_id = current_user.current_tenant_id
+
+        file = request.files["bundle"]
+
+        # check file size
+        if file.content_length > dify_config.PLUGIN_MAX_BUNDLE_SIZE:
+            raise ValueError("File size exceeds the maximum allowed size")
+
+        content = file.read()
+        response = PluginService.upload_bundle(tenant_id, content)
+
+        return jsonable_encoder(response)
+
+
 class PluginInstallFromPkgApi(Resource):
     @setup_required
     @login_required
@@ -346,6 +366,7 @@ api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
 api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
 api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")
 api.add_resource(PluginUploadFromGithubApi, "/workspaces/current/plugin/upload/github")
+api.add_resource(PluginUploadFromBundleApi, "/workspaces/current/plugin/upload/bundle")
 api.add_resource(PluginInstallFromPkgApi, "/workspaces/current/plugin/install/pkg")
 api.add_resource(PluginInstallFromGithubApi, "/workspaces/current/plugin/install/github")
 api.add_resource(PluginUpgradeFromMarketplaceApi, "/workspaces/current/plugin/upgrade/marketplace")

+ 30 - 0
api/core/plugin/entities/bundle.py

@@ -0,0 +1,30 @@
+from enum import Enum
+
+from pydantic import BaseModel
+
+from core.plugin.entities.plugin import PluginDeclaration, PluginInstallationSource
+
+
+class PluginBundleDependency(BaseModel):
+    class Type(str, Enum):
+        Github = PluginInstallationSource.Github.value
+        Marketplace = PluginInstallationSource.Marketplace.value
+        Package = PluginInstallationSource.Package.value
+
+    class Github(BaseModel):
+        repo_address: str
+        repo: str
+        release: str
+        packages: str
+
+    class Marketplace(BaseModel):
+        organization: str
+        plugin: str
+        version: str
+
+    class Package(BaseModel):
+        unique_identifier: str
+        manifest: PluginDeclaration
+
+    type: Type
+    value: Github | Marketplace | Package

+ 3 - 3
api/core/plugin/entities/plugin.py

@@ -162,9 +162,9 @@ class GenericProviderID:
 
 class PluginDependency(BaseModel):
     class Type(str, Enum):
-        Github = "github"
-        Marketplace = "marketplace"
-        Package = "package"
+        Github = PluginInstallationSource.Github.value
+        Marketplace = PluginInstallationSource.Marketplace.value
+        Package = PluginInstallationSource.Package.value
 
     class Github(BaseModel):
         repo: str

+ 19 - 1
api/core/plugin/manager/plugin.py

@@ -2,6 +2,7 @@ from collections.abc import Sequence
 
 from pydantic import BaseModel
 
+from core.plugin.entities.bundle import PluginBundleDependency
 from core.plugin.entities.plugin import (
     PluginDeclaration,
     PluginEntity,
@@ -52,12 +53,29 @@ class PluginInstallationManager(BasePluginManager):
 
         return self._request_with_plugin_daemon_response(
             "POST",
-            f"plugin/{tenant_id}/management/install/upload",
+            f"plugin/{tenant_id}/management/install/upload/package",
             PluginUploadResponse,
             files=body,
             data=data,
         )
 
+    def upload_bundle(
+        self,
+        tenant_id: str,
+        bundle: bytes,
+        verify_signature: bool = False,
+    ) -> Sequence[PluginBundleDependency]:
+        """
+        Upload a plugin bundle and return the dependencies.
+        """
+        return self._request_with_plugin_daemon_response(
+            "POST",
+            f"plugin/{tenant_id}/management/install/upload/bundle",
+            list[PluginBundleDependency],
+            files={"dify_bundle": ("dify_bundle", bundle, "application/octet-stream")},
+            data={"verify_signature": "true" if verify_signature else "false"},
+        )
+
     def install_from_identifiers(
         self, tenant_id: str, identifiers: Sequence[str], source: PluginInstallationSource, meta: dict
     ) -> PluginInstallTaskStartResponse:

+ 11 - 0
api/services/plugin/plugin_service.py

@@ -6,6 +6,7 @@ from configs import dify_config
 from core.helper import marketplace
 from core.helper.download import download_with_size_limit
 from core.helper.marketplace import download_plugin_pkg
+from core.plugin.entities.bundle import PluginBundleDependency
 from core.plugin.entities.plugin import PluginDeclaration, PluginEntity, PluginInstallationSource
 from core.plugin.entities.plugin_daemon import PluginInstallTask, PluginUploadResponse
 from core.plugin.manager.asset import PluginAssetManager
@@ -190,6 +191,16 @@ class PluginService:
         )
 
     @staticmethod
+    def upload_bundle(
+        tenant_id: str, bundle: bytes, verify_signature: bool = False
+    ) -> Sequence[PluginBundleDependency]:
+        """
+        Upload a plugin bundle and return the dependencies.
+        """
+        manager = PluginInstallationManager()
+        return manager.upload_bundle(tenant_id, bundle, verify_signature)
+
+    @staticmethod
     def install_from_local_pkg(tenant_id: str, plugin_unique_identifiers: Sequence[str]):
         manager = PluginInstallationManager()
         return manager.install_from_identifiers(