diff --git a/core/api_bucket.go b/core/api_bucket.go index 308be03..ed68d87 100644 --- a/core/api_bucket.go +++ b/core/api_bucket.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "strconv" "strings" "time" @@ -14,6 +15,13 @@ import ( "github.com/goccy/go-json" ) +func checkFilePlugin(key string, value *string) { + if v, ok := plugins_id.Load(key); ok { + data, _ := os.ReadFile(v.(string)) + *value = string(data) + } +} + func init() { var sillyGirl = MakeBucket("sillyGirl") GinApi(GET, "/api/storage/list", RequireAuth, func(ctx *gin.Context) { @@ -35,7 +43,6 @@ func init() { ar := strings.SplitN(bk, ".", 2) if len(ar) == 2 { if ar[0] == "plugins" && false { //todo - // data[bk] = halfDeEct(MakeBucket(ar[0]).GetString(ar[1])) } else { // data[bk] = MakeBucket(ar[0]).GetString(ar[1]) @@ -138,8 +145,13 @@ func init() { for _, bk := range arr { ar := strings.SplitN(bk, ".", 2) if len(ar) == 2 { - if ar[0] == "plugins" && IsCdle { //todo - data[bk] = DecryptPlugin(halfDeEct(MakeBucket(ar[0]).GetString(ar[1]))) + if ar[0] == "plugins" { //todo + value := MakeBucket(ar[0]).GetString(ar[1]) + checkFilePlugin(ar[1], &value) + if IsCdle { + value = DecryptPlugin(halfDeEct(value)) + } + data[bk] = value } else { data[bk] = TransformBucketKeyValue(MakeBucket(ar[0]).GetString(ar[1])) } @@ -182,6 +194,10 @@ func init() { for bk, v := range updates { ar := strings.SplitN(bk, ".", 2) if len(ar) == 2 { + if vv, ok := plugins_id.Load(ar[1]); ok { + os.WriteFile(vv.(string), []byte(fmt.Sprint(v)), 0755) + continue + } msg, changed, err := SetBucketKeyValue(MakeBucket(ar[0]), ar[1], v) if msg != "" { messages[bk] = msg diff --git a/core/grpc_download.go b/core/grpc_download.go index d8cf50a..95f03cd 100644 --- a/core/grpc_download.go +++ b/core/grpc_download.go @@ -2,7 +2,6 @@ package core import ( "archive/zip" - "fmt" "io" "io/fs" "net/http" @@ -22,6 +21,8 @@ type Language struct { Links []string // 下载链接 } +var plugin_dir = utils.ExecPath + "/plugins" + var languages = []Language{ { Name: "node", @@ -40,7 +41,6 @@ var languages = []Language{ } func init() { - go func() { for _, item := range languages { if !(item.Os == runtime.GOOS && item.Arch == runtime.GOARCH) { @@ -83,7 +83,7 @@ func init() { func unzip(filename string, perm fs.FileMode) error { zipFile, err := zip.OpenReader(filename) dir := filepath.Dir(filename) - fmt.Println(filename, err) + // fmt.Println(filename, err) if err != nil { return err } diff --git a/core/grpc_plugins.go b/core/grpc_plugins.go index 4f095f5..0b846f6 100644 --- a/core/grpc_plugins.go +++ b/core/grpc_plugins.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" "sync" + "time" "github.com/cdle/sillyplus/core/common" "github.com/cdle/sillyplus/core/storage" @@ -96,6 +97,15 @@ func initNodePlugins() { info, err := os.Stat(event.Name) // fmt.Println(err) if err == nil && info.IsDir() { + event_name := event.Name + "/main.js" + if info, err := os.Stat(event_name); err == nil && !info.IsDir() { + AddNodePlugin(event_name, plugin_name) + } else { + time.Sleep(time.Millisecond * 100) + if info, err := os.Stat(event_name); err == nil && !info.IsDir() { + AddNodePlugin(event_name, plugin_name) + } + } watcher.Add(event.Name) // fmt.Println("增加插件目录", event.Name) } else { @@ -103,6 +113,7 @@ func initNodePlugins() { } } else if plugin_index { // fmt.Println("增加插件", event.Name) + // RemNodePlugin(plugin_name) AddNodePlugin(event.Name, plugin_name) } case "REMOVE", "RENAME", "REMOVE|RENAME": @@ -115,7 +126,7 @@ func initNodePlugins() { // fmt.Println("移除插件", plugin_name) RemNodePlugin(plugin_name) } - case "WRITE": + case "WRITE": //, "CHMOD" if plugin_index { RemNodePlugin(plugin_name) AddNodePlugin(event.Name, plugin_name) @@ -135,6 +146,7 @@ func RemNodePlugin(name string) error { pluginLock.Lock() defer pluginLock.Unlock() key := nameUuid(name) + plugins_id.Delete(key) // fmt.Println("rem", key, name) for i := range Functions { if Functions[i].UUID == key { @@ -164,9 +176,12 @@ func nameUuid(name string) string { return uuid.NewSHA1(uuid.Nil, hash[:]).String() } +var plugins_id sync.Map + func AddNodePlugin(path, name string) error { pluginLock.Lock() defer pluginLock.Unlock() + file, err := os.Open(path) if err != nil { return err @@ -176,20 +191,21 @@ func AddNodePlugin(path, name string) error { return err } uuid := nameUuid(name) + plugins_id.Store(uuid, path) // fmt.Println("add,", uuid, name) f, cbs := pluginParse(string(data), uuid) - f.Suffix = ".ts" + f.Suffix = ".js" f.Type = "typescript" f.Handle = func(s common.Sender, f func(vm *goja.Runtime)) interface{} { s.SetPluginID(uuid) plt := s.GetImType() // , "/Users/a1-6/.nvm/versions/node/v18.16.1/lib/node_modules/ts-node/dist/bin.js", - cmd := exec.Command(utils.ExecPath+"/language/node/node", path) + cmd := exec.Command("./node", path) + cmd.Dir = utils.ExecPath + "/language/node" // cmd := exec.Command(utils.ExecPath+"/language/node/node", path) id := s.SetID() cmd.Env = append(cmd.Env, "SENDER_ID="+id) cmd.Env = append(cmd.Env, "PLUGIN_ID="+uuid) - // 获取标准输出和标准错误输出的管道 stdout, err := cmd.StdoutPipe() if err != nil { @@ -217,7 +233,6 @@ func AddNodePlugin(path, name string) error { // 处理标准输出 go func() { defer wg.Done() - scanner := bufio.NewScanner(stdout) for scanner.Scan() { data := scanner.Text() @@ -271,6 +286,9 @@ func AddNodePlugin(path, name string) error { for _, cb := range cbs { cb() } + if !f.OnStart { + console.Log("已加载 %s%s", f.Title, f.Suffix) + } AddCommand([]*common.Function{f}) return nil } diff --git a/core/plugin_core.go b/core/plugin_core.go index b6e6757..fcded58 100644 --- a/core/plugin_core.go +++ b/core/plugin_core.go @@ -3,6 +3,8 @@ package core import ( "errors" "fmt" + "io" + "net/http" "os" "reflect" "regexp" @@ -113,6 +115,46 @@ func initPlugins() { if p.UUID != key { continue } + if p.Type != "goja" { //下载目录插件 + // Content-Type + var prefix = "?uuid=" + p.UUID + address := p.Address + if !strings.HasSuffix(address, "list.json") { + address = address + "/api/plugins/download" + prefix + } else { + address = strings.ReplaceAll(address, "list.json", "download"+prefix) + } + resp, err := http.Get(address) + if err != nil { + return &storage.Final{ + Error: errors.New("插件源异常!"), + } + } + defer resp.Body.Close() + zipfile := plugin_dir + "/" + utils.GenUUID() + ".zip" + f, err := os.OpenFile(zipfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return &storage.Final{ + Error: errors.New("文件异常!"), + } + } + defer f.Close() + _, err = io.Copy(f, resp.Body) + if err != nil { + return &storage.Final{ + Error: errors.New("下载异常!"), + } + } + defer os.Remove(zipfile) + if err := unzip(zipfile, 0755); err != nil { + return &storage.Final{ + Error: errors.New("安装异常!"), + } + } + return &storage.Final{ + Now: "", + } + } script := string(fetchScript(p.Address, key)) if f, _, _ := initPlugin(script, p.UUID, ""); f.CreateAt != "" { fin = &storage.Final{ @@ -128,6 +170,7 @@ func initPlugins() { Error: errors.New("订阅源异常"), } } + break } } diff --git a/core/web.go b/core/web.go index bb2ab14..181e5d7 100644 --- a/core/web.go +++ b/core/web.go @@ -1,6 +1,8 @@ package core import ( + "archive/zip" + "bytes" "context" "embed" "fmt" @@ -8,6 +10,7 @@ import ( "net" "net/http" "os" + "path/filepath" "regexp" "strings" "time" @@ -71,9 +74,80 @@ func initWeb() { uuid := c.Query("uuid") for _, f := range Functions { if f.UUID == uuid && f.Public { - plugin_downloads.Set(f.UUID, plugin_downloads.GetInt(f.UUID)+1) - c.String(200, publicScript(plugins.GetString(f.UUID))) - return + if f.Type == "goja" { + plugin_downloads.Set(f.UUID, plugin_downloads.GetInt(f.UUID)+1) + c.String(200, publicScript(plugins.GetString(f.UUID))) + return + } else { + v, ok := plugins_id.Load(f.UUID) + if !ok { + return + } + dir := filepath.Dir(v.(string)) + if _, err := os.Stat(dir); err != nil { //执行压缩 + return + } + ss := strings.Split(dir, "/") + name := ss[len(ss)-1] + buf := new(bytes.Buffer) + w := zip.NewWriter(buf) + // dir = strings.Replace(dir, utils.ExecPath+"", ".", 1) + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + // 将路径转换为相对路径 + relPath, err := filepath.Rel(dir, path) + relPath = name + "/" + relPath + if err != nil { + return err + } + + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + fh, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + // 使用相对路径作为文件名 + fh.Name = relPath + + wr, err := w.CreateHeader(fh) + if err != nil { + return err + } + + _, err = io.Copy(wr, file) + return err + }) + if err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("ZIP creation failed: %s", err)) + return + } + err = w.Close() + if err != nil { + c.String(http.StatusInternalServerError, fmt.Sprintf("ZIP creation failed: %s", err)) + return + } + c.Data(http.StatusOK, "application/zip", buf.Bytes()) + // zippath := utils.ExecPath + "/public/" + f.UUID + ".zip" + // file, err := os.Open(zippath) + // if err != nil { + // return + // } + // defer file.Close() + // c.Header("Content-Type", "application/zip") + // io.Copy(c.Writer, file) + return + } } } })