Ver código fonte

feat: packager

Yeuoly 1 ano atrás
pai
commit
d7493d31e8

+ 40 - 0
cmd/packager/main.go

@@ -1 +1,41 @@
 package main
+
+import (
+	"flag"
+	"os"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/packager"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/log"
+)
+
+func main() {
+	var (
+		in_path  string
+		out_path string
+		help     bool
+	)
+
+	flag.StringVar(&in_path, "in", "", "directory of plugin to be packaged")
+	flag.StringVar(&out_path, "out", "", "package output path")
+	flag.BoolVar(&help, "help", false, "show help")
+	flag.Parse()
+
+	if help || in_path == "" || out_path == "" {
+		flag.PrintDefaults()
+		os.Exit(0)
+	}
+
+	packager := packager.NewPackager(in_path)
+	zip_file, err := packager.Pack()
+
+	if err != nil {
+		log.Panic("failed to package plugin %v", err)
+	}
+
+	err = os.WriteFile(out_path, zip_file, 0644)
+	if err != nil {
+		log.Panic("failed to write package file %v", err)
+	}
+
+	log.Info("plugin packaged successfully, output path: %s", out_path)
+}

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

@@ -103,7 +103,6 @@ func parsePluginConfig(configuration_path string) (*plugin_entities.PluginDeclar
 		return nil, err
 	}
 
-	// TODO: validate
 	result, err := plugin_entities.UnmarshalPluginDeclarationFromYaml(text)
 	if err != nil {
 		return nil, err

+ 18 - 0
internal/core/plugin_packager/packager/file_scanner.go

@@ -1,5 +1,23 @@
 package packager
 
+import (
+	"os"
+	"path"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/types/entities/plugin_entities"
+)
+
 func (p *Packager) ScanProvider() error {
 	return nil
 }
+
+func (p *Packager) fetchManifest() (*plugin_entities.PluginDeclaration, error) {
+
+	file_path := path.Join(p.wp, p.manifest)
+	file, err := os.ReadFile(file_path)
+	if err != nil {
+		return nil, err
+	}
+
+	return plugin_entities.UnmarshalPluginDeclarationFromYaml(file)
+}

+ 54 - 2
internal/core/plugin_packager/packager/packager.go

@@ -1,5 +1,13 @@
 package packager
 
+import (
+	"archive/zip"
+	"bytes"
+	"io/fs"
+	"os"
+	"path"
+)
+
 type Packager struct {
 	wp string // working path
 
@@ -13,6 +21,50 @@ func NewPackager(plugin_path string) *Packager {
 	}
 }
 
-func (p *Packager) Pack() error {
-	return nil
+func (p *Packager) Pack() ([]byte, error) {
+	// read manifest
+	_, err := p.fetchManifest()
+	if err != nil {
+		return nil, err
+	}
+
+	zip_buffer := new(bytes.Buffer)
+	zip_writer := zip.NewWriter(zip_buffer)
+	err = fs.WalkDir(os.DirFS(p.wp), ".", func(root_path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+
+		if d.IsDir() {
+			return nil
+		}
+
+		file, err := os.ReadFile(path.Join(p.wp, root_path))
+		if err != nil {
+			return err
+		}
+
+		zip_file, err := zip_writer.Create(root_path)
+		if err != nil {
+			return err
+		}
+
+		_, err = zip_file.Write(file)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		return nil, err
+	}
+
+	err = zip_writer.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	return zip_buffer.Bytes(), nil
 }

+ 2 - 2
internal/core/plugin_packager/signer/sign.go

@@ -96,9 +96,9 @@ func SignPlugin(plugin []byte) ([]byte, error) {
 		}
 	}
 
-	comments := parser.MarshalJson(map[string]string{
+	comments := parser.MarshalJson(map[string]any{
 		"signature": base64.StdEncoding.EncodeToString(signature),
-		"time":      time_string,
+		"time":      ct,
 	})
 
 	// write signature

+ 84 - 0
internal/core/plugin_packager/verifier/verifier.go

@@ -0,0 +1,84 @@
+package verifier
+
+import (
+	"archive/zip"
+	"bytes"
+	"crypto/sha256"
+	"encoding/base64"
+	"strconv"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/core/license/public_key"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/encryption"
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
+)
+
+// VerifyPlugin is a function that verifies the signature of a plugin
+// It takes a plugin as a stream of bytes and verifies the signature
+func VerifyPlugin(archive []byte) error {
+	// load public key
+	public_key, err := encryption.LoadPublicKey(public_key.PUBLIC_KEY)
+	if err != nil {
+		return err
+	}
+
+	// construct zip
+	zip_reader, err := zip.NewReader(bytes.NewReader(archive), int64(len(archive)))
+	if err != nil {
+		return err
+	}
+
+	data := new(bytes.Buffer)
+	// read one by one
+	for _, file := range zip_reader.File {
+		// read file bytes
+		file_reader, err := file.Open()
+		if err != nil {
+			return err
+		}
+		defer file_reader.Close()
+
+		temp_data := new(bytes.Buffer)
+		_, err = temp_data.ReadFrom(file_reader)
+		if err != nil {
+			return err
+		}
+
+		hash := sha256.New()
+		hash.Write(temp_data.Bytes())
+		hashed := hash.Sum(nil)
+
+		// write the hash into data
+		data.Write(hashed)
+	}
+
+	// get the signature
+	signature := zip_reader.Comment
+
+	type signatureData struct {
+		Signature string `json:"signature"`
+		Time      int64  `json:"time"`
+	}
+
+	signature_data, err := parser.UnmarshalJson[signatureData](signature)
+	if err != nil {
+		return err
+	}
+
+	plugin_sig := signature_data.Signature
+	plugin_time := signature_data.Time
+
+	// convert time to bytes
+	time_string := strconv.FormatInt(plugin_time, 10)
+
+	// write the time into data
+	data.Write([]byte(time_string))
+
+	sig_bytes, err := base64.StdEncoding.DecodeString(plugin_sig)
+	if err != nil {
+		return err
+	}
+
+	// verify signature
+	err = encryption.VerifySign(public_key, data.Bytes(), sig_bytes)
+	return err
+}

+ 1 - 1
internal/types/entities/plugin_entities/plugin_declaration.go

@@ -74,7 +74,7 @@ type PluginDeclaration struct {
 	Name      string                    `json:"name" yaml:"name" validate:"required" enum:"plugin"`
 	CreatedAt time.Time                 `json:"created_at" yaml:"created_at" validate:"required"`
 	Resource  PluginResourceRequirement `json:"resource" yaml:"resource" validate:"required"`
-	Plugin    []string                  `json:"plugins" yaml:"plugin" validate:"required"`
+	Plugins   []string                  `json:"plugins" yaml:"plugins" validate:"required"`
 }
 
 func (p *PluginDeclaration) Identity() string {