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

enhancement: support plugin cache

Yeuoly пре 10 месеци
родитељ
комит
9679ae00f3

+ 0 - 1
internal/core/plugin_manager/manager.go

@@ -112,7 +112,6 @@ func (p *PluginManager) GetAsset(id string) ([]byte, error) {
 }
 
 func (p *PluginManager) Init(configuration *app.Config) {
-	// TODO: init plugin manager
 	log.Info("start plugin manager daemon...")
 
 	// init redis client

+ 0 - 1
internal/db/init.go

@@ -82,7 +82,6 @@ func autoMigrate() error {
 		models.PluginInstallation{},
 		models.Endpoint{},
 		models.ServerlessRuntime{},
-		models.EndpointInstallation{},
 		models.ToolInstallation{},
 		models.AIModelInstallation{},
 	)

+ 56 - 6
internal/service/endpoint.go

@@ -20,6 +20,7 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/requests"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache/helper"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/routine"
 )
 
@@ -216,14 +217,18 @@ func ListEndpoints(tenant_id string, page int, page_size int) *entities.Response
 			return entities.NewErrorResponse(-404, fmt.Sprintf("failed to find plugin installation: %v", err))
 		}
 
-		plugin, err := db.GetOne[models.Plugin](
-			db.Equal("plugin_unique_identifier", plugin_installation.PluginUniqueIdentifier),
+		plugin_unique_identifier, err := plugin_entities.NewPluginUniqueIdentifier(
+			plugin_installation.PluginUniqueIdentifier,
 		)
 		if err != nil {
-			return entities.NewErrorResponse(-404, fmt.Sprintf("failed to find plugin: %v", err))
+			return entities.NewErrorResponse(-500, fmt.Sprintf("failed to parse plugin unique identifier: %v", err))
+		}
+
+		plugin_declaration, err := helper.CombinedGetPluginDeclaration(plugin_unique_identifier)
+		if err != nil {
+			return entities.NewErrorResponse(-500, fmt.Sprintf("failed to get plugin declaration: %v", err))
 		}
 
-		plugin_declaration := plugin.Declaration
 		if plugin_declaration.Endpoint == nil {
 			return entities.NewErrorResponse(-404, "plugin does not have an endpoint")
 		}
@@ -256,6 +261,51 @@ func ListEndpoints(tenant_id string, page int, page_size int) *entities.Response
 }
 
 func ListPluginEndpoints(tenant_id string, plugin_unique_identifier plugin_entities.PluginUniqueIdentifier, page int, page_size int) *entities.Response {
-	// TODO:
-	return nil
+	endpoints, err := db.GetAll[models.Endpoint](
+		db.Equal("plugin_id", plugin_unique_identifier.PluginID()),
+		db.Equal("tenant_id", tenant_id),
+		db.OrderBy("created_at", true),
+		db.Page(page, page_size),
+	)
+	if err != nil {
+		return entities.NewErrorResponse(-500, fmt.Sprintf("failed to list endpoints: %v", err))
+	}
+
+	manager := plugin_manager.Manager()
+	if manager == nil {
+		return entities.NewErrorResponse(-500, "failed to get plugin manager")
+	}
+
+	// decrypt settings
+	for i, endpoint := range endpoints {
+		plugin_declaration, err := helper.CombinedGetPluginDeclaration(plugin_unique_identifier)
+		if err != nil {
+			return entities.NewErrorResponse(-500, fmt.Sprintf("failed to get plugin declaration: %v", err))
+		}
+
+		decrypted_settings, err := manager.BackwardsInvocation().InvokeEncrypt(&dify_invocation.InvokeEncryptRequest{
+			BaseInvokeDifyRequest: dify_invocation.BaseInvokeDifyRequest{
+				TenantId: tenant_id,
+				UserId:   "",
+				Type:     dify_invocation.INVOKE_TYPE_ENCRYPT,
+			},
+			InvokeEncryptSchema: dify_invocation.InvokeEncryptSchema{
+				Opt:       dify_invocation.ENCRYPT_OPT_DECRYPT,
+				Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
+				Identity:  endpoint.ID,
+				Data:      endpoint.GetSettings(),
+				Config:    plugin_declaration.Endpoint.Settings,
+			},
+		})
+		if err != nil {
+			return entities.NewErrorResponse(-500, fmt.Sprintf("failed to decrypt settings: %v", err))
+		}
+
+		endpoint.SetSettings(decrypted_settings)
+		endpoint.Declaration = plugin_declaration.Endpoint
+
+		endpoints[i] = endpoint
+	}
+
+	return entities.NewSuccessResponse(endpoints)
 }

+ 13 - 7
internal/service/manage_plugin.go

