Browse Source

feat: install plugins

Yeuoly 11 months ago
parent
commit
8a7bd6a75d

+ 1 - 0
internal/service/install.go

@@ -0,0 +1 @@
+package service

+ 1 - 0
internal/service/install_task/cons.go

@@ -0,0 +1 @@
+package install_task

+ 1 - 0
internal/service/install_task/prod.go

@@ -0,0 +1 @@
+package install_task

+ 70 - 0
internal/service/install_task/state.go

@@ -0,0 +1,70 @@
+package install_task
+
+import (
+	"time"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/db"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
+	"github.com/langgenius/dify-plugin-daemon/internal/types/models/curd"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/strings"
+)
+
+func InstallPlugin(
+	tenant_id string,
+	user_id string,
+	runtime entities.PluginRuntimeInterface,
+	configuration map[string]any,
+) error {
+	identity, err := runtime.Identity()
+	if err != nil {
+		return err
+	}
+
+	plugin := &models.Plugin{
+		PluginID:     identity,
+		Refers:       0,
+		Checksum:     runtime.Checksum(),
+		InstallType:  runtime.Type(),
+		ManifestType: runtime.Configuration().Type,
+	}
+
+	plugin, installation, err := curd.CreatePlugin(tenant_id, user_id, plugin, configuration)
+	if err != nil {
+		return err
+	}
+
+	// check if there is a webhook for the plugin
+	if runtime.Configuration().Resource.Permission.AllowRegistryWebhook() {
+		_, err := InstallWebhook(plugin.PluginID, installation.ID, tenant_id, user_id)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// installs a plugin to db,
+// returns the webhook id
+func InstallWebhook(plugin_id string, installation_id string, tenant_id string, user_id string) (string, error) {
+	installation := &models.Webhook{
+		HookID:               strings.RandomString(64),
+		PluginID:             plugin_id,
+		TenantID:             tenant_id,
+		UserID:               user_id,
+		ExpiredAt:            time.Now().Add(time.Hour * 24 * 365 * 10),
+		PluginInstallationId: installation_id,
+	}
+
+	if err := db.Create(&installation); err != nil {
+		return "", err
+	}
+
+	return installation.HookID, nil
+}
+
+// uninstalls a plugin from db
+func UninstallWebhook(webhook *models.Webhook) error {
+	return db.Delete(webhook)
+}

+ 12 - 3
internal/types/entities/plugin_entities/plugin_declaration.go

@@ -16,9 +16,10 @@ const (
 )
 
 type PluginPermissionRequirement struct {
-	Tool  *PluginPermissionToolRequirement  `json:"tool" yaml:"tool" validate:"omitempty"`
-	Model *PluginPermissionModelRequirement `json:"model" yaml:"model" validate:"omitempty"`
-	Node  *PluginPermissionNodeRequirement  `json:"node" yaml:"node" validate:"omitempty"`
+	Tool    *PluginPermissionToolRequirement    `json:"tool" yaml:"tool" validate:"omitempty"`
+	Model   *PluginPermissionModelRequirement   `json:"model" yaml:"model" validate:"omitempty"`
+	Node    *PluginPermissionNodeRequirement    `json:"node" yaml:"node" validate:"omitempty"`
+	Webhook *PluginPermissionWebhookRequirement `json:"webhook" yaml:"webhook" validate:"omitempty"`
 }
 
 func (p *PluginPermissionRequirement) AllowInvokeTool() bool {
@@ -53,6 +54,10 @@ func (p *PluginPermissionRequirement) AllowInvokeNode() bool {
 	return p != nil && p.Node != nil && p.Node.Enabled
 }
 
+func (p *PluginPermissionRequirement) AllowRegistryWebhook() bool {
+	return p != nil && p.Webhook != nil && p.Webhook.Enabled
+}
+
 type PluginPermissionToolRequirement struct {
 	Enabled bool `json:"enabled" yaml:"enabled"`
 }
@@ -71,6 +76,10 @@ type PluginPermissionNodeRequirement struct {
 	Enabled bool `json:"enabled" yaml:"enabled"`
 }
 
+type PluginPermissionWebhookRequirement struct {
+	Enabled bool `json:"enabled" yaml:"enabled"`
+}
+
 type PluginResourceRequirement struct {
 	// Memory in bytes
 	Memory int64 `json:"memory" yaml:"memory" validate:"required"`

+ 6 - 1
internal/types/models/curd/atomic.go

@@ -5,13 +5,16 @@ import (
 
 	"github.com/langgenius/dify-plugin-daemon/internal/db"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/models"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
 	"gorm.io/gorm"
 )
 
 // Create plugin for a tenant, create plugin if it has never been created before
 // and install it to the tenant, return the plugin and the installation
 // if the plugin has been created before, return the plugin which has been created before
-func CreatePlugin(tenant_id string, user_id string, plugin *models.Plugin) (*models.Plugin, *models.PluginInstallation, error) {
+func CreatePlugin(tenant_id string, user_id string, plugin *models.Plugin, config map[string]any) (
+	*models.Plugin, *models.PluginInstallation, error,
+) {
 	var plugin_to_be_returns *models.Plugin
 	var installation_to_be_returns *models.PluginInstallation
 
@@ -30,6 +33,7 @@ func CreatePlugin(tenant_id string, user_id string, plugin *models.Plugin) (*mod
 		p, err := db.GetOne[models.Plugin](
 			db.WithTransactionContext(tx),
 			db.Equal("plugin_id", plugin.PluginID),
+			db.Equal("checksum", plugin.Checksum),
 			db.WLock(),
 		)
 
@@ -56,6 +60,7 @@ func CreatePlugin(tenant_id string, user_id string, plugin *models.Plugin) (*mod
 			PluginID: plugin_to_be_returns.PluginID,
 			TenantID: tenant_id,
 			UserID:   user_id,
+			Config:   parser.MarshalJson(config),
 		}
 
 		err = db.Create(installation, tx)

+ 11 - 0
internal/types/models/installation.go

@@ -1,5 +1,7 @@
 package models
 
+import "encoding/json"
+
 type PluginInstallationStatus string
 
 type PluginInstallation struct {
@@ -7,4 +9,13 @@ type PluginInstallation struct {
 	TenantID string `json:"tenant_id" orm:"index;type:uuid;"`
 	UserID   string `json:"user_id" orm:"index;type:uuid;"`
 	PluginID string `json:"plugin_id" orm:"index;size:127"`
+	Config   string `json:"config"`
+}
+
+func (p *PluginInstallation) ConfigMap() (map[string]any, error) {
+	var config map[string]any
+	if err := json.Unmarshal([]byte(p.Config), &config); err != nil {
+		return nil, err
+	}
+	return config, nil
 }

+ 5 - 6
internal/types/models/plugin.go

@@ -7,10 +7,9 @@ import (
 
 type Plugin struct {
 	Model
-	PluginID          string                           `json:"id" orm:"index;size:127"`
-	ConfigurationText string                           `json:"configuration_text" orm:"type:text"`
-	Refers            int                              `json:"refers" orm:"default:0"`
-	Checksum          string                           `json:"checksum" orm:"size:127"`
-	InstallType       entities.PluginRuntimeType       `json:"install_type" orm:"size:127"`
-	ManifestType      plugin_entities.DifyManifestType `json:"manifest_type" orm:"size:127"`
+	PluginID     string                           `json:"id" orm:"index;size:127"`
+	Refers       int                              `json:"refers" orm:"default:0"`
+	Checksum     string                           `json:"checksum" orm:"size:127"`
+	InstallType  entities.PluginRuntimeType       `json:"install_type" orm:"size:127;index"`
+	ManifestType plugin_entities.DifyManifestType `json:"manifest_type" orm:"size:127"`
 }

+ 6 - 5
internal/types/models/webhook.go

@@ -5,9 +5,10 @@ import "time"
 // HookID is a pointer to plugin id and tenant id, using it to identify the webhook plugin
 type Webhook struct {
 	Model
-	HookID    string    `json:"hook_id" orm:"index;size:127;column:hook_id"`
-	TenantID  string    `json:"tenant_id" orm:"index;size:64;column:tenant_id"`
-	UserID    string    `json:"user_id" orm:"index;size:64;column:user_id"`
-	PluginID  string    `json:"plugin_id" orm:"index;size:64;column:plugin_id"`
-	ExpiredAt time.Time `json:"expired_at" orm:"column:expired_at"`
+	HookID               string    `json:"hook_id" orm:"uniqueIndex;size:127;column:hook_id"`
+	TenantID             string    `json:"tenant_id" orm:"index;size:64;column:tenant_id"`
+	UserID               string    `json:"user_id" orm:"index;size:64;column:user_id"`
+	PluginID             string    `json:"plugin_id" orm:"index;size:64;column:plugin_id"`
+	ExpiredAt            time.Time `json:"expired_at" orm:"column:expired_at"`
+	PluginInstallationId string    `json:"plugin_installation_id" orm:"index;size:64;column:plugin_installation_id"`
 }

+ 12 - 0
internal/utils/strings/random.go

@@ -0,0 +1,12 @@
+package strings
+
+import "math/rand"
+
+func RandomString(length int) string {
+	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	b := make([]byte, length)
+	for i := range b {
+		b[i] = charset[rand.Intn(len(charset))]
+	}
+	return string(b)
+}