diff --git a/kernel/api/av.go b/kernel/api/av.go index 88953e6b3..06ded13ee 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -27,6 +27,29 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func duplicateAttributeViewBlock(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + avID := arg["avID"].(string) + + newAvID, newBlockID, err := model.DuplicateDatabaseBlock(avID) + if nil != err { + ret.Code = -1 + ret.Msg = err.Error() + return + } + + ret.Data = map[string]interface{}{ + "avID": newAvID, + "blockID": newBlockID, + } +} + func getAttributeViewKeysByAvID(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index d4e622fa5..2a53c6934 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -425,6 +425,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/setDatabaseBlockView", model.CheckAuth, model.CheckReadonly, setDatabaseBlockView) ginServer.Handle("POST", "/api/av/getMirrorDatabaseBlocks", model.CheckAuth, model.CheckReadonly, getMirrorDatabaseBlocks) ginServer.Handle("POST", "/api/av/getAttributeViewKeysByAvID", model.CheckAuth, model.CheckReadonly, getAttributeViewKeysByAvID) + ginServer.Handle("POST", "/api/av/duplicateAttributeViewBlock", model.CheckAuth, model.CheckReadonly, duplicateAttributeViewBlock) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index d7c048c8b..9f564200c 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -41,6 +41,46 @@ import ( "github.com/xrash/smetrics" ) +func DuplicateDatabaseBlock(avID string) (newAvID, newBlockID string, err error) { + storageAvDir := filepath.Join(util.DataDir, "storage", "av") + oldAvPath := filepath.Join(storageAvDir, avID+".json") + newAvID, newBlockID = ast.NewNodeID(), ast.NewNodeID() + + oldAv, err := av.ParseAttributeView(avID) + if nil != err { + return + } + + data, err := filelock.ReadFile(oldAvPath) + if nil != err { + logging.LogErrorf("read attribute view [%s] failed: %s", avID, err) + return + } + + data = bytes.ReplaceAll(data, []byte(avID), []byte(newAvID)) + av.UpsertBlockRel(newAvID, newBlockID) + + newAv := &av.AttributeView{} + if err = gulu.JSON.UnmarshalJSON(data, newAv); nil != err { + logging.LogErrorf("unmarshal attribute view [%s] failed: %s", newAvID, err) + return + } + + newAv.Name = oldAv.Name + " (Duplicated " + time.Now().Format("2006-01-02 15:04:05") + ")" + data, err = gulu.JSON.MarshalJSON(newAv) + if nil != err { + logging.LogErrorf("marshal attribute view [%s] failed: %s", newAvID, err) + return + } + + newAvPath := filepath.Join(storageAvDir, newAvID+".json") + if err = filelock.WriteFile(newAvPath, data); nil != err { + logging.LogErrorf("write attribute view [%s] failed: %s", newAvID, err) + return + } + return +} + func GetAttributeViewKeysByAvID(avID string) (ret []*av.Key) { ret = []*av.Key{}