@@ -7,7 +7,7 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
-	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache/helper"
 )
 
 func ListPlugins(tenant_id string, page int, page_size int) *entities.Response {
@@ -35,20 +35,26 @@ func ListPlugins(tenant_id string, page int, page_size int) *entities.Response {
 	data := make([]installation, 0, len(plugin_installations))
 
 	for _, plugin_installation := range plugin_installations {
-		plugin, err := cache.Get[models.Plugin](plugin_installation.PluginUniqueIdentifier)
+		plugin_unique_identifier, err := plugin_entities.NewPluginUniqueIdentifier(
+			plugin_installation.PluginUniqueIdentifier,
+		)
+		if err != nil {
+			return entities.NewErrorResponse(-500, err.Error())
+		}
+
+		plugin_declaration, err := helper.CombinedGetPluginDeclaration(plugin_unique_identifier)
 		if err != nil {
 			return entities.NewErrorResponse(-500, err.Error())
 		}
 
-		declaration := plugin.Declaration
 		data = append(data, installation{
 			ID:             plugin_installation.ID,
-			Name:           declaration.Name,
-			PluginID:       plugin.ID,
+			Name:           plugin_declaration.Name,
+			PluginID:       plugin_unique_identifier.PluginID(),
 			InstallationID: plugin_installation.ID,
-			Description:    &declaration,
+			Description:    plugin_declaration,
 			RuntimeType:    plugin_entities.PluginRuntimeType(plugin_installation.RuntimeType),
-			Version:        declaration.Version,
+			Version:        plugin_declaration.Version,
 			CreatedAt:      plugin_installation.CreatedAt,
 			UpdatedAt:      plugin_installation.UpdatedAt,
 		})

+ 19 - 16
internal/service/setup_endpoint.go

@@ -10,6 +10,7 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache/helper"
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/encryption"
 )
 
@@ -29,24 +30,21 @@ func SetupEndpoint(
 	}
 
 	// try get plugin
-	plugin, err := db.GetOne[models.Plugin](
-		db.Equal("plugin_unique_identifier", plugin_unique_identifier.String()),
-	)
+	plugin_declaration, err := helper.CombinedGetPluginDeclaration(plugin_unique_identifier)
 	if err != nil {
 		return entities.NewErrorResponse(-404, fmt.Sprintf("failed to find plugin: %v", err))
 	}
 
-	declaration := plugin.Declaration
-	if !declaration.Resource.Permission.AllowRegisterEndpoint() {
+	if !plugin_declaration.Resource.Permission.AllowRegisterEndpoint() {
 		return entities.NewErrorResponse(-403, "permission denied")
 	}
 
-	if declaration.Endpoint == nil {
+	if plugin_declaration.Endpoint == nil {
 		return entities.NewErrorResponse(-404, "plugin does not have an endpoint")
 	}
 
 	// check settings
-	if err := plugin_entities.ValidateProviderConfigs(settings, declaration.Endpoint.Settings); err != nil {
+	if err := plugin_entities.ValidateProviderConfigs(settings, plugin_declaration.Endpoint.Settings); err != nil {
 		return entities.NewErrorResponse(-400, fmt.Sprintf("failed to validate settings: %v", err))
 	}
 
@@ -79,7 +77,7 @@ func SetupEndpoint(
 				Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
 				Identity:  endpoint.ID,
 				Data:      settings,
-				Config:    declaration.Endpoint.Settings,
+				Config:    plugin_declaration.Endpoint.Settings,
 			},
 		},
 	)
@@ -152,15 +150,20 @@ func UpdateEndpoint(endpoint_id string, tenant_id string, user_id string, settin
 		return entities.NewErrorResponse(-404, fmt.Sprintf("failed to find plugin installation: %v", err))
 	}
 
-	// get plugin
-	plugin, err := db.GetOne[models.Plugin](
-		db.Equal("plugin_unique_identifier", installation.PluginUniqueIdentifier),
+	plugin_unique_identifier, err := plugin_entities.NewPluginUniqueIdentifier(
+		installation.PluginUniqueIdentifier,
 	)
 	if err != nil {
+		return entities.NewErrorResponse(-500, fmt.Sprintf("failed to parse plugin unique identifier: %v", err))
+	}
+
+	// get plugin
+	plugin_declaration, err := helper.CombinedGetPluginDeclaration(plugin_unique_identifier)
+	if err != nil {
 		return entities.NewErrorResponse(-404, fmt.Sprintf("failed to find plugin: %v", err))
 	}
 
-	if plugin.Declaration.Endpoint == nil {
+	if plugin_declaration.Endpoint == nil {
 		return entities.NewErrorResponse(-404, "plugin does not have an endpoint")
 	}
 
@@ -182,7 +185,7 @@ func UpdateEndpoint(endpoint_id string, tenant_id string, user_id string, settin
 				Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
 				Identity:  installation.ID,
 				Data:      endpoint.GetSettings(),
-				Config:    plugin.Declaration.Endpoint.Settings,
+				Config:    plugin_declaration.Endpoint.Settings,
 			},
 		},
 	)
@@ -190,7 +193,7 @@ func UpdateEndpoint(endpoint_id string, tenant_id string, user_id string, settin
 		return entities.NewErrorResponse(-500, fmt.Sprintf("failed to decrypt settings: %v", err))
 	}
 
-	masked_settings := encryption.MaskConfigCredentials(original_settings, plugin.Declaration.Endpoint.Settings)
+	masked_settings := encryption.MaskConfigCredentials(original_settings, plugin_declaration.Endpoint.Settings)
 
 	// check if settings is changed, replace the value is the same as masked_settings
 	for setting_name, value := range settings {
@@ -200,7 +203,7 @@ func UpdateEndpoint(endpoint_id string, tenant_id string, user_id string, settin
 	}
 
 	// check settings
-	if err := plugin_entities.ValidateProviderConfigs(settings, plugin.Declaration.Endpoint.Settings); err != nil {
+	if err := plugin_entities.ValidateProviderConfigs(settings, plugin_declaration.Endpoint.Settings); err != nil {
 		return entities.NewErrorResponse(-400, fmt.Sprintf("failed to validate settings: %v", err))
 	}
 
@@ -217,7 +220,7 @@ func UpdateEndpoint(endpoint_id string, tenant_id string, user_id string, settin
 				Namespace: dify_invocation.ENCRYPT_NAMESPACE_ENDPOINT,
 				Identity:  endpoint.ID,
 				Data:      settings,
-				Config:    plugin.Declaration.Endpoint.Settings,
+				Config:    plugin_declaration.Endpoint.Settings,
 			},
 		},
 	)

+ 0 - 15
internal/types/models/curd/atomic.go

@@ -100,21 +100,6 @@ func InstallPlugin(
 			}
 		}
 
-		// create endpoint installation
-		if declaration.Endpoint != nil {
-			endpoint_installation := &models.EndpointInstallation{
-				PluginID:               plugin_to_be_returns.PluginID,
-				PluginUniqueIdentifier: plugin_to_be_returns.PluginUniqueIdentifier,
-				TenantID:               tenant_id,
-				Declaration:            *declaration.Endpoint,
-			}
-
-			err := db.Create(endpoint_installation, tx)
-			if err != nil {
-				return err
-			}
-		}
-
 		// create model installation
 		if declaration.Model != nil {
 			model_installation := &models.AIModelInstallation{

+ 0 - 8
internal/types/models/endpoint.go

@@ -7,14 +7,6 @@ import (
 	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 )
 
-type EndpointInstallation struct {
-	Model
-	TenantID               string                                      `json:"tenant_id" gorm:"column:tenant_id;type:uuid;index;not null"`
-	PluginUniqueIdentifier string                                      `json:"plugin_unique_identifier" gorm:"index;size:127"`
-	PluginID               string                                      `json:"plugin_id" gorm:"index;size:127"`
-	Declaration            plugin_entities.EndpointProviderDeclaration `json:"declaration" gorm:"serializer:json;type:text;size:65535;not null"`
-}
-
 // HookID is a pointer to plugin id and tenant id, using it to identify the endpoint plugin
 type Endpoint struct {
 	Model

+ 24 - 0
internal/utils/cache/helper/redis.go

@@ -0,0 +1,24 @@
+package helper
+
+import (
+	"github.com/langgenius/dify-plugin-daemon/internal/db"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/cache"
+)
+
+func CombinedGetPluginDeclaration(plugin_unique_identifier plugin_entities.PluginUniqueIdentifier) (*plugin_entities.PluginDeclaration, error) {
+	return cache.AutoGetWithGetter(
+		plugin_unique_identifier.String(),
+		func() (*plugin_entities.PluginDeclaration, error) {
+			model, err := db.GetOne[models.Plugin](
+				db.Equal("plugin_unique_identifier", plugin_unique_identifier.String()),
+			)
+			if err != nil {
+				return nil, err
+			}
+
+			return &model.Declaration, nil
+		},
+	)
+}

+ 49 - 0
internal/utils/cache/redis_auto_type.go

@@ -0,0 +1,49 @@
+package cache
+
+import (
+	"errors"
+	"reflect"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
+	"github.com/redis/go-redis/v9"
+)
+
+// Get the value with key
+func AutoGet[T any](key string, context ...redis.Cmdable) (*T, error) {
+	return AutoGetWithGetter(key, func() (*T, error) {
+		return nil, errors.New("not found")
+	}, context...)
+}
+
+// Get the value with key, fallback to getter if not found, and set the value to cache
+func AutoGetWithGetter[T any](key string, getter func() (*T, error), context ...redis.Cmdable) (*T, error) {
+	if client == nil {
+		return nil, ErrDBNotInit
+	}
+
+	var result_tmpl T
+
+	// fetch full type info
+	full_type_info := reflect.TypeOf(result_tmpl)
+	full_type_name := full_type_info.Name()
+
+	key = serialKey("auto_type", full_type_name, key)
+	val, err := getCmdable(context...).Get(ctx, key).Result()
+	if err != nil {
+		if err == redis.Nil {
+			value, err := getter()
+			if err != nil {
+				return nil, err
+			}
+
+			if err := Store(key, value, 0, context...); err != nil {
+				return nil, err
+			}
+			return value, nil
+		}
+		return nil, err
+	}
+
+	result, err := parser.UnmarshalJson[T](val)
+	return &result, err
+}