diff --git a/app/src/plugin/loader.ts b/app/src/plugin/loader.ts
index 4d2995cd6..4bcb7717d 100644
--- a/app/src/plugin/loader.ts
+++ b/app/src/plugin/loader.ts
@@ -17,7 +17,7 @@ const runCode = (code: string, sourceURL: string) => {
}
export const loadPlugins = (app: App) => {
- fetchPost("/api/plugin/loadPlugins", {}, response => {
+ fetchPost("/api/petal/loadPetals", {}, response => {
let css = "";
response.data.forEach((item: { id: string, name: string, jsCode: string, cssCode: string, lang: IObject }) => {
const moduleObj = {}
diff --git a/kernel/api/plugin.go b/kernel/api/plugin.go
new file mode 100644
index 000000000..ddcd71ca4
--- /dev/null
+++ b/kernel/api/plugin.go
@@ -0,0 +1,33 @@
+// SiYuan - Build Your Eternal Digital Garden
+// Copyright (c) 2020-present, b3log.org
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package api
+
+import (
+ "net/http"
+
+ "github.com/88250/gulu"
+ "github.com/gin-gonic/gin"
+ "github.com/siyuan-note/siyuan/kernel/model"
+)
+
+func loadPetals(c *gin.Context) {
+ ret := gulu.Ret.NewResult()
+ defer c.JSON(http.StatusOK, ret)
+
+ petals := model.LoadPetals()
+ ret.Data = petals
+}
diff --git a/kernel/api/router.go b/kernel/api/router.go
index 11f69abb7..32d33f918 100644
--- a/kernel/api/router.go
+++ b/kernel/api/router.go
@@ -343,4 +343,6 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, model.CheckReadonly, chatGPT)
ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, model.CheckReadonly, chatGPTWithAction)
+
+ ginServer.Handle("POST", "/api/petal/loadPetals", model.CheckAuth, model.CheckReadonly, loadPetals)
}
diff --git a/kernel/bazaar/package.go b/kernel/bazaar/package.go
index bf8ab61e2..ebd8c1b3c 100644
--- a/kernel/bazaar/package.go
+++ b/kernel/bazaar/package.go
@@ -38,10 +38,18 @@ import (
"golang.org/x/text/transform"
)
+type Funding struct {
+ OpenCollective string `json:"openCollective"`
+ Patreon string `json:"patreon"`
+ GitHub string `json:"github"`
+ Custom []string `json:"custom"`
+}
+
type Package struct {
- Author string `json:"author"`
- URL string `json:"url"`
- Version string `json:"version"`
+ Author string `json:"author"`
+ URL string `json:"url"`
+ Version string `json:"version"`
+ Funding *Funding `json:"funding"`
Name string `json:"name"`
RepoURL string `json:"repoURL"`
diff --git a/kernel/model/plugin.go b/kernel/model/plugin.go
new file mode 100644
index 000000000..e777f7491
--- /dev/null
+++ b/kernel/model/plugin.go
@@ -0,0 +1,127 @@
+// SiYuan - Build Your Eternal Digital Garden
+// Copyright (c) 2020-present, b3log.org
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package model
+
+import (
+ "crypto/sha1"
+ "fmt"
+ "path/filepath"
+
+ "github.com/88250/gulu"
+ "github.com/siyuan-note/filelock"
+ "github.com/siyuan-note/logging"
+ "github.com/siyuan-note/siyuan/kernel/bazaar"
+ "github.com/siyuan-note/siyuan/kernel/util"
+)
+
+// Petal represents a plugin's management status.
+type Petal struct {
+ ID string `json:"id"` // Plugin ID
+ Name string `json:"name"` // Plugin name
+ Enabled bool `json:"enabled"` // Whether enabled
+
+ JS string `json:"js"` // JS code
+ CSS string `json:"css"` // CSS code
+ I18n map[string]interface{} `json:"i18n"` // i18n text
+}
+
+func LoadPetals() (ret []*Petal) {
+ petalDir := filepath.Join(util.DataDir, "storage", "petal")
+ confPath := filepath.Join(petalDir, "petals.json")
+
+ ret = []*Petal{}
+ if !gulu.File.IsExist(confPath) {
+ data, err := gulu.JSON.MarshalIndentJSON(ret, "", "\t")
+ if nil != err {
+ logging.LogErrorf("marshal petals failed: %s", err)
+ return
+ }
+ if err = filelock.WriteFile(confPath, data); nil != err {
+ logging.LogErrorf("write petals [%s] failed: %s", confPath, err)
+ return
+ }
+ return
+ }
+
+ data, err := filelock.ReadFile(confPath)
+ if nil != err {
+ logging.LogErrorf("read petal file [%s] failed: %s", confPath, err)
+ return
+ }
+
+ if err = gulu.JSON.UnmarshalJSON(data, &ret); nil != err {
+ logging.LogErrorf("unmarshal petals failed: %s", err)
+ return
+ }
+
+ plugins := bazaar.InstalledPlugins()
+ for _, plugin := range plugins {
+ id := hash(plugin.URL)
+ petal := getPetalByID(id, ret)
+ if nil == petal {
+ continue
+ }
+
+ pluginDir := filepath.Join(util.DataDir, "plugins", plugin.Name)
+ data, err := filelock.ReadFile(filepath.Join(pluginDir, "index.js"))
+ if nil != err {
+ logging.LogErrorf("read plugin [%s] js failed: %s", plugin.Name, err)
+ continue
+ }
+ petal.JS = string(data)
+
+ cssPath := filepath.Join(pluginDir, "index.css")
+ if gulu.File.IsExist(cssPath) {
+ data, err := filelock.ReadFile(cssPath)
+ if nil != err {
+ logging.LogErrorf("read plugin [%s] css failed: %s", plugin.Name, err)
+ } else {
+ petal.CSS = string(data)
+ }
+ }
+
+ i18nPath := filepath.Join(pluginDir, "i18n", Conf.Lang)
+ if gulu.File.IsExist(i18nPath) {
+ data, err := filelock.ReadFile(i18nPath)
+ if nil != err {
+ logging.LogErrorf("read plugin [%s] i18n failed: %s", plugin.Name, err)
+ } else {
+ petal.I18n = map[string]interface{}{}
+ if err = gulu.JSON.UnmarshalJSON(data, &petal.I18n); nil != err {
+ logging.LogErrorf("unmarshal plugin [%s] i18n failed: %s", plugin.Name, err)
+ }
+ }
+ }
+
+ ret = append(ret, petal)
+ }
+ return
+}
+
+func getPetalByID(id string, petals []*Petal) (ret *Petal) {
+ for _, p := range petals {
+ if id == p.ID {
+ ret = p
+ break
+ }
+ }
+ return
+}
+
+func hash(str string) string {
+ return fmt.Sprintf("%x", sha1.Sum([]byte(str)))
+}