🧑‍💻 Add a kernel API /api/filetree/moveDocsByID https://github.com/siyuan-note/siyuan/issues/13247

This commit is contained in:
Daniel 2024-12-05 10:49:54 +08:00
parent c1fd34f57b
commit f21f0ea60b
No known key found for this signature in database
GPG Key ID: 86211BA83DF03017
4 changed files with 145 additions and 32 deletions

75
API.md
View File

@ -73,7 +73,8 @@
* Endpoint: `http://127.0.0.1:6806` * Endpoint: `http://127.0.0.1:6806`
* Both are POST methods * Both are POST methods
* An interface with parameters is required, the parameter is a JSON string, placed in the body, and the header Content-Type is `application/json` * An interface with parameters is required, the parameter is a JSON string, placed in the body, and the header
Content-Type is `application/json`
* Return value * Return value
````json ````json
@ -328,7 +329,8 @@ View API token in <kbd>Settings - About</kbd>, request header: `Authorization: T
``` ```
* `notebook`: Notebook ID * `notebook`: Notebook ID
* `path`: Document path, which needs to start with / and separate levels with / (path here corresponds to the database hpath field) * `path`: Document path, which needs to start with / and separate levels with / (path here corresponds to the
database hpath field)
* `markdown`: GFM Markdown content * `markdown`: GFM Markdown content
* Return value * Return value
@ -381,8 +383,8 @@ Rename a document by `id`:
} }
``` ```
* `id`: Document ID * `id`: Document ID
* `title`: New document title * `title`: New document title
* Return value * Return value
```json ```json
@ -416,7 +418,7 @@ Rename a document by `id`:
"data": null "data": null
} }
``` ```
Remove a document by `id`: Remove a document by `id`:
* `/api/filetree/removeDocByID` * `/api/filetree/removeDocByID`
@ -428,7 +430,7 @@ Remove a document by `id`:
} }
``` ```
* `id`: Document ID * `id`: Document ID
* Return value * Return value
```json ```json
@ -465,6 +467,30 @@ Remove a document by `id`:
} }
``` ```
Move documents by `id`:
* `/api/filetree/moveDocsByID`
* Parameters
```json
{
"fromIDs": ["20210917220056-yxtyl7i"],
"toID": "20210817205410-2kvfpfn"
}
```
* `fromIDs`: Source docs' IDs
* `toID`: Target parent ID
* Return value
```json
{
"code": 0,
"msg": "",
"data": null
}
```
### Get human-readable path based on path ### Get human-readable path based on path
* `/api/filetree/getHPathByPath` * `/api/filetree/getHPathByPath`
@ -510,7 +536,7 @@ Remove a document by `id`:
"data": "/foo/bar" "data": "/foo/bar"
} }
``` ```
### Get storage path based on ID ### Get storage path based on ID
* `/api/filetree/getPathByID` * `/api/filetree/getPathByID`
@ -522,7 +548,7 @@ Remove a document by `id`:
} }
``` ```
* `id`: Block ID * `id`: Block ID
* Return value * Return value
```json ```json
@ -545,8 +571,8 @@ Remove a document by `id`:
} }
``` ```
* `path`: Human-readable path * `path`: Human-readable path
* `notebook`: Notebook ID * `notebook`: Notebook ID
* Return value * Return value
```json ```json
@ -570,7 +596,8 @@ Remove a document by `id`:
* `"/assets/"`: workspace/data/assets/ folder * `"/assets/"`: workspace/data/assets/ folder
* `"/assets/sub/"`: workspace/data/assets/sub/ folder * `"/assets/sub/"`: workspace/data/assets/sub/ folder
Under normal circumstances, it is recommended to use the first method, which is stored in the assets folder of the workspace, putting in a subdirectory has some side effects, please refer to the assets chapter of the user guide. Under normal circumstances, it is recommended to use the first method, which is stored in the assets folder of the
workspace, putting in a subdirectory has some side effects, please refer to the assets chapter of the user guide.
* `file[]`: Uploaded file list * `file[]`: Uploaded file list
* Return value * Return value
@ -588,7 +615,9 @@ Remove a document by `id`:
``` ```
* `errFiles`: List of filenames with errors in upload processing * `errFiles`: List of filenames with errors in upload processing
* `succMap`: For successfully processed files, the key is the file name when uploading, and the value is assets/foo-id.png, which is used to replace the asset link address in the existing Markdown content with the uploaded address * `succMap`: For successfully processed files, the key is the file name when uploading, and the value is
assets/foo-id.png, which is used to replace the asset link address in the existing Markdown content with the
uploaded address
## Blocks ## Blocks
@ -613,7 +642,8 @@ Remove a document by `id`:
* `previousID`: The ID of the previous block, used to anchor the insertion position * `previousID`: The ID of the previous block, used to anchor the insertion position
* `parentID`: The ID of the parent block, used to anchor the insertion position * `parentID`: The ID of the parent block, used to anchor the insertion position
`nextID`, `previousID`, and `parentID` must have at least one value, using priority: `nextID` > `previousID` > `parentID` `nextID`, `previousID`, and `parentID` must have at least one value, using priority: `nextID` > `previousID` >
`parentID`
* Return value * Return value
```json ```json
@ -820,7 +850,8 @@ Remove a document by `id`:
* `id`: Block ID to move * `id`: Block ID to move
* `previousID`: The ID of the previous block, used to anchor the insertion position * `previousID`: The ID of the previous block, used to anchor the insertion position
* `parentID`: The ID of the parent block, used to anchor the insertion position, `previousID` and `parentID` cannot be empty at the same time, if they exist at the same time, `previousID` will be used first * `parentID`: The ID of the parent block, used to anchor the insertion position, `previousID` and `parentID` cannot
be empty at the same time, if they exist at the same time, `previousID` will be used first
* Return value * Return value
```json ```json
@ -860,7 +891,7 @@ Remove a document by `id`:
} }
``` ```
* `id`: Block ID to fold * `id`: Block ID to fold
* Return value * Return value
```json ```json
@ -882,7 +913,7 @@ Remove a document by `id`:
} }
``` ```
* `id`: Block ID to unfold * `id`: Block ID to unfold
* Return value * Return value
```json ```json
@ -1380,7 +1411,8 @@ Remove a document by `id`:
"timeout": 7000 "timeout": 7000
} }
``` ```
* `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 milliseconds * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000
milliseconds
* Return value * Return value
```json ```json
@ -1405,7 +1437,8 @@ Remove a document by `id`:
"timeout": 7000 "timeout": 7000
} }
``` ```
* `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 milliseconds * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000
milliseconds
* Return value * Return value
```json ```json
@ -1457,7 +1490,8 @@ Remove a document by `id`:
* `base32` | `base32-std` * `base32` | `base32-std`
* `base32-hex` * `base32-hex`
* `hex` * `hex`
* `responseEncoding`: The encoding scheme used by `body` in response data, default is `text`, optional values are as follows * `responseEncoding`: The encoding scheme used by `body` in response data, default is `text`, optional values are as
follows
* `text` * `text`
* `base64` | `base64-std` * `base64` | `base64-std`
@ -1484,7 +1518,8 @@ Remove a document by `id`:
} }
``` ```
* `bodyEncoding`: The encoding scheme used by `body`, is consistent with field `responseEncoding` in request, default is `text`, optional values are as follows * `bodyEncoding`: The encoding scheme used by `body`, is consistent with field `responseEncoding` in request,
default is `text`, optional values are as follows
* `text` * `text`
* `base64` | `base64-std` * `base64` | `base64-std`

View File

@ -381,8 +381,8 @@
} }
``` ```
* `id`:文档 ID * `id`:文档 ID
* `title`:新标题 * `title`:新标题
* 返回值 * 返回值
```json ```json
@ -416,7 +416,7 @@
"data": null "data": null
} }
``` ```
通过 `id` 删除文档: 通过 `id` 删除文档:
* `/api/filetree/removeDocByID` * `/api/filetree/removeDocByID`
@ -428,7 +428,7 @@
} }
``` ```
* `id`:文档 ID * `id`:文档 ID
* 返回值 * 返回值
```json ```json
@ -465,6 +465,30 @@
} }
``` ```
通过 `id` 移动文档:
* `/api/filetree/moveDocsByID`
* 参数
```json
{
"fromIDs": ["20210917220056-yxtyl7i"],
"toID": "20210817205410-2kvfpfn"
}
```
* `fromIDs`:源文档 ID
* `toID`:目标父文档 ID
* 返回值
```json
{
"code": 0,
"msg": "",
"data": null
}
```
### 根据路径获取人类可读路径 ### 根据路径获取人类可读路径
* `/api/filetree/getHPathByPath` * `/api/filetree/getHPathByPath`
@ -522,7 +546,7 @@
} }
``` ```
* `id`:块 ID * `id`:块 ID
* 返回值 * 返回值
```json ```json
@ -545,8 +569,8 @@
} }
``` ```
* `path`:人类可读路径 * `path`:人类可读路径
* `notebook`:笔记本 ID * `notebook`:笔记本 ID
* 返回值 * 返回值
```json ```json
@ -860,7 +884,7 @@
} }
``` ```
* `id`:待折叠块的 ID * `id`:待折叠块的 ID
* 返回值 * 返回值
```json ```json
@ -882,7 +906,7 @@
} }
``` ```
* `id`:待展开块的 ID * `id`:待展开块的 ID
* 返回值 * 返回值
```json ```json
@ -1063,7 +1087,7 @@
] ]
} }
``` ```
### 提交事务 ### 提交事务
* `/api/sqlite/flushTransaction` * `/api/sqlite/flushTransaction`

View File

@ -472,9 +472,7 @@ func moveDocs(c *gin.Context) {
if util.InvalidIDPattern(toNotebook, ret) { if util.InvalidIDPattern(toNotebook, ret) {
return return
} }
callback := arg["callback"] callback := arg["callback"]
err := model.MoveDocs(fromPaths, toNotebook, toPath, callback) err := model.MoveDocs(fromPaths, toNotebook, toPath, callback)
if err != nil { if err != nil {
ret.Code = -1 ret.Code = -1
@ -484,6 +482,61 @@ func moveDocs(c *gin.Context) {
} }
} }
func moveDocsByID(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
fromIDsArg := arg["fromIDs"].([]any)
var fromIDs []string
for _, fromIDArg := range fromIDsArg {
fromID := fromIDArg.(string)
if util.InvalidIDPattern(fromID, ret) {
return
}
fromIDs = append(fromIDs, fromID)
}
toID := arg["toID"].(string)
if util.InvalidIDPattern(toID, ret) {
return
}
var fromPaths []string
for _, fromID := range fromIDs {
tree, err := model.LoadTreeByBlockID(fromID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
ret.Data = map[string]interface{}{"closeTimeout": 7000}
return
}
fromPaths = append(fromPaths, tree.Path)
}
fromPaths = gulu.Str.RemoveDuplicatedElem(fromPaths)
toTree, err := model.LoadTreeByBlockID(toID)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
ret.Data = map[string]interface{}{"closeTimeout": 7000}
return
}
toNotebook := toTree.Box
toPath := toTree.Path
callback := arg["callback"]
err = model.MoveDocs(fromPaths, toNotebook, toPath, callback)
if err != nil {
ret.Code = -1
ret.Msg = err.Error()
ret.Data = map[string]interface{}{"closeTimeout": 7000}
return
}
}
func removeDoc(c *gin.Context) { func removeDoc(c *gin.Context) {
ret := gulu.Ret.NewResult() ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret) defer c.JSON(http.StatusOK, ret)

View File

@ -109,6 +109,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/filetree/removeDocByID", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocByID) ginServer.Handle("POST", "/api/filetree/removeDocByID", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocByID)
ginServer.Handle("POST", "/api/filetree/removeDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocs) ginServer.Handle("POST", "/api/filetree/removeDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocs)
ginServer.Handle("POST", "/api/filetree/moveDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, moveDocs) ginServer.Handle("POST", "/api/filetree/moveDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, moveDocs)
ginServer.Handle("POST", "/api/filetree/moveDocsByID", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, moveDocsByID)
ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, duplicateDoc) ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, duplicateDoc)
ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath) ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath)
ginServer.Handle("POST", "/api/filetree/getHPathsByPaths", model.CheckAuth, getHPathsByPaths) ginServer.Handle("POST", "/api/filetree/getHPathsByPaths", model.CheckAuth, getHPathsByPaths)