diff --git a/README_zh_CN.md b/README_zh_CN.md index ece108df9..8a7cfe6bb 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -45,6 +45,7 @@ * [Docker 部署](#docker-部署) * [Unraid 部署](#unraid-部署) * [宝塔面板 部署](#宝塔面板部署) + * [小皮面板 部署](#小皮面板部署) * [内部预览版](#内部预览版) * [🏘️ 社区](#️-社区) * [🛠️ 开发指南](#️-开发指南) @@ -338,6 +339,29 @@ Publish parameters: --accessAuthCode=******(访问授权码) +### 小皮面板部署 + +
+小皮面板 部署文档 + +#### 前提 + +* 需要安装小皮面板,前往[小皮面板](https://www.xp.cn/download),选择对应的脚本执行安装 + +#### 部署 + +1. 登录小皮面板后,点击左侧菜单的 **Docker** +2. 首次打开会提示安装 Docker,点击 **点击安装 Docker** +3. 按照提示安装 Docker +4. 点击 **应用商店**,找到 **思源笔记**,点击 **安装** -> **立即安装** +5. 等待安装结束后,可在 **任务队列** 界面的 **已结束** 中点击 **详情** 查看安装信息 + +#### 访问思源笔记 + +* 在浏览器输入 `http://<小皮面板机器IP>:6806` 访问 + +
+ ### 内部预览版 我们会在有重大更新前发布内部预览版,请访问 [https://github.com/siyuan-note/insider](https://github.com/siyuan-note/insider)。 diff --git a/kernel/api/av.go b/kernel/api/av.go index 59e757597..c7e24dfd4 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -27,6 +27,29 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func changeAttrViewLayout(c *gin.Context) { + ret := gulu.Ret.NewResult() + arg, ok := util.JsonArg(c, ret) + if !ok { + c.JSON(http.StatusOK, ret) + return + } + + blockID := arg["blockID"].(string) + avID := arg["avID"].(string) + layoutType := arg["layoutType"].(av.LayoutType) + err := model.ChangeAttrViewLayout(blockID, avID, layoutType) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + c.JSON(http.StatusOK, ret) + return + } + + ret = renderAttrView(avID, "", "", 1, -1) + c.JSON(http.StatusOK, ret) +} + func duplicateAttributeViewBlock(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) @@ -532,10 +555,9 @@ func renderHistoryAttributeView(c *gin.Context) { func renderAttributeView(c *gin.Context) { ret := gulu.Ret.NewResult() - defer c.JSON(http.StatusOK, ret) - arg, ok := util.JsonArg(c, ret) if !ok { + c.JSON(http.StatusOK, ret) return } @@ -563,7 +585,13 @@ func renderAttributeView(c *gin.Context) { query = queryArg.(string) } - view, attrView, err := model.RenderAttributeView(id, viewID, query, page, pageSize) + ret = renderAttrView(id, viewID, query, page, pageSize) + c.JSON(http.StatusOK, ret) +} + +func renderAttrView(avID, viewID, query string, page, pageSize int) (ret *gulu.Result) { + ret = gulu.Ret.NewResult() + view, attrView, err := model.RenderAttributeView(avID, viewID, query, page, pageSize) if err != nil { ret.Code = -1 ret.Msg = err.Error() @@ -602,6 +630,7 @@ func renderAttributeView(c *gin.Context) { "view": view, "isMirror": av.IsMirror(attrView.ID), } + return } func getCurrentAttrViewImages(c *gin.Context) { diff --git a/kernel/api/router.go b/kernel/api/router.go index dc0c7fa5f..0c3d8f0dd 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -455,6 +455,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/duplicateAttributeViewBlock", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, duplicateAttributeViewBlock) ginServer.Handle("POST", "/api/av/appendAttributeViewDetachedBlocksWithValues", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, appendAttributeViewDetachedBlocksWithValues) ginServer.Handle("POST", "/api/av/getCurrentAttrViewImages", model.CheckAuth, getCurrentAttrViewImages) + ginServer.Handle("POST", "/api/av/changeAttrViewLayout", model.CheckAuth, changeAttrViewLayout) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, model.CheckAdminRole, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, model.CheckAdminRole, chatGPTWithAction) diff --git a/kernel/api/system.go b/kernel/api/system.go index 79fb64e65..a52b76e90 100644 --- a/kernel/api/system.go +++ b/kernel/api/system.go @@ -28,6 +28,7 @@ import ( "github.com/88250/lute" "github.com/88250/lute/html" "github.com/gin-gonic/gin" + "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/conf" "github.com/siyuan-note/siyuan/kernel/model" @@ -171,8 +172,13 @@ func getEmojiConf(c *gin.Context) { } if !util.IsValidUploadFileName(html.UnescapeString(name)) { + emojiFullName := filepath.Join(customConfDir, name) + fullPathFilteredName := filepath.Join(customConfDir, util.FilterUploadFileName(name)) // XSS through emoji name https://github.com/siyuan-note/siyuan/issues/15034 - logging.LogWarnf("invalid custom emoji name [%s]", name) + logging.LogWarnf("renaming invalid custom emoji file [%s] to [%s]", name, fullPathFilteredName) + if removeErr := filelock.Rename(emojiFullName, fullPathFilteredName); nil != removeErr { + logging.LogErrorf("renaming invalid custom emoji file to [%s] failed: %s", fullPathFilteredName, removeErr) + } } if customEmoji.IsDir() { @@ -194,9 +200,13 @@ func getEmojiConf(c *gin.Context) { } if !util.IsValidUploadFileName(html.UnescapeString(name)) { + emojiFullName := filepath.Join(customConfDir, name) + fullPathFilteredName := filepath.Join(customConfDir, util.FilterUploadFileName(name)) // XSS through emoji name https://github.com/siyuan-note/siyuan/issues/15034 - logging.LogWarnf("invalid custom emoji name [%s]", name) - continue + logging.LogWarnf("renaming invalid custom emoji file [%s] to [%s]", name, fullPathFilteredName) + if removeErr := filelock.Rename(emojiFullName, fullPathFilteredName); nil != removeErr { + logging.LogErrorf("renaming invalid custom emoji file to [%s] failed: %s", fullPathFilteredName, removeErr) + } } addCustomEmoji(customEmoji.Name()+"/"+name, &items) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index e8ad1b471..d92571d6f 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -45,7 +45,7 @@ import ( ) func (tx *Transaction) doSetAttrViewBlockView(operation *Operation) (ret *TxErr) { - err := SetDatabaseBlockView(operation.BlockID, operation.ID, operation.AvID) + err := SetDatabaseBlockView(operation.BlockID, operation.AvID, operation.ID) if err != nil { return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()} } @@ -53,25 +53,25 @@ func (tx *Transaction) doSetAttrViewBlockView(operation *Operation) (ret *TxErr) } func (tx *Transaction) doChangeAttrViewLayout(operation *Operation) (ret *TxErr) { - err := changeAttrViewLayout(operation) + err := ChangeAttrViewLayout(operation.BlockID, operation.AvID, operation.Layout) if err != nil { return &TxErr{code: TxErrWriteAttributeView, id: operation.AvID, msg: err.Error()} } return } -func changeAttrViewLayout(operation *Operation) (err error) { - attrView, err := av.ParseAttributeView(operation.AvID) +func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error) { + attrView, err := av.ParseAttributeView(avID) if err != nil { return } - view, err := getAttrViewViewByBlockID(attrView, operation.BlockID) + view, err := getAttrViewViewByBlockID(attrView, blockID) if err != nil { return } - newLayout := operation.Layout + newLayout := layout if newLayout == view.LayoutType { return } diff --git a/kernel/model/import.go b/kernel/model/import.go index 96ecbe1be..8bc6b4dae 100644 --- a/kernel/model/import.go +++ b/kernel/model/import.go @@ -22,7 +22,6 @@ import ( "encoding/json" "errors" "fmt" - util2 "github.com/88250/lute/util" "image" "image/jpeg" "image/png" @@ -44,6 +43,7 @@ import ( "github.com/88250/lute/html/atom" "github.com/88250/lute/parse" "github.com/88250/lute/render" + util2 "github.com/88250/lute/util" "github.com/siyuan-note/filelock" "github.com/siyuan-note/logging" "github.com/siyuan-note/riff" @@ -557,6 +557,19 @@ func ImportSY(zipPath, boxID, toPath string) (err error) { } // 将包含的自定义表情统一移动到 data/emojis/ 下 + unzipRootEmojisPath := filepath.Join(unzipRootPath, "emojis") + filelock.Walk(unzipRootEmojisPath, func(path string, d fs.DirEntry, err error) error { + if !util.IsValidUploadFileName(d.Name()) { + emojiFullName := filepath.Join(unzipRootEmojisPath, d.Name()) + fullPathFilteredName := filepath.Join(unzipRootEmojisPath, util.FilterUploadFileName(d.Name())) + // XSS through emoji name https://github.com/siyuan-note/siyuan/issues/15034 + logging.LogWarnf("renaming invalid custom emoji file [%s] to [%s]", d.Name(), fullPathFilteredName) + if removeErr := filelock.Rename(emojiFullName, fullPathFilteredName); nil != removeErr { + logging.LogErrorf("renaming invalid custom emoji file to [%s] failed: %s", fullPathFilteredName, removeErr) + } + } + return nil + }) var emojiDirs []string filelock.Walk(unzipRootPath, func(path string, d fs.DirEntry, err error) error { if strings.Contains(path, "emojis") && d.IsDir() { @@ -675,6 +688,19 @@ func ImportData(zipPath string) (err error) { } tmpDataPath := filepath.Join(unzipPath, dirs[0].Name()) + tmpDataEmojisPath := filepath.Join(tmpDataPath, "emojis") + filelock.Walk(tmpDataEmojisPath, func(path string, d fs.DirEntry, err error) error { + if !util.IsValidUploadFileName(d.Name()) { + emojiFullName := filepath.Join(tmpDataEmojisPath, d.Name()) + fullPathFilteredName := filepath.Join(tmpDataEmojisPath, util.FilterUploadFileName(d.Name())) + // XSS through emoji name https://github.com/siyuan-note/siyuan/issues/15034 + logging.LogWarnf("renaming invalid custom emoji file [%s] to [%s]", d.Name(), fullPathFilteredName) + if removeErr := filelock.Rename(emojiFullName, fullPathFilteredName); nil != removeErr { + logging.LogErrorf("renaming invalid custom emoji file to [%s] failed: %s", fullPathFilteredName, removeErr) + } + } + return nil + }) if err = filelock.Copy(tmpDataPath, util.DataDir); err != nil { logging.LogErrorf("copy data dir from [%s] to [%s] failed: %s", tmpDataPath, util.DataDir, err) err = errors.New("copy data failed") diff --git a/kernel/util/file.go b/kernel/util/file.go index 5b792ed64..92fe70765 100644 --- a/kernel/util/file.go +++ b/kernel/util/file.go @@ -207,6 +207,7 @@ func FilterUploadFileName(name string) string { ret = strings.ReplaceAll(ret, "#", "") ret = strings.ReplaceAll(ret, "%", "") ret = strings.ReplaceAll(ret, "$", "") + ret = strings.ReplaceAll(ret, ";", "") ret = TruncateLenFileName(ret) return ret }