mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-23 16:56:10 +01:00
❤️ 完整开源界面和内核 https://github.com/siyuan-note/siyuan/issues/5013
This commit is contained in:
parent
e650b8100c
commit
f40ed985e1
1214 changed files with 345766 additions and 9 deletions
92
kernel/api/account.go
Normal file
92
kernel/api/account.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func useActivationcode(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
code := arg["data"].(string)
|
||||
err := model.UseActivationcode(code)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func checkActivationcode(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
code := arg["data"].(string)
|
||||
ret.Code, ret.Msg = model.CheckActivationcode(code)
|
||||
}
|
||||
|
||||
func deactivateUser(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.DeactivateUser()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
ret.Code = -1
|
||||
|
||||
arg := map[string]interface{}{}
|
||||
if err := c.BindJSON(&arg); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "parses request failed"
|
||||
c.JSON(http.StatusOK, ret)
|
||||
return
|
||||
}
|
||||
|
||||
name := arg["userName"].(string)
|
||||
password := arg["userPassword"].(string)
|
||||
captcha := arg["captcha"].(string)
|
||||
result, err := model.Login(name, password, captcha)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
223
kernel/api/asset.go
Normal file
223
kernel/api/asset.go
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getDocImageAssets(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
assets, err := model.DocImageAssets(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = assets
|
||||
}
|
||||
|
||||
func setFileAnnotation(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p := arg["path"].(string)
|
||||
p = strings.ReplaceAll(p, "%23", "#")
|
||||
data := arg["data"].(string)
|
||||
writePath, err := resolveFileAnnotationAbsPath(p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := gulu.File.WriteFileSafer(writePath, []byte(data), 0644); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
model.IncWorkspaceDataVer()
|
||||
}
|
||||
|
||||
func getFileAnnotation(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p := arg["path"].(string)
|
||||
p = strings.ReplaceAll(p, "%23", "#")
|
||||
readPath, err := resolveFileAnnotationAbsPath(p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
if !gulu.File.IsExist(readPath) {
|
||||
ret.Code = 1
|
||||
return
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(readPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = map[string]interface{}{
|
||||
"data": string(data),
|
||||
}
|
||||
}
|
||||
|
||||
func resolveFileAnnotationAbsPath(assetRelPath string) (ret string, err error) {
|
||||
filePath := strings.TrimSuffix(assetRelPath, ".sya")
|
||||
absPath, err := model.GetAssetAbsPath(filePath)
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
dir := filepath.Dir(absPath)
|
||||
base := filepath.Base(assetRelPath)
|
||||
ret = filepath.Join(dir, base)
|
||||
return
|
||||
}
|
||||
|
||||
func removeUnusedAsset(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p := arg["path"].(string)
|
||||
asset := model.RemoveUnusedAsset(p)
|
||||
ret.Data = map[string]interface{}{
|
||||
"path": asset,
|
||||
}
|
||||
}
|
||||
|
||||
func removeUnusedAssets(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
paths := model.RemoveUnusedAssets()
|
||||
ret.Data = map[string]interface{}{
|
||||
"paths": paths,
|
||||
}
|
||||
}
|
||||
|
||||
func getUnusedAssets(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
unusedAssets := model.UnusedAssets()
|
||||
ret.Data = map[string]interface{}{
|
||||
"unusedAssets": unusedAssets,
|
||||
}
|
||||
}
|
||||
|
||||
func resolveAssetPath(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
path := arg["path"].(string)
|
||||
p, err := model.GetAssetAbsPath(path)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 3000}
|
||||
return
|
||||
}
|
||||
ret.Data = p
|
||||
return
|
||||
}
|
||||
|
||||
func uploadCloud(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
rootID := arg["id"].(string)
|
||||
err := model.UploadAssets2Cloud(rootID)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 3000}
|
||||
} else {
|
||||
util.PushMsg(model.Conf.Language(41), 3000)
|
||||
}
|
||||
}
|
||||
|
||||
func insertLocalAssets(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
assetPathsArg := arg["assetPaths"].([]interface{})
|
||||
var assetPaths []string
|
||||
for _, pathArg := range assetPathsArg {
|
||||
assetPaths = append(assetPaths, pathArg.(string))
|
||||
}
|
||||
id := arg["id"].(string)
|
||||
succMap, err := model.InsertLocalAssets(id, assetPaths)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = map[string]interface{}{
|
||||
"succMap": succMap,
|
||||
}
|
||||
}
|
||||
92
kernel/api/attr.go
Normal file
92
kernel/api/attr.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getBookmarkLabels(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = model.BookmarkLabels()
|
||||
}
|
||||
|
||||
func getBlockAttrs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
ret.Data = model.GetBlockAttrs(id)
|
||||
}
|
||||
|
||||
func setBlockAttrs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
attrs := arg["attrs"].(map[string]interface{})
|
||||
nameValues := map[string]string{}
|
||||
for name, value := range attrs {
|
||||
nameValues[name] = value.(string)
|
||||
}
|
||||
err := model.SetBlockAttrs(id, nameValues)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func resetBlockAttrs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
attrs := arg["attrs"].(map[string]interface{})
|
||||
nameValues := map[string]string{}
|
||||
for name, value := range attrs {
|
||||
nameValues[name] = value.(string)
|
||||
}
|
||||
err := model.ResetBlockAttrs(id, nameValues)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
129
kernel/api/backup.go
Normal file
129
kernel/api/backup.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func removeCloudBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.RemoveCloudBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func downloadCloudBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.DownloadBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func uploadLocalBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.UploadBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func recoverLocalBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.RecoverLocalBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func createLocalBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
err := model.CreateLocalBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalBackup(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
backup, err := model.GetLocalBackup()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"backup": backup,
|
||||
}
|
||||
}
|
||||
|
||||
func getCloudSpace(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
sync, backup, size, assetSize, totalSize, err := model.GetCloudSpace()
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
util.PushErrMsg(err.Error(), 3000)
|
||||
return
|
||||
}
|
||||
|
||||
hTrafficUploadSize := humanize.Bytes(uint64(model.Conf.User.UserTrafficUpload))
|
||||
hTrafficDownloadSize := humanize.Bytes(uint64(model.Conf.User.UserTrafficDownload))
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"sync": sync,
|
||||
"backup": backup,
|
||||
"hAssetSize": assetSize,
|
||||
"hSize": size,
|
||||
"hTotalSize": totalSize,
|
||||
"hTrafficUploadSize": hTrafficUploadSize,
|
||||
"hTrafficDownloadSize": hTrafficDownloadSize,
|
||||
}
|
||||
}
|
||||
276
kernel/api/bazaar.go
Normal file
276
kernel/api/bazaar.go
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getBazaarPackageREAME(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
repoURL := arg["repoURL"].(string)
|
||||
repoHash := arg["repoHash"].(string)
|
||||
ret.Data = map[string]interface{}{
|
||||
"html": model.GetPackageREADME(repoURL, repoHash),
|
||||
}
|
||||
}
|
||||
|
||||
func getBazaarWidget(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarWidgets(),
|
||||
}
|
||||
}
|
||||
|
||||
func installBazaarWidget(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
repoURL := arg["repoURL"].(string)
|
||||
repoHash := arg["repoHash"].(string)
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.InstallBazaarWidget(repoURL, repoHash, packageName)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
util.PushMsg(model.Conf.Language(69), 3000)
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarWidgets(),
|
||||
}
|
||||
}
|
||||
|
||||
func uninstallBazaarWidget(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.UninstallBazaarWidget(packageName)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarWidgets(),
|
||||
}
|
||||
}
|
||||
|
||||
func getBazaarIcon(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarIcons(),
|
||||
}
|
||||
}
|
||||
|
||||
func installBazaarIcon(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
repoURL := arg["repoURL"].(string)
|
||||
repoHash := arg["repoHash"].(string)
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.InstallBazaarIcon(repoURL, repoHash, packageName)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
util.PushMsg(model.Conf.Language(69), 3000)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarIcons(),
|
||||
"appearance": model.Conf.Appearance,
|
||||
}
|
||||
}
|
||||
|
||||
func uninstallBazaarIcon(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.UninstallBazaarIcon(packageName)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarIcons(),
|
||||
"appearance": model.Conf.Appearance,
|
||||
}
|
||||
}
|
||||
|
||||
func getBazaarTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarTemplates(),
|
||||
}
|
||||
}
|
||||
|
||||
func installBazaarTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
repoURL := arg["repoURL"].(string)
|
||||
repoHash := arg["repoHash"].(string)
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.InstallBazaarTemplate(repoURL, repoHash, packageName)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarTemplates(),
|
||||
}
|
||||
|
||||
util.PushMsg(model.Conf.Language(69), 3000)
|
||||
}
|
||||
|
||||
func uninstallBazaarTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.UninstallBazaarTemplate(packageName)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarTemplates(),
|
||||
}
|
||||
}
|
||||
|
||||
func getBazaarTheme(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarThemes(),
|
||||
}
|
||||
}
|
||||
|
||||
func installBazaarTheme(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
repoURL := arg["repoURL"].(string)
|
||||
repoHash := arg["repoHash"].(string)
|
||||
packageName := arg["packageName"].(string)
|
||||
mode := arg["mode"].(float64)
|
||||
update := false
|
||||
if nil != arg["update"] {
|
||||
update = arg["update"].(bool)
|
||||
}
|
||||
err := model.InstallBazaarTheme(repoURL, repoHash, packageName, int(mode), update)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
util.PushMsg(model.Conf.Language(69), 3000)
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarThemes(),
|
||||
"appearance": model.Conf.Appearance,
|
||||
}
|
||||
}
|
||||
|
||||
func uninstallBazaarTheme(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
packageName := arg["packageName"].(string)
|
||||
err := model.UninstallBazaarTheme(packageName)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"packages": model.BazaarThemes(),
|
||||
"appearance": model.Conf.Appearance,
|
||||
}
|
||||
}
|
||||
282
kernel/api/block.go
Normal file
282
kernel/api/block.go
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/html"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/sql"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func setBlockReminder(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
timed := arg["timed"].(string) // yyyyMMddHHmmss
|
||||
err := model.SetBlockReminder(id, timed)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func checkBlockFold(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
ret.Data = sql.IsBlockFolded(id)
|
||||
}
|
||||
|
||||
func checkBlockExist(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
b, err := model.GetBlock(id)
|
||||
if filesys.ErrUnableLockFile == err {
|
||||
ret.Code = 2
|
||||
ret.Data = id
|
||||
return
|
||||
}
|
||||
ret.Data = nil != b
|
||||
}
|
||||
|
||||
func getDocInfo(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
info := model.GetDocInfo(id)
|
||||
ret.Data = info
|
||||
}
|
||||
|
||||
func getRecentUpdatedBlocks(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
blocks := model.RecentUpdatedBlocks()
|
||||
ret.Data = blocks
|
||||
}
|
||||
|
||||
func getBlockWordCount(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
blockRuneCount, blockWordCount, rootBlockRuneCount, rootBlockWordCount := model.BlockWordCount(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"blockRuneCount": blockRuneCount,
|
||||
"blockWordCount": blockWordCount,
|
||||
"rootBlockRuneCount": rootBlockRuneCount,
|
||||
"rootBlockWordCount": rootBlockWordCount,
|
||||
}
|
||||
}
|
||||
|
||||
func getRefText(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
ret.Data = model.GetBlockRefText(id)
|
||||
}
|
||||
|
||||
func getRefIDs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
refIDs, refTexts, defIDs := model.GetBlockRefIDs(id)
|
||||
ret.Data = map[string][]string{
|
||||
"refIDs": refIDs,
|
||||
"refTexts": refTexts,
|
||||
"defIDs": defIDs,
|
||||
}
|
||||
}
|
||||
|
||||
func getRefIDsByFileAnnotationID(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
refIDs, refTexts := model.GetBlockRefIDsByFileAnnotationID(id)
|
||||
ret.Data = map[string][]string{
|
||||
"refIDs": refIDs,
|
||||
"refTexts": refTexts,
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockDefIDsByRefText(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
anchor := arg["anchor"].(string)
|
||||
excludeIDsArg := arg["excludeIDs"].([]interface{})
|
||||
var excludeIDs []string
|
||||
for _, excludeID := range excludeIDsArg {
|
||||
excludeIDs = append(excludeIDs, excludeID.(string))
|
||||
}
|
||||
excludeIDs = nil // 不限制虚拟引用搜索自己 https://ld246.com/article/1633243424177
|
||||
ids := model.GetBlockDefIDsByRefText(anchor, excludeIDs)
|
||||
ret.Data = ids
|
||||
}
|
||||
|
||||
func getBlockBreadcrumb(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
blockPath, err := model.BuildBlockBreadcrumb(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = blockPath
|
||||
}
|
||||
|
||||
func getBlockInfo(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
block, err := model.GetBlock(id)
|
||||
if filesys.ErrUnableLockFile == err {
|
||||
ret.Code = 2
|
||||
ret.Data = id
|
||||
return
|
||||
}
|
||||
if nil == block {
|
||||
ret.Code = 1
|
||||
ret.Msg = fmt.Sprintf(model.Conf.Language(15), id)
|
||||
return
|
||||
}
|
||||
|
||||
var rootChildID string
|
||||
b := block
|
||||
for i := 0; i < 128; i++ {
|
||||
parentID := b.ParentID
|
||||
if "" == parentID {
|
||||
rootChildID = b.ID
|
||||
break
|
||||
}
|
||||
if b, _ = model.GetBlock(parentID); nil == b {
|
||||
util.LogErrorf("not found parent")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
root, err := model.GetBlock(block.RootID)
|
||||
if filesys.ErrUnableLockFile == err {
|
||||
ret.Code = 2
|
||||
ret.Data = id
|
||||
return
|
||||
}
|
||||
rootTitle := root.IAL["title"]
|
||||
rootTitle = html.UnescapeString(rootTitle)
|
||||
ret.Data = map[string]string{
|
||||
"box": block.Box,
|
||||
"path": block.Path,
|
||||
"rootID": block.RootID,
|
||||
"rootTitle": rootTitle,
|
||||
"rootChildID": rootChildID,
|
||||
"rootIcon": root.IAL["icon"],
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockDOM(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
dom := model.GetBlockDOM(id)
|
||||
ret.Data = map[string]string{
|
||||
"id": id,
|
||||
"dom": dom,
|
||||
}
|
||||
}
|
||||
301
kernel/api/block_op.go
Normal file
301
kernel/api/block_op.go
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute"
|
||||
"github.com/88250/lute/ast"
|
||||
"github.com/88250/protyle"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func appendBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
data := arg["data"].(string)
|
||||
dataType := arg["dataType"].(string)
|
||||
parentID := arg["parentID"].(string)
|
||||
if "markdown" == dataType {
|
||||
luteEngine := model.NewLute()
|
||||
data = dataBlockDOM(data, luteEngine)
|
||||
}
|
||||
|
||||
transactions := []*model.Transaction{
|
||||
{
|
||||
DoOperations: []*model.Operation{
|
||||
{
|
||||
Action: "appendInsert",
|
||||
Data: data,
|
||||
ParentID: parentID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := model.PerformTransactions(&transactions)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
|
||||
ret.Data = transactions
|
||||
broadcastTransactions(transactions)
|
||||
}
|
||||
|
||||
func prependBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
data := arg["data"].(string)
|
||||
dataType := arg["dataType"].(string)
|
||||
parentID := arg["parentID"].(string)
|
||||
if "markdown" == dataType {
|
||||
luteEngine := model.NewLute()
|
||||
data = dataBlockDOM(data, luteEngine)
|
||||
}
|
||||
|
||||
transactions := []*model.Transaction{
|
||||
{
|
||||
DoOperations: []*model.Operation{
|
||||
{
|
||||
Action: "prependInsert",
|
||||
Data: data,
|
||||
ParentID: parentID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := model.PerformTransactions(&transactions)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
|
||||
ret.Data = transactions
|
||||
broadcastTransactions(transactions)
|
||||
}
|
||||
|
||||
func insertBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
data := arg["data"].(string)
|
||||
dataType := arg["dataType"].(string)
|
||||
var parentID, previousID string
|
||||
if nil != arg["parentID"] {
|
||||
parentID = arg["parentID"].(string)
|
||||
}
|
||||
if nil != arg["previousID"] {
|
||||
previousID = arg["previousID"].(string)
|
||||
}
|
||||
|
||||
if "markdown" == dataType {
|
||||
luteEngine := model.NewLute()
|
||||
data = dataBlockDOM(data, luteEngine)
|
||||
}
|
||||
|
||||
transactions := []*model.Transaction{
|
||||
{
|
||||
DoOperations: []*model.Operation{
|
||||
{
|
||||
Action: "insert",
|
||||
Data: data,
|
||||
ParentID: parentID,
|
||||
PreviousID: previousID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := model.PerformTransactions(&transactions)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
|
||||
ret.Data = transactions
|
||||
broadcastTransactions(transactions)
|
||||
}
|
||||
|
||||
func updateBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
data := arg["data"].(string)
|
||||
dataType := arg["dataType"].(string)
|
||||
id := arg["id"].(string)
|
||||
|
||||
luteEngine := model.NewLute()
|
||||
if "markdown" == dataType {
|
||||
data = dataBlockDOM(data, luteEngine)
|
||||
}
|
||||
tree := luteEngine.BlockDOM2Tree(data)
|
||||
if nil == tree || nil == tree.Root || nil == tree.Root.FirstChild {
|
||||
ret.Code = -1
|
||||
ret.Msg = "parse tree failed"
|
||||
return
|
||||
}
|
||||
|
||||
block, err := model.GetBlock(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "get block failed: " + err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
var transactions []*model.Transaction
|
||||
if "NodeDocument" == block.Type {
|
||||
oldTree, err := model.LoadTree(block.Box, block.Path)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "load tree failed: " + err.Error()
|
||||
return
|
||||
}
|
||||
var toRemoves []*ast.Node
|
||||
var ops []*model.Operation
|
||||
for n := oldTree.Root.FirstChild; nil != n; n = n.Next {
|
||||
toRemoves = append(toRemoves, n)
|
||||
ops = append(ops, &model.Operation{Action: "delete", ID: n.ID})
|
||||
}
|
||||
for _, n := range toRemoves {
|
||||
n.Unlink()
|
||||
}
|
||||
ops = append(ops, &model.Operation{Action: "appendInsert", Data: data, ParentID: id})
|
||||
transactions = append(transactions, &model.Transaction{
|
||||
DoOperations: ops,
|
||||
})
|
||||
} else {
|
||||
if "NodeListItem" == block.Type {
|
||||
// 使用 API `api/block/updateBlock` 更新列表项时渲染错误 https://github.com/siyuan-note/siyuan/issues/4658
|
||||
|
||||
tree.Root.AppendChild(tree.Root.FirstChild.FirstChild) // 将列表下的第一个列表项移到文档结尾,移动以后根下面直接挂列表项,渲染器可以正常工作
|
||||
tree.Root.FirstChild.Unlink() // 删除列表
|
||||
tree.Root.FirstChild.Unlink() // 继续删除列表 IAL
|
||||
}
|
||||
tree.Root.FirstChild.SetIALAttr("id", id)
|
||||
|
||||
data = luteEngine.Tree2BlockDOM(tree, luteEngine.RenderOptions)
|
||||
transactions = []*model.Transaction{
|
||||
{
|
||||
DoOperations: []*model.Operation{
|
||||
{
|
||||
Action: "update",
|
||||
ID: id,
|
||||
Data: data,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
err = model.PerformTransactions(&transactions)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
|
||||
ret.Data = transactions
|
||||
broadcastTransactions(transactions)
|
||||
}
|
||||
|
||||
func deleteBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
|
||||
transactions := []*model.Transaction{
|
||||
{
|
||||
DoOperations: []*model.Operation{
|
||||
{
|
||||
Action: "delete",
|
||||
ID: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := model.PerformTransactions(&transactions)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = transactions
|
||||
broadcastTransactions(transactions)
|
||||
}
|
||||
|
||||
func broadcastTransactions(transactions []*model.Transaction) {
|
||||
evt := util.NewCmdResult("transactions", 0, util.PushModeBroadcast, util.PushModeBroadcast)
|
||||
evt.Data = transactions
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func dataBlockDOM(data string, luteEngine *lute.Lute) (ret string) {
|
||||
ret = luteEngine.Md2BlockDOM(data)
|
||||
if "" == ret {
|
||||
// 使用 API 插入空字符串出现错误 https://github.com/siyuan-note/siyuan/issues/3931
|
||||
blankParagraph := protyle.NewParagraph()
|
||||
ret = lute.RenderNodeBlockDOM(blankParagraph, luteEngine.ParseOptions, luteEngine.RenderOptions)
|
||||
}
|
||||
return
|
||||
}
|
||||
52
kernel/api/bookmark.go
Normal file
52
kernel/api/bookmark.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getBookmark(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = model.BuildBookmark()
|
||||
}
|
||||
|
||||
func renameBookmark(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
oldBookmark := arg["oldBookmark"].(string)
|
||||
newBookmark := arg["newBookmark"].(string)
|
||||
if err := model.RenameBookmark(oldBookmark, newBookmark); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
31
kernel/api/clipboard.go
Normal file
31
kernel/api/clipboard.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/88250/clipboard"
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func readFilePaths(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(200, ret)
|
||||
|
||||
paths, _ := clipboard.ReadFilePaths()
|
||||
ret.Data = paths
|
||||
}
|
||||
219
kernel/api/export.go
Normal file
219
kernel/api/export.go
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func exportDataInFolder(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
exportFolder := arg["folder"].(string)
|
||||
err := model.ExportDataInFolder(exportFolder)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func exportData(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
zipPath := model.ExportData()
|
||||
ret.Data = map[string]interface{}{
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
func batchExportMd(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
zipPath := model.BatchExportMarkdown(notebook, p)
|
||||
ret.Data = map[string]interface{}{
|
||||
"name": path.Base(zipPath),
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
func exportMd(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
name, zipPath := model.ExportMarkdown(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"name": name,
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
func exportSY(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
name, zipPath := model.ExportSY(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"name": name,
|
||||
"zip": zipPath,
|
||||
}
|
||||
}
|
||||
|
||||
func exportMdContent(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
hPath, content := model.ExportMarkdownContent(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"hPath": hPath,
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
|
||||
func exportDocx(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
savePath := arg["savePath"].(string)
|
||||
err := model.ExportDocx(id, savePath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func exportMdHTML(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
savePath := arg["savePath"].(string)
|
||||
name, content := model.ExportMarkdownHTML(id, savePath, false)
|
||||
ret.Data = map[string]interface{}{
|
||||
"id": id,
|
||||
"name": name,
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
|
||||
func exportHTML(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
pdf := arg["pdf"].(bool)
|
||||
savePath := arg["savePath"].(string)
|
||||
name, content := model.ExportHTML(id, savePath, pdf)
|
||||
ret.Data = map[string]interface{}{
|
||||
"id": id,
|
||||
"name": name,
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
|
||||
func addPDFOutline(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
path := arg["path"].(string)
|
||||
err := model.AddPDFOutline(id, path)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func exportPreview(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
stdHTML := model.Preview(id)
|
||||
ret.Data = map[string]interface{}{
|
||||
"html": stdHTML,
|
||||
}
|
||||
}
|
||||
135
kernel/api/extension.go
Normal file
135
kernel/api/extension.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/ast"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func extensionCopy(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(200, ret)
|
||||
|
||||
form, _ := c.MultipartForm()
|
||||
dom := form.Value["dom"][0]
|
||||
assets := filepath.Join(util.DataDir, "assets")
|
||||
if notebookVal := form.Value["notebook"]; 0 < len(notebookVal) {
|
||||
assets = filepath.Join(util.DataDir, notebookVal[0], "assets")
|
||||
if !gulu.File.IsDir(assets) {
|
||||
assets = filepath.Join(util.DataDir, "assets")
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(assets, 0755); nil != err {
|
||||
util.LogErrorf("create assets folder [%s] failed: %s", assets, err)
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
luteEngine := model.NewLute()
|
||||
md := luteEngine.HTML2Md(dom)
|
||||
md = strings.TrimSpace(md)
|
||||
ret.Data = map[string]interface{}{
|
||||
"md": md,
|
||||
}
|
||||
|
||||
uploaded := map[string]string{}
|
||||
for originalName, file := range form.File {
|
||||
oName, err := url.PathUnescape(originalName)
|
||||
if nil != err {
|
||||
if strings.Contains(originalName, "%u") {
|
||||
originalName = strings.ReplaceAll(originalName, "%u", "\\u")
|
||||
originalName, err = strconv.Unquote("\"" + originalName + "\"")
|
||||
if nil != err {
|
||||
continue
|
||||
}
|
||||
oName, err = url.PathUnescape(originalName)
|
||||
if nil != err {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
u, _ := url.Parse(oName)
|
||||
if "" == u.Path {
|
||||
continue
|
||||
}
|
||||
fName := path.Base(u.Path)
|
||||
fName = util.FilterUploadFileName(fName)
|
||||
f, err := file[0].Open()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
break
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(f)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
break
|
||||
}
|
||||
|
||||
ext := path.Ext(fName)
|
||||
fName = fName[0 : len(fName)-len(ext)]
|
||||
if "" == ext && bytes.HasPrefix(data, []byte("<svg ")) && bytes.HasSuffix(data, []byte("</svg>")) {
|
||||
ext = ".svg"
|
||||
}
|
||||
fName = fName + "-" + ast.NewNodeID() + ext
|
||||
writePath := filepath.Join(assets, fName)
|
||||
if err = gulu.File.WriteFileSafer(writePath, data, 0644); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
break
|
||||
}
|
||||
|
||||
uploaded[oName] = "assets/" + fName
|
||||
}
|
||||
|
||||
for k, v := range uploaded {
|
||||
if "" == md {
|
||||
// 复制单个图片的情况
|
||||
md = ""
|
||||
break
|
||||
}
|
||||
md = strings.ReplaceAll(md, "]("+k+")", "]("+v+")")
|
||||
p, err := url.Parse(k)
|
||||
if nil != err {
|
||||
continue
|
||||
}
|
||||
md = strings.ReplaceAll(md, "]("+p.Path+")", "]("+v+")")
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"md": md,
|
||||
}
|
||||
ret.Msg = model.Conf.Language(72)
|
||||
}
|
||||
147
kernel/api/file.go
Normal file
147
kernel/api/file.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getFile(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
c.JSON(http.StatusOK, ret)
|
||||
return
|
||||
}
|
||||
|
||||
filePath := arg["path"].(string)
|
||||
filePath = filepath.Join(util.WorkspaceDir, filePath)
|
||||
info, err := os.Stat(filePath)
|
||||
if os.IsNotExist(err) {
|
||||
c.Status(404)
|
||||
return
|
||||
}
|
||||
if nil != err {
|
||||
util.LogErrorf("stat [%s] failed: %s", filePath, err)
|
||||
c.Status(500)
|
||||
return
|
||||
}
|
||||
if info.IsDir() {
|
||||
util.LogErrorf("file [%s] is a directory", filePath)
|
||||
c.Status(405)
|
||||
return
|
||||
}
|
||||
|
||||
if err = model.ServeFile(c, filePath); nil != err {
|
||||
c.Status(http.StatusConflict)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func putFile(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
filePath := c.PostForm("path")
|
||||
filePath = filepath.Join(util.WorkspaceDir, filePath)
|
||||
isDirStr := c.PostForm("isDir")
|
||||
isDir, _ := strconv.ParseBool(isDirStr)
|
||||
|
||||
var err error
|
||||
if isDir {
|
||||
err = os.MkdirAll(filePath, 0755)
|
||||
if nil != err {
|
||||
util.LogErrorf("make a dir [%s] failed: %s", filePath, err)
|
||||
}
|
||||
} else {
|
||||
file, _ := c.FormFile("file")
|
||||
if nil == file {
|
||||
util.LogErrorf("form file is nil [path=%s]", filePath)
|
||||
c.Status(400)
|
||||
return
|
||||
}
|
||||
|
||||
dir := filepath.Dir(filePath)
|
||||
if err = os.MkdirAll(dir, 0755); nil != err {
|
||||
util.LogErrorf("put a file [%s] make dir [%s] failed: %s", filePath, dir, err)
|
||||
} else {
|
||||
if filesys.IsLocked(filePath) {
|
||||
msg := fmt.Sprintf("file [%s] is locked", filePath)
|
||||
util.LogErrorf(msg)
|
||||
err = errors.New(msg)
|
||||
} else {
|
||||
err = writeFile(file, filePath)
|
||||
if nil != err {
|
||||
util.LogErrorf("put a file [%s] failed: %s", filePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
modTimeStr := c.PostForm("modTime")
|
||||
modTimeInt, err := strconv.ParseInt(modTimeStr, 10, 64)
|
||||
if nil != err {
|
||||
util.LogErrorf("parse mod time [%s] failed: %s", modTimeStr, err)
|
||||
c.Status(500)
|
||||
return
|
||||
}
|
||||
modTime := millisecond2Time(modTimeInt)
|
||||
if err = os.Chtimes(filePath, modTime, modTime); nil != err {
|
||||
util.LogErrorf("change time failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func writeFile(file *multipart.FileHeader, dst string) error {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
err = gulu.File.WriteFileSaferByReader(dst, src, 0644)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func millisecond2Time(t int64) time.Time {
|
||||
sec := t / 1000
|
||||
msec := t % 1000
|
||||
return time.Unix(sec, msec*int64(time.Millisecond))
|
||||
}
|
||||
636
kernel/api/filetree.go
Normal file
636
kernel/api/filetree.go
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func refreshFiletree(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
model.RefreshFileTree()
|
||||
}
|
||||
|
||||
func doc2Heading(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
srcID := arg["srcID"].(string)
|
||||
targetID := arg["targetID"].(string)
|
||||
after := arg["after"].(bool)
|
||||
srcTreeBox, srcTreePath, err := model.Doc2Heading(srcID, targetID, after)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"srcTreeBox": srcTreeBox,
|
||||
"srcTreePath": srcTreePath,
|
||||
}
|
||||
}
|
||||
|
||||
func heading2Doc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
srcHeadingID := arg["srcHeadingID"].(string)
|
||||
targetNotebook := arg["targetNoteBook"].(string)
|
||||
targetPath := arg["targetPath"].(string)
|
||||
srcRootBlockID, targetPath, err := model.Heading2Doc(srcHeadingID, targetNotebook, targetPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
tree, err := model.LoadTree(targetNotebook, targetPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
name := path.Base(targetPath)
|
||||
box := model.Conf.Box(targetNotebook)
|
||||
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort)
|
||||
evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": box,
|
||||
"path": targetPath,
|
||||
"files": files,
|
||||
"name": name,
|
||||
"id": tree.Root.ID,
|
||||
"srcRootBlockID": srcRootBlockID,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func li2Doc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
srcListItemID := arg["srcListItemID"].(string)
|
||||
targetNotebook := arg["targetNoteBook"].(string)
|
||||
targetPath := arg["targetPath"].(string)
|
||||
srcRootBlockID, targetPath, err := model.ListItem2Doc(srcListItemID, targetNotebook, targetPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
model.WaitForWritingFiles()
|
||||
tree, err := model.LoadTree(targetNotebook, targetPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
name := path.Base(targetPath)
|
||||
box := model.Conf.Box(targetNotebook)
|
||||
files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), model.Conf.FileTree.Sort)
|
||||
evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": box,
|
||||
"path": targetPath,
|
||||
"files": files,
|
||||
"name": name,
|
||||
"id": tree.Root.ID,
|
||||
"srcRootBlockID": srcRootBlockID,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func getHPathByPath(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
|
||||
hPath, err := model.GetHPathByPath(notebook, p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = hPath
|
||||
}
|
||||
|
||||
func getHPathByID(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
hPath, err := model.GetHPathByID(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = hPath
|
||||
}
|
||||
|
||||
func getFullHPathByID(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
hPath, err := model.GetFullHPathByID(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = hPath
|
||||
}
|
||||
|
||||
func moveDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fromNotebook := arg["fromNotebook"].(string)
|
||||
toNotebook := arg["toNotebook"].(string)
|
||||
fromPath := arg["fromPath"].(string)
|
||||
toPath := arg["toPath"].(string)
|
||||
|
||||
newPath, err := model.MoveDoc(fromNotebook, fromPath, toNotebook, toPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("moveDoc", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"fromNotebook": fromNotebook,
|
||||
"toNotebook": toNotebook,
|
||||
"fromPath": fromPath,
|
||||
"toPath": toPath,
|
||||
"newPath": newPath,
|
||||
}
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func removeDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
|
||||
err := model.RemoveDoc(notebook, p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("remove", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
"path": p,
|
||||
}
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func renameDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
title := arg["title"].(string)
|
||||
|
||||
err := model.RenameDoc(notebook, p, title)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func duplicateDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
err := model.DuplicateDoc(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
|
||||
block, _ := model.GetBlock(id)
|
||||
p := block.Path
|
||||
notebook := block.Box
|
||||
box := model.Conf.Box(notebook)
|
||||
tree, err := model.LoadTree(box.ID, p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
pushCreate(box, p, tree.Root.ID, arg)
|
||||
}
|
||||
|
||||
func createDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
title := arg["title"].(string)
|
||||
md := arg["md"].(string)
|
||||
|
||||
err := model.CreateDocByMd(notebook, p, title, md)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||
return
|
||||
}
|
||||
|
||||
box := model.Conf.Box(notebook)
|
||||
tree, err := model.LoadTree(box.ID, p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
pushCreate(box, p, tree.Root.ID, arg)
|
||||
}
|
||||
|
||||
func createDailyNote(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p, err := model.CreateDailyNote(notebook)
|
||||
if nil != err {
|
||||
if model.ErrBoxNotFound == err {
|
||||
ret.Code = 1
|
||||
} else {
|
||||
ret.Code = -1
|
||||
}
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
box := model.Conf.Box(notebook)
|
||||
model.WaitForWritingFiles()
|
||||
tree, err := model.LoadTree(box.ID, p)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("createdailynote", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
name := path.Base(p)
|
||||
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": box,
|
||||
"path": p,
|
||||
"files": files,
|
||||
"name": name,
|
||||
"id": tree.Root.ID,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func createDocWithMd(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
hPath := arg["path"].(string)
|
||||
markdown := arg["markdown"].(string)
|
||||
|
||||
baseName := path.Base(hPath)
|
||||
dir := path.Dir(hPath)
|
||||
r, _ := regexp.Compile("\r\n|\r|\n|\u2028|\u2029|\t|/")
|
||||
baseName = r.ReplaceAllString(baseName, "")
|
||||
if 512 < utf8.RuneCountInString(baseName) {
|
||||
baseName = gulu.Str.SubStr(baseName, 512)
|
||||
}
|
||||
hPath = path.Join(dir, baseName)
|
||||
if !strings.HasPrefix(hPath, "/") {
|
||||
hPath = "/" + hPath
|
||||
}
|
||||
|
||||
id, err := model.CreateWithMarkdown(notebook, hPath, markdown)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = id
|
||||
|
||||
box := model.Conf.Box(notebook)
|
||||
b, _ := model.GetBlock(id)
|
||||
p := b.Path
|
||||
pushCreate(box, p, id, arg)
|
||||
}
|
||||
|
||||
func lockFile(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
locked, filePath := model.LockFileByBlockID(id)
|
||||
if !locked {
|
||||
ret.Code = -1
|
||||
ret.Msg = fmt.Sprintf(model.Conf.Language(75), filePath)
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
}
|
||||
}
|
||||
|
||||
func getDocNameTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
box := model.Conf.Box(notebook)
|
||||
nameTemplate := model.Conf.FileTree.CreateDocNameTemplate
|
||||
if nil != box {
|
||||
nameTemplate = box.GetConf().CreateDocNameTemplate
|
||||
}
|
||||
if "" == nameTemplate {
|
||||
nameTemplate = model.Conf.FileTree.CreateDocNameTemplate
|
||||
}
|
||||
|
||||
name, err := model.RenderCreateDocNameTemplate(nameTemplate)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = map[string]interface{}{
|
||||
"name": name,
|
||||
}
|
||||
}
|
||||
|
||||
func changeSort(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
pathsArg := arg["paths"].([]interface{})
|
||||
var paths []string
|
||||
for _, p := range pathsArg {
|
||||
paths = append(paths, p.(string))
|
||||
}
|
||||
model.ChangeFileTreeSort(notebook, paths)
|
||||
}
|
||||
|
||||
func searchDocs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
k := arg["k"].(string)
|
||||
ret.Data = model.SearchDocsByKeyword(k)
|
||||
}
|
||||
|
||||
func listDocsByPath(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
p := arg["path"].(string)
|
||||
sortParam := arg["sort"]
|
||||
sortMode := model.Conf.FileTree.Sort
|
||||
if nil != sortParam {
|
||||
sortMode = int(sortParam.(float64))
|
||||
}
|
||||
files, totals, err := model.ListDocTree(notebook, p, sortMode)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if model.Conf.FileTree.MaxListCount < totals {
|
||||
util.PushMsg(fmt.Sprintf(model.Conf.Language(48), len(files)), 7000)
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
"path": p,
|
||||
"files": files,
|
||||
}
|
||||
|
||||
// 持久化文档面板排序
|
||||
model.Conf.FileTree.Sort = sortMode
|
||||
model.Conf.Save()
|
||||
}
|
||||
|
||||
func getDoc(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
idx := arg["index"]
|
||||
index := 0
|
||||
if nil != idx {
|
||||
index = int(idx.(float64))
|
||||
}
|
||||
k := arg["k"]
|
||||
var keyword string
|
||||
if nil != k {
|
||||
keyword = k.(string)
|
||||
}
|
||||
m := arg["mode"] // 0: 仅当前 ID,1:向上 2:向下,3:上下都加载,4:加载末尾
|
||||
mode := 0
|
||||
if nil != m {
|
||||
mode = int(m.(float64))
|
||||
}
|
||||
s := arg["size"]
|
||||
size := 102400 // 默认最大加载块数
|
||||
if nil != s {
|
||||
size = int(s.(float64))
|
||||
}
|
||||
|
||||
blockCount, content, parentID, parent2ID, rootID, typ, eof, boxID, docPath, err := model.GetDoc(id, index, keyword, mode, size)
|
||||
if filesys.ErrUnableLockFile == err {
|
||||
ret.Code = 2
|
||||
ret.Data = id
|
||||
return
|
||||
}
|
||||
if model.ErrBlockNotFound == err {
|
||||
ret.Code = 3
|
||||
return
|
||||
}
|
||||
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"id": id,
|
||||
"mode": mode,
|
||||
"parentID": parentID,
|
||||
"parent2ID": parent2ID,
|
||||
"rootID": rootID,
|
||||
"type": typ,
|
||||
"content": content,
|
||||
"blockCount": blockCount,
|
||||
"eof": eof,
|
||||
"box": boxID,
|
||||
"path": docPath,
|
||||
}
|
||||
}
|
||||
|
||||
func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) {
|
||||
evt := util.NewCmdResult("create", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
name := path.Base(p)
|
||||
files, _, _ := model.ListDocTree(box.ID, path.Dir(p), model.Conf.FileTree.Sort)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": box,
|
||||
"path": p,
|
||||
"files": files,
|
||||
"name": name,
|
||||
"id": treeID,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
64
kernel/api/format.go
Normal file
64
kernel/api/format.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func netImg2LocalAssets(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
err := model.NetImg2LocalAssets(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func autoSpace(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
err := model.AutoSpace(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
131
kernel/api/graph.go
Normal file
131
kernel/api/graph.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/conf"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func resetGraph(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
graph := conf.NewGlobalGraph()
|
||||
model.Conf.Graph.Global = graph
|
||||
model.Conf.Save()
|
||||
ret.Data = map[string]interface{}{
|
||||
"conf": graph,
|
||||
}
|
||||
}
|
||||
|
||||
func resetLocalGraph(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
graph := conf.NewLocalGraph()
|
||||
model.Conf.Graph.Local = graph
|
||||
model.Conf.Save()
|
||||
ret.Data = map[string]interface{}{
|
||||
"conf": graph,
|
||||
}
|
||||
}
|
||||
|
||||
func getGraph(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
query := arg["k"].(string)
|
||||
|
||||
graphConf, err := gulu.JSON.MarshalJSON(arg["conf"])
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
global := conf.NewGlobalGraph()
|
||||
if err = gulu.JSON.UnmarshalJSON(graphConf, global); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.Graph.Global = global
|
||||
model.Conf.Save()
|
||||
|
||||
boxID, nodes, links := model.BuildGraph(query)
|
||||
ret.Data = map[string]interface{}{
|
||||
"nodes": nodes,
|
||||
"links": links,
|
||||
"conf": global,
|
||||
"box": boxID,
|
||||
"reqId": arg["reqId"],
|
||||
}
|
||||
util.RandomSleep(200, 500)
|
||||
}
|
||||
|
||||
func getLocalGraph(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
keyword := arg["k"].(string)
|
||||
id := arg["id"].(string)
|
||||
|
||||
graphConf, err := gulu.JSON.MarshalJSON(arg["conf"])
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
local := conf.NewLocalGraph()
|
||||
if err = gulu.JSON.UnmarshalJSON(graphConf, local); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.Graph.Local = local
|
||||
model.Conf.Save()
|
||||
|
||||
boxID, nodes, links := model.BuildTreeGraph(id, keyword)
|
||||
ret.Data = map[string]interface{}{
|
||||
"id": id,
|
||||
"box": boxID,
|
||||
"nodes": nodes,
|
||||
"links": links,
|
||||
"conf": local,
|
||||
"reqId": arg["reqId"],
|
||||
}
|
||||
util.RandomSleep(200, 500)
|
||||
}
|
||||
178
kernel/api/history.go
Normal file
178
kernel/api/history.go
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getNotebookHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
histories, err := model.GetNotebookHistory()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"histories": histories,
|
||||
}
|
||||
}
|
||||
|
||||
func getAssetsHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
histories, err := model.GetAssetsHistory()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"histories": histories,
|
||||
}
|
||||
}
|
||||
|
||||
func clearWorkspaceHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
util.PushMsg(model.Conf.Language(100), 1000*60*15)
|
||||
time.Sleep(3 * time.Second)
|
||||
err := model.ClearWorkspaceHistory()
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
util.PushMsg(model.Conf.Language(99), 1000*5)
|
||||
}
|
||||
|
||||
func getDocHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
histories, err := model.GetDocHistory(notebook)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
"histories": histories,
|
||||
}
|
||||
}
|
||||
|
||||
func getDocHistoryContent(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
historyPath := arg["historyPath"].(string)
|
||||
content, err := model.GetDocHistoryContent(historyPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
|
||||
func rollbackDocHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
historyPath := arg["historyPath"].(string)
|
||||
err := model.RollbackDocHistory(notebook, historyPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
}
|
||||
}
|
||||
|
||||
func rollbackAssetsHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
historyPath := arg["historyPath"].(string)
|
||||
err := model.RollbackAssetsHistory(historyPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func rollbackNotebookHistory(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
historyPath := arg["historyPath"].(string)
|
||||
err := model.RollbackNotebookHistory(historyPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
179
kernel/api/import.go
Normal file
179
kernel/api/import.go
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func importSY(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(200, ret)
|
||||
|
||||
form, err := c.MultipartForm()
|
||||
if nil != err {
|
||||
util.LogErrorf("parse import .sy.zip failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
files := form.File["file"]
|
||||
if 1 > len(files) {
|
||||
util.LogErrorf("parse import .sy.zip failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
file := files[0]
|
||||
reader, err := file.Open()
|
||||
if nil != err {
|
||||
util.LogErrorf("read import .sy.zip failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
importDir := filepath.Join(util.TempDir, "import")
|
||||
if err = os.MkdirAll(importDir, 0755); nil != err {
|
||||
util.LogErrorf("make import dir [%s] failed: %s", importDir, err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
writePath := filepath.Join(util.TempDir, "import", file.Filename)
|
||||
defer os.RemoveAll(writePath)
|
||||
writer, err := os.OpenFile(writePath, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if nil != err {
|
||||
util.LogErrorf("open import .sy.zip [%s] failed: %s", writePath, err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(writer, reader); nil != err {
|
||||
util.LogErrorf("write import .sy.zip failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
writer.Close()
|
||||
reader.Close()
|
||||
|
||||
notebook := form.Value["notebook"][0]
|
||||
toPath := form.Value["toPath"][0]
|
||||
|
||||
err = model.ImportSY(writePath, notebook, toPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func importData(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
form, err := c.MultipartForm()
|
||||
if nil != err {
|
||||
util.LogErrorf("import data failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if 1 > len(form.File["file"]) {
|
||||
util.LogErrorf("import data failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = "file not found"
|
||||
return
|
||||
}
|
||||
|
||||
tmpImport := filepath.Join(util.TempDir, "import")
|
||||
err = os.MkdirAll(tmpImport, 0755)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "create temp import dir failed"
|
||||
return
|
||||
}
|
||||
dataZipPath := filepath.Join(tmpImport, util.CurrentTimeSecondsStr()+".zip")
|
||||
defer os.RemoveAll(dataZipPath)
|
||||
dataZipFile, err := os.Create(dataZipPath)
|
||||
if nil != err {
|
||||
util.LogErrorf("create temp file failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = "create temp file failed"
|
||||
return
|
||||
}
|
||||
file := form.File["file"][0]
|
||||
fileReader, err := file.Open()
|
||||
if nil != err {
|
||||
util.LogErrorf("open upload file failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = "open file failed"
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(dataZipFile, fileReader)
|
||||
if nil != err {
|
||||
util.LogErrorf("read upload file failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = "read file failed"
|
||||
return
|
||||
}
|
||||
if err = dataZipFile.Close(); nil != err {
|
||||
util.LogErrorf("close file failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = "close file failed"
|
||||
return
|
||||
}
|
||||
fileReader.Close()
|
||||
|
||||
err = model.ImportData(dataZipPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func importStdMd(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
localPath := arg["localPath"].(string)
|
||||
toPath := arg["toPath"].(string)
|
||||
err := model.ImportFromLocalPath(notebook, localPath, toPath)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
68
kernel/api/inbox.go
Normal file
68
kernel/api/inbox.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func removeShorthands(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
idsArg := arg["ids"].([]interface{})
|
||||
var ids []string
|
||||
for _, id := range idsArg {
|
||||
ids = append(ids, id.(string))
|
||||
}
|
||||
|
||||
err := model.RemoveCloudShorthands(ids)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getShorthands(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
page := int(arg["page"].(float64))
|
||||
data, err := model.GetCloudShorthands(page)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = data
|
||||
}
|
||||
144
kernel/api/lute.go
Normal file
144
kernel/api/lute.go
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/ast"
|
||||
"github.com/88250/lute/parse"
|
||||
"github.com/88250/lute/render"
|
||||
"github.com/88250/protyle"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func copyStdMarkdown(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
ret.Data = model.CopyStdMarkdown(id)
|
||||
}
|
||||
|
||||
func html2BlockDOM(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
dom := arg["dom"].(string)
|
||||
luteEngine := model.NewLute()
|
||||
markdown, err := luteEngine.HTML2Markdown(dom)
|
||||
if nil != err {
|
||||
ret.Data = "Failed to convert"
|
||||
return
|
||||
}
|
||||
|
||||
var unlinks []*ast.Node
|
||||
tree := parse.Parse("", []byte(markdown), luteEngine.ParseOptions)
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
if ast.NodeListItem == n.Type && nil == n.FirstChild {
|
||||
newNode := protyle.NewParagraph()
|
||||
n.AppendChild(newNode)
|
||||
n.SetIALAttr("updated", util.TimeFromID(newNode.ID))
|
||||
return ast.WalkSkipChildren
|
||||
} else if ast.NodeBlockquote == n.Type && nil == n.FirstChild.Next {
|
||||
unlinks = append(unlinks, n)
|
||||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
for _, n := range unlinks {
|
||||
n.Unlink()
|
||||
}
|
||||
|
||||
if "std" == model.Conf.System.Container {
|
||||
// 处理本地资源文件复制
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering || ast.NodeLinkDest != n.Type {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
if "" == n.TokensStr() {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
localPath := n.TokensStr()
|
||||
if strings.HasPrefix(localPath, "http") {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
localPath = strings.TrimPrefix(localPath, "file://")
|
||||
if gulu.OS.IsWindows() {
|
||||
localPath = strings.TrimPrefix(localPath, "/")
|
||||
}
|
||||
if !gulu.File.IsExist(localPath) {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
name := filepath.Base(localPath)
|
||||
ext := filepath.Ext(name)
|
||||
name = name[0 : len(name)-len(ext)]
|
||||
name = name + "-" + ast.NewNodeID() + ext
|
||||
targetPath := filepath.Join(util.DataDir, "assets", name)
|
||||
if err = gulu.File.CopyFile(localPath, targetPath); nil != err {
|
||||
util.LogErrorf("copy asset from [%s] to [%s] failed: %s", localPath, targetPath, err)
|
||||
return ast.WalkStop
|
||||
}
|
||||
n.Tokens = gulu.Str.ToBytes("assets/" + name)
|
||||
return ast.WalkContinue
|
||||
})
|
||||
}
|
||||
|
||||
renderer := render.NewBlockRenderer(tree, luteEngine.RenderOptions)
|
||||
output := renderer.Render()
|
||||
ret.Data = gulu.Str.FromBytes(output)
|
||||
}
|
||||
|
||||
func spinBlockDOM(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
dom := arg["dom"].(string)
|
||||
luteEngine := model.NewLute()
|
||||
|
||||
dom = luteEngine.SpinBlockDOM(dom)
|
||||
ret.Data = map[string]interface{}{
|
||||
"dom": dom,
|
||||
}
|
||||
}
|
||||
277
kernel/api/notebook.go
Normal file
277
kernel/api/notebook.go
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func setNotebookIcon(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
boxID := arg["notebook"].(string)
|
||||
icon := arg["icon"].(string)
|
||||
model.SetBoxIcon(boxID, icon)
|
||||
}
|
||||
|
||||
func changeSortNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
idsArg := arg["notebooks"].([]interface{})
|
||||
var ids []string
|
||||
for _, p := range idsArg {
|
||||
ids = append(ids, p.(string))
|
||||
}
|
||||
model.ChangeBoxSort(ids)
|
||||
}
|
||||
|
||||
func renameNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
name := arg["name"].(string)
|
||||
err := model.RenameBox(notebook, name)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("renamenotebook", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
"name": name,
|
||||
}
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func removeNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
err := model.RemoveBox(notebook)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("unmount", 0, util.PushModeBroadcast, 0)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": notebook,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func createNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
name := arg["name"].(string)
|
||||
id, err := model.CreateBox(name)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
existed, err := model.Mount(id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"notebook": model.Conf.Box(id),
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("createnotebook", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": model.Conf.Box(id),
|
||||
"existed": existed,
|
||||
}
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func openNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
util.PushMsg(model.Conf.Language(45), 1000*60*15)
|
||||
existed, err := model.Mount(notebook)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
evt := util.NewCmdResult("mount", 0, util.PushModeBroadcast, util.PushModeNone)
|
||||
evt.Data = map[string]interface{}{
|
||||
"box": model.Conf.Box(notebook),
|
||||
"existed": existed,
|
||||
}
|
||||
evt.Callback = arg["callback"]
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
|
||||
func closeNotebook(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
model.Unmount(notebook)
|
||||
}
|
||||
|
||||
func getNotebookConf(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
box := model.Conf.Box(notebook)
|
||||
ret.Data = map[string]interface{}{
|
||||
"box": box.ID,
|
||||
"name": box.Name,
|
||||
"conf": box.GetConf(),
|
||||
}
|
||||
}
|
||||
|
||||
func setNotebookConf(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
notebook := arg["notebook"].(string)
|
||||
box := model.Conf.Box(notebook)
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg["conf"])
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
boxConf := box.GetConf()
|
||||
if err = gulu.JSON.UnmarshalJSON(param, boxConf); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
boxConf.RefCreateSavePath = strings.TrimSpace(boxConf.RefCreateSavePath)
|
||||
if "" != boxConf.RefCreateSavePath {
|
||||
if !strings.HasSuffix(boxConf.RefCreateSavePath, "/") {
|
||||
boxConf.RefCreateSavePath += "/"
|
||||
}
|
||||
}
|
||||
|
||||
boxConf.DailyNoteSavePath = strings.TrimSpace(boxConf.DailyNoteSavePath)
|
||||
if "" != boxConf.DailyNoteSavePath {
|
||||
if !strings.HasPrefix(boxConf.DailyNoteSavePath, "/") {
|
||||
boxConf.DailyNoteSavePath = "/" + boxConf.DailyNoteSavePath
|
||||
}
|
||||
}
|
||||
if "/" == boxConf.DailyNoteSavePath {
|
||||
ret.Code = -1
|
||||
ret.Msg = model.Conf.Language(49)
|
||||
return
|
||||
}
|
||||
|
||||
boxConf.DailyNoteTemplatePath = strings.TrimSpace(boxConf.DailyNoteTemplatePath)
|
||||
if "" != boxConf.DailyNoteTemplatePath {
|
||||
if !strings.HasSuffix(boxConf.DailyNoteTemplatePath, ".md") {
|
||||
boxConf.DailyNoteTemplatePath += ".md"
|
||||
}
|
||||
if !strings.HasPrefix(boxConf.DailyNoteTemplatePath, "/") {
|
||||
boxConf.DailyNoteTemplatePath = "/" + boxConf.DailyNoteTemplatePath
|
||||
}
|
||||
}
|
||||
|
||||
box.SaveConf(boxConf)
|
||||
ret.Data = boxConf
|
||||
}
|
||||
|
||||
func lsNotebooks(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
notebooks, err := model.ListNotebooks()
|
||||
if nil != err {
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"notebooks": notebooks,
|
||||
}
|
||||
}
|
||||
49
kernel/api/outline.go
Normal file
49
kernel/api/outline.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getDocOutline(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if nil == arg["id"] {
|
||||
return
|
||||
}
|
||||
|
||||
rootID := arg["id"].(string)
|
||||
headings, err := model.Outline(rootID)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = headings
|
||||
}
|
||||
96
kernel/api/ref.go
Normal file
96
kernel/api/ref.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func refreshBacklink(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
model.RefreshBacklink(id)
|
||||
}
|
||||
|
||||
func getBacklink(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if nil == arg["id"] {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
keyword := arg["k"].(string)
|
||||
mentionKeyword := arg["mk"].(string)
|
||||
beforeLen := arg["beforeLen"].(float64)
|
||||
boxID, backlinks, backmentions, linkRefsCount, mentionsCount := model.BuildTreeBacklink(id, keyword, mentionKeyword, int(beforeLen))
|
||||
ret.Data = map[string]interface{}{
|
||||
"backlinks": backlinks,
|
||||
"linkRefsCount": linkRefsCount,
|
||||
"backmentions": backmentions,
|
||||
"mentionsCount": mentionsCount,
|
||||
"k": keyword,
|
||||
"mk": mentionKeyword,
|
||||
"box": boxID,
|
||||
}
|
||||
util.RandomSleep(200, 500)
|
||||
}
|
||||
|
||||
func createBacklink(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
defID := arg["defID"].(string)
|
||||
refID := arg["refID"].(string)
|
||||
refText := arg["refText"].(string)
|
||||
isDynamic := arg["isDynamic"].(bool)
|
||||
refRootID, err := model.CreateBacklink(defID, refID, refText, isDynamic)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = map[string]interface{}{
|
||||
"defID": defID,
|
||||
"refID": refID,
|
||||
"refRootID": refRootID,
|
||||
"refText": refText,
|
||||
}
|
||||
}
|
||||
246
kernel/api/router.go
Normal file
246
kernel/api/router.go
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
)
|
||||
|
||||
func ServeAPI(ginServer *gin.Engine) {
|
||||
// 不需要鉴权
|
||||
|
||||
ginServer.Handle("GET", "/api/system/bootProgress", bootProgress)
|
||||
ginServer.Handle("POST", "/api/system/bootProgress", bootProgress)
|
||||
ginServer.Handle("GET", "/api/system/version", version)
|
||||
ginServer.Handle("POST", "/api/system/version", version)
|
||||
ginServer.Handle("POST", "/api/system/currentTime", currentTime)
|
||||
ginServer.Handle("POST", "/api/system/uiproc", addUIProcess)
|
||||
ginServer.Handle("POST", "/api/system/loginAuth", model.LoginAuth)
|
||||
ginServer.Handle("POST", "/api/system/logoutAuth", model.LogoutAuth)
|
||||
|
||||
// 需要鉴权
|
||||
|
||||
ginServer.Handle("POST", "/api/system/getEmojiConf", model.CheckAuth, getEmojiConf)
|
||||
ginServer.Handle("POST", "/api/system/setAccessAuthCode", model.CheckAuth, setAccessAuthCode)
|
||||
ginServer.Handle("POST", "/api/system/setNetworkServe", model.CheckAuth, setNetworkServe)
|
||||
ginServer.Handle("POST", "/api/system/setUploadErrLog", model.CheckAuth, setUploadErrLog)
|
||||
ginServer.Handle("POST", "/api/system/setNetworkProxy", model.CheckAuth, setNetworkProxy)
|
||||
ginServer.Handle("POST", "/api/system/setWorkspaceDir", model.CheckAuth, setWorkspaceDir)
|
||||
ginServer.Handle("POST", "/api/system/listWorkspaceDirs", model.CheckAuth, listWorkspaceDirs)
|
||||
ginServer.Handle("POST", "/api/system/setAppearanceMode", model.CheckAuth, setAppearanceMode)
|
||||
ginServer.Handle("POST", "/api/system/getSysFonts", model.CheckAuth, getSysFonts)
|
||||
ginServer.Handle("POST", "/api/system/setE2EEPasswd", model.CheckAuth, setE2EEPasswd)
|
||||
ginServer.Handle("POST", "/api/system/exit", model.CheckAuth, exit)
|
||||
ginServer.Handle("POST", "/api/system/setUILayout", model.CheckAuth, setUILayout)
|
||||
ginServer.Handle("POST", "/api/system/getConf", model.CheckAuth, getConf)
|
||||
ginServer.Handle("POST", "/api/system/checkUpdate", model.CheckAuth, checkUpdate)
|
||||
|
||||
ginServer.Handle("POST", "/api/account/login", model.CheckAuth, login)
|
||||
ginServer.Handle("POST", "/api/account/checkActivationcode", model.CheckAuth, checkActivationcode)
|
||||
ginServer.Handle("POST", "/api/account/useActivationcode", model.CheckAuth, useActivationcode)
|
||||
ginServer.Handle("POST", "/api/account/deactivate", model.CheckAuth, deactivateUser)
|
||||
|
||||
ginServer.Handle("POST", "/api/notebook/lsNotebooks", model.CheckAuth, lsNotebooks)
|
||||
ginServer.Handle("POST", "/api/notebook/openNotebook", model.CheckAuth, openNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/closeNotebook", model.CheckAuth, closeNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/getNotebookConf", model.CheckAuth, getNotebookConf)
|
||||
ginServer.Handle("POST", "/api/notebook/setNotebookConf", model.CheckAuth, setNotebookConf)
|
||||
ginServer.Handle("POST", "/api/notebook/createNotebook", model.CheckAuth, createNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/removeNotebook", model.CheckAuth, removeNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/renameNotebook", model.CheckAuth, renameNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/changeSortNotebook", model.CheckAuth, changeSortNotebook)
|
||||
ginServer.Handle("POST", "/api/notebook/setNotebookIcon", model.CheckAuth, setNotebookIcon)
|
||||
|
||||
ginServer.Handle("POST", "/api/filetree/searchDocs", model.CheckAuth, searchDocs)
|
||||
ginServer.Handle("POST", "/api/filetree/listDocsByPath", model.CheckAuth, listDocsByPath)
|
||||
ginServer.Handle("POST", "/api/filetree/getDoc", model.CheckAuth, getDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/getDocNameTemplate", model.CheckAuth, getDocNameTemplate)
|
||||
ginServer.Handle("POST", "/api/filetree/changeSort", model.CheckAuth, changeSort)
|
||||
ginServer.Handle("POST", "/api/filetree/lockFile", model.CheckAuth, lockFile)
|
||||
ginServer.Handle("POST", "/api/filetree/createDocWithMd", model.CheckAuth, model.CheckReadonly, createDocWithMd)
|
||||
ginServer.Handle("POST", "/api/filetree/createDailyNote", model.CheckAuth, model.CheckReadonly, createDailyNote)
|
||||
ginServer.Handle("POST", "/api/filetree/createDoc", model.CheckAuth, model.CheckReadonly, createDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/renameDoc", model.CheckAuth, model.CheckReadonly, renameDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/removeDoc", model.CheckAuth, model.CheckReadonly, removeDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/moveDoc", model.CheckAuth, model.CheckReadonly, moveDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckReadonly, duplicateDoc)
|
||||
ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath)
|
||||
ginServer.Handle("POST", "/api/filetree/getHPathByID", model.CheckAuth, getHPathByID)
|
||||
ginServer.Handle("POST", "/api/filetree/getFullHPathByID", model.CheckAuth, getFullHPathByID)
|
||||
ginServer.Handle("POST", "/api/filetree/doc2Heading", model.CheckAuth, model.CheckReadonly, doc2Heading)
|
||||
ginServer.Handle("POST", "/api/filetree/heading2Doc", model.CheckAuth, model.CheckReadonly, heading2Doc)
|
||||
ginServer.Handle("POST", "/api/filetree/li2Doc", model.CheckAuth, model.CheckReadonly, li2Doc)
|
||||
ginServer.Handle("POST", "/api/filetree/refreshFiletree", model.CheckAuth, model.CheckReadonly, refreshFiletree)
|
||||
|
||||
ginServer.Handle("POST", "/api/format/autoSpace", model.CheckAuth, model.CheckReadonly, autoSpace)
|
||||
ginServer.Handle("POST", "/api/format/netImg2LocalAssets", model.CheckAuth, model.CheckReadonly, netImg2LocalAssets)
|
||||
|
||||
ginServer.Handle("POST", "/api/history/getNotebookHistory", model.CheckAuth, getNotebookHistory)
|
||||
ginServer.Handle("POST", "/api/history/rollbackNotebookHistory", model.CheckAuth, rollbackNotebookHistory)
|
||||
ginServer.Handle("POST", "/api/history/getAssetsHistory", model.CheckAuth, getAssetsHistory)
|
||||
ginServer.Handle("POST", "/api/history/rollbackAssetsHistory", model.CheckAuth, rollbackAssetsHistory)
|
||||
ginServer.Handle("POST", "/api/history/getDocHistory", model.CheckAuth, getDocHistory)
|
||||
ginServer.Handle("POST", "/api/history/getDocHistoryContent", model.CheckAuth, getDocHistoryContent)
|
||||
ginServer.Handle("POST", "/api/history/rollbackDocHistory", model.CheckAuth, model.CheckReadonly, rollbackDocHistory)
|
||||
ginServer.Handle("POST", "/api/history/clearWorkspaceHistory", model.CheckAuth, model.CheckReadonly, clearWorkspaceHistory)
|
||||
|
||||
ginServer.Handle("POST", "/api/outline/getDocOutline", model.CheckAuth, getDocOutline)
|
||||
ginServer.Handle("POST", "/api/bookmark/getBookmark", model.CheckAuth, getBookmark)
|
||||
ginServer.Handle("POST", "/api/bookmark/renameBookmark", model.CheckAuth, renameBookmark)
|
||||
ginServer.Handle("POST", "/api/tag/getTag", model.CheckAuth, getTag)
|
||||
ginServer.Handle("POST", "/api/tag/renameTag", model.CheckAuth, renameTag)
|
||||
ginServer.Handle("POST", "/api/tag/removeTag", model.CheckAuth, removeTag)
|
||||
|
||||
ginServer.Handle("POST", "/api/lute/spinBlockDOM", model.CheckAuth, spinBlockDOM) // 未测试
|
||||
ginServer.Handle("POST", "/api/lute/html2BlockDOM", model.CheckAuth, html2BlockDOM)
|
||||
ginServer.Handle("POST", "/api/lute/copyStdMarkdown", model.CheckAuth, copyStdMarkdown)
|
||||
|
||||
ginServer.Handle("POST", "/api/query/sql", model.CheckAuth, SQL)
|
||||
|
||||
ginServer.Handle("POST", "/api/search/searchTag", model.CheckAuth, searchTag)
|
||||
ginServer.Handle("POST", "/api/search/searchTemplate", model.CheckAuth, searchTemplate)
|
||||
ginServer.Handle("POST", "/api/search/searchWidget", model.CheckAuth, searchWidget)
|
||||
ginServer.Handle("POST", "/api/search/searchRefBlock", model.CheckAuth, searchRefBlock)
|
||||
ginServer.Handle("POST", "/api/search/searchEmbedBlock", model.CheckAuth, searchEmbedBlock)
|
||||
ginServer.Handle("POST", "/api/search/fullTextSearchBlock", model.CheckAuth, fullTextSearchBlock)
|
||||
ginServer.Handle("POST", "/api/search/searchAsset", model.CheckAuth, searchAsset)
|
||||
ginServer.Handle("POST", "/api/search/findReplace", model.CheckAuth, findReplace)
|
||||
|
||||
ginServer.Handle("POST", "/api/block/getBlockInfo", model.CheckAuth, getBlockInfo)
|
||||
ginServer.Handle("POST", "/api/block/getBlockDOM", model.CheckAuth, getBlockDOM)
|
||||
ginServer.Handle("POST", "/api/block/getBlockBreadcrumb", model.CheckAuth, getBlockBreadcrumb)
|
||||
ginServer.Handle("POST", "/api/block/getRefIDs", model.CheckAuth, getRefIDs)
|
||||
ginServer.Handle("POST", "/api/block/getRefIDsByFileAnnotationID", model.CheckAuth, getRefIDsByFileAnnotationID)
|
||||
ginServer.Handle("POST", "/api/block/getBlockDefIDsByRefText", model.CheckAuth, getBlockDefIDsByRefText)
|
||||
ginServer.Handle("POST", "/api/block/getRefText", model.CheckAuth, getRefText)
|
||||
ginServer.Handle("POST", "/api/block/getBlockWordCount", model.CheckAuth, getBlockWordCount)
|
||||
ginServer.Handle("POST", "/api/block/getRecentUpdatedBlocks", model.CheckAuth, getRecentUpdatedBlocks)
|
||||
ginServer.Handle("POST", "/api/block/getDocInfo", model.CheckAuth, getDocInfo)
|
||||
ginServer.Handle("POST", "/api/block/checkBlockExist", model.CheckAuth, checkBlockExist)
|
||||
ginServer.Handle("POST", "/api/block/checkBlockFold", model.CheckAuth, checkBlockFold)
|
||||
ginServer.Handle("POST", "/api/block/insertBlock", model.CheckAuth, insertBlock)
|
||||
ginServer.Handle("POST", "/api/block/prependBlock", model.CheckAuth, prependBlock)
|
||||
ginServer.Handle("POST", "/api/block/appendBlock", model.CheckAuth, appendBlock)
|
||||
ginServer.Handle("POST", "/api/block/updateBlock", model.CheckAuth, updateBlock)
|
||||
ginServer.Handle("POST", "/api/block/deleteBlock", model.CheckAuth, deleteBlock)
|
||||
ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, setBlockReminder)
|
||||
|
||||
ginServer.Handle("POST", "/api/file/getFile", model.CheckAuth, getFile)
|
||||
ginServer.Handle("POST", "/api/file/putFile", model.CheckAuth, putFile)
|
||||
|
||||
ginServer.Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink)
|
||||
ginServer.Handle("POST", "/api/ref/getBacklink", model.CheckAuth, getBacklink)
|
||||
ginServer.Handle("POST", "/api/ref/createBacklink", model.CheckAuth, model.CheckReadonly, createBacklink)
|
||||
|
||||
ginServer.Handle("POST", "/api/attr/getBookmarkLabels", model.CheckAuth, getBookmarkLabels)
|
||||
ginServer.Handle("POST", "/api/attr/resetBlockAttrs", model.CheckAuth, model.CheckReadonly, resetBlockAttrs)
|
||||
ginServer.Handle("POST", "/api/attr/setBlockAttrs", model.CheckAuth, model.CheckReadonly, setBlockAttrs)
|
||||
ginServer.Handle("POST", "/api/attr/getBlockAttrs", model.CheckAuth, getBlockAttrs)
|
||||
|
||||
ginServer.Handle("POST", "/api/cloud/getCloudSpace", model.CheckAuth, getCloudSpace)
|
||||
|
||||
ginServer.Handle("POST", "/api/backup/getLocalBackup", model.CheckAuth, getLocalBackup)
|
||||
ginServer.Handle("POST", "/api/backup/createLocalBackup", model.CheckAuth, model.CheckReadonly, createLocalBackup)
|
||||
ginServer.Handle("POST", "/api/backup/recoverLocalBackup", model.CheckAuth, model.CheckReadonly, recoverLocalBackup)
|
||||
ginServer.Handle("POST", "/api/backup/uploadLocalBackup", model.CheckAuth, model.CheckReadonly, uploadLocalBackup)
|
||||
ginServer.Handle("POST", "/api/backup/downloadCloudBackup", model.CheckAuth, model.CheckReadonly, downloadCloudBackup)
|
||||
ginServer.Handle("POST", "/api/backup/removeCloudBackup", model.CheckAuth, model.CheckReadonly, removeCloudBackup)
|
||||
|
||||
ginServer.Handle("POST", "/api/sync/setSyncEnable", model.CheckAuth, setSyncEnable)
|
||||
ginServer.Handle("POST", "/api/sync/setCloudSyncDir", model.CheckAuth, setCloudSyncDir)
|
||||
ginServer.Handle("POST", "/api/sync/createCloudSyncDir", model.CheckAuth, model.CheckReadonly, createCloudSyncDir)
|
||||
ginServer.Handle("POST", "/api/sync/removeCloudSyncDir", model.CheckAuth, model.CheckReadonly, removeCloudSyncDir)
|
||||
ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, listCloudSyncDir)
|
||||
ginServer.Handle("POST", "/api/sync/performSync", model.CheckAuth, performSync)
|
||||
ginServer.Handle("POST", "/api/sync/performBootSync", model.CheckAuth, performBootSync)
|
||||
ginServer.Handle("POST", "/api/sync/getBootSync", model.CheckAuth, getBootSync)
|
||||
ginServer.Handle("POST", "/api/sync/getSyncDirection", model.CheckAuth, getSyncDirection)
|
||||
|
||||
ginServer.Handle("POST", "/api/inbox/getShorthands", model.CheckAuth, getShorthands)
|
||||
ginServer.Handle("POST", "/api/inbox/removeShorthands", model.CheckAuth, removeShorthands)
|
||||
|
||||
ginServer.Handle("POST", "/api/extension/copy", model.CheckAuth, extensionCopy)
|
||||
|
||||
ginServer.Handle("POST", "/api/clipboard/readFilePaths", model.CheckAuth, readFilePaths)
|
||||
|
||||
ginServer.Handle("POST", "/api/asset/uploadCloud", model.CheckAuth, model.CheckReadonly, uploadCloud)
|
||||
ginServer.Handle("POST", "/api/asset/insertLocalAssets", model.CheckAuth, model.CheckReadonly, insertLocalAssets)
|
||||
ginServer.Handle("POST", "/api/asset/resolveAssetPath", model.CheckAuth, resolveAssetPath)
|
||||
ginServer.Handle("POST", "/api/asset/upload", model.CheckAuth, model.CheckReadonly, model.Upload)
|
||||
ginServer.Handle("POST", "/api/asset/setFileAnnotation", model.CheckAuth, model.CheckReadonly, setFileAnnotation)
|
||||
ginServer.Handle("POST", "/api/asset/getFileAnnotation", model.CheckAuth, getFileAnnotation)
|
||||
ginServer.Handle("POST", "/api/asset/getUnusedAssets", model.CheckAuth, getUnusedAssets)
|
||||
ginServer.Handle("POST", "/api/asset/removeUnusedAsset", model.CheckAuth, model.CheckReadonly, removeUnusedAsset)
|
||||
ginServer.Handle("POST", "/api/asset/removeUnusedAssets", model.CheckAuth, model.CheckReadonly, removeUnusedAssets)
|
||||
ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, model.CheckReadonly, getDocImageAssets)
|
||||
|
||||
ginServer.Handle("POST", "/api/export/batchExportMd", model.CheckAuth, batchExportMd)
|
||||
ginServer.Handle("POST", "/api/export/exportMd", model.CheckAuth, exportMd)
|
||||
ginServer.Handle("POST", "/api/export/exportSY", model.CheckAuth, exportSY)
|
||||
ginServer.Handle("POST", "/api/export/exportMdContent", model.CheckAuth, exportMdContent)
|
||||
ginServer.Handle("POST", "/api/export/exportHTML", model.CheckAuth, exportHTML)
|
||||
ginServer.Handle("POST", "/api/export/exportMdHTML", model.CheckAuth, exportMdHTML)
|
||||
ginServer.Handle("POST", "/api/export/exportDocx", model.CheckAuth, exportDocx)
|
||||
ginServer.Handle("POST", "/api/export/addPDFOutline", model.CheckAuth, addPDFOutline)
|
||||
ginServer.Handle("POST", "/api/export/preview", model.CheckAuth, exportPreview)
|
||||
ginServer.Handle("POST", "/api/export/exportData", model.CheckAuth, exportData)
|
||||
ginServer.Handle("POST", "/api/export/exportDataInFolder", model.CheckAuth, exportDataInFolder)
|
||||
|
||||
ginServer.Handle("POST", "/api/import/importStdMd", model.CheckAuth, model.CheckReadonly, importStdMd)
|
||||
ginServer.Handle("POST", "/api/import/importData", model.CheckAuth, model.CheckReadonly, importData)
|
||||
ginServer.Handle("POST", "/api/import/importSY", model.CheckAuth, model.CheckReadonly, importSY)
|
||||
|
||||
ginServer.Handle("POST", "/api/template/render", model.CheckAuth, renderTemplate)
|
||||
ginServer.Handle("POST", "/api/template/docSaveAsTemplate", model.CheckAuth, docSaveAsTemplate)
|
||||
|
||||
ginServer.Handle("POST", "/api/transactions", model.CheckAuth, model.CheckReadonly, performTransactions)
|
||||
|
||||
ginServer.Handle("POST", "/api/setting/setAccount", model.CheckAuth, setAccount)
|
||||
ginServer.Handle("POST", "/api/setting/setEditor", model.CheckAuth, setEditor)
|
||||
ginServer.Handle("POST", "/api/setting/setExport", model.CheckAuth, setExport)
|
||||
ginServer.Handle("POST", "/api/setting/setFiletree", model.CheckAuth, setFiletree)
|
||||
ginServer.Handle("POST", "/api/setting/setSearch", model.CheckAuth, setSearch)
|
||||
ginServer.Handle("POST", "/api/setting/setKeymap", model.CheckAuth, setKeymap)
|
||||
ginServer.Handle("POST", "/api/setting/setAppearance", model.CheckAuth, setAppearance)
|
||||
ginServer.Handle("POST", "/api/setting/getCloudUser", model.CheckAuth, getCloudUser)
|
||||
ginServer.Handle("POST", "/api/setting/logoutCloudUser", model.CheckAuth, logoutCloudUser)
|
||||
ginServer.Handle("POST", "/api/setting/login2faCloudUser", model.CheckAuth, login2faCloudUser)
|
||||
ginServer.Handle("POST", "/api/setting/getCustomCSS", model.CheckAuth, getCustomCSS)
|
||||
ginServer.Handle("POST", "/api/setting/setCustomCSS", model.CheckAuth, setCustomCSS)
|
||||
ginServer.Handle("POST", "/api/setting/setEmoji", model.CheckAuth, setEmoji)
|
||||
ginServer.Handle("POST", "/api/setting/setSearchCaseSensitive", model.CheckAuth, setSearchCaseSensitive)
|
||||
|
||||
ginServer.Handle("POST", "/api/graph/resetGraph", model.CheckAuth, resetGraph)
|
||||
ginServer.Handle("POST", "/api/graph/resetLocalGraph", model.CheckAuth, resetLocalGraph)
|
||||
ginServer.Handle("POST", "/api/graph/getGraph", model.CheckAuth, getGraph)
|
||||
ginServer.Handle("POST", "/api/graph/getLocalGraph", model.CheckAuth, getLocalGraph)
|
||||
|
||||
ginServer.Handle("POST", "/api/bazaar/getBazaarWidget", model.CheckAuth, getBazaarWidget)
|
||||
ginServer.Handle("POST", "/api/bazaar/installBazaarWidget", model.CheckAuth, installBazaarWidget)
|
||||
ginServer.Handle("POST", "/api/bazaar/uninstallBazaarWidget", model.CheckAuth, uninstallBazaarWidget)
|
||||
ginServer.Handle("POST", "/api/bazaar/getBazaarIcon", model.CheckAuth, getBazaarIcon)
|
||||
ginServer.Handle("POST", "/api/bazaar/installBazaarIcon", model.CheckAuth, installBazaarIcon)
|
||||
ginServer.Handle("POST", "/api/bazaar/uninstallBazaarIcon", model.CheckAuth, uninstallBazaarIcon)
|
||||
ginServer.Handle("POST", "/api/bazaar/getBazaarTemplate", model.CheckAuth, getBazaarTemplate)
|
||||
ginServer.Handle("POST", "/api/bazaar/installBazaarTemplate", model.CheckAuth, installBazaarTemplate)
|
||||
ginServer.Handle("POST", "/api/bazaar/uninstallBazaarTemplate", model.CheckAuth, uninstallBazaarTemplate)
|
||||
ginServer.Handle("POST", "/api/bazaar/getBazaarTheme", model.CheckAuth, getBazaarTheme)
|
||||
ginServer.Handle("POST", "/api/bazaar/installBazaarTheme", model.CheckAuth, installBazaarTheme)
|
||||
ginServer.Handle("POST", "/api/bazaar/uninstallBazaarTheme", model.CheckAuth, uninstallBazaarTheme)
|
||||
ginServer.Handle("POST", "/api/bazaar/getBazaarPackageREAME", model.CheckAuth, getBazaarPackageREAME)
|
||||
}
|
||||
205
kernel/api/search.go
Normal file
205
kernel/api/search.go
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/html"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func findReplace(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
k := arg["k"].(string)
|
||||
r := arg["r"].(string)
|
||||
idsArg := arg["ids"].([]interface{})
|
||||
var ids []string
|
||||
for _, id := range idsArg {
|
||||
ids = append(ids, id.(string))
|
||||
}
|
||||
err := model.FindReplace(k, r, ids)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 3000}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func searchAsset(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
k := arg["k"].(string)
|
||||
ret.Data = model.SearchAssetsByName(k)
|
||||
return
|
||||
}
|
||||
|
||||
func searchTag(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
k := arg["k"].(string)
|
||||
tags := model.SearchTags(k)
|
||||
ret.Data = map[string]interface{}{
|
||||
"tags": tags,
|
||||
"k": k,
|
||||
}
|
||||
}
|
||||
|
||||
func searchWidget(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
keyword := arg["k"].(string)
|
||||
blocks := model.SearchWidget(keyword)
|
||||
ret.Data = map[string]interface{}{
|
||||
"blocks": blocks,
|
||||
"k": keyword,
|
||||
}
|
||||
}
|
||||
|
||||
func searchTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
keyword := arg["k"].(string)
|
||||
blocks := model.SearchTemplate(keyword)
|
||||
ret.Data = map[string]interface{}{
|
||||
"blocks": blocks,
|
||||
"k": keyword,
|
||||
}
|
||||
}
|
||||
|
||||
func searchEmbedBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
stmt := arg["stmt"].(string)
|
||||
excludeIDsArg := arg["excludeIDs"].([]interface{})
|
||||
var excludeIDs []string
|
||||
for _, excludeID := range excludeIDsArg {
|
||||
excludeIDs = append(excludeIDs, excludeID.(string))
|
||||
}
|
||||
headingMode := 0 // 0:带标题下方块
|
||||
headingModeArg := arg["headingMode"]
|
||||
if nil != headingModeArg {
|
||||
headingMode = int(headingModeArg.(float64))
|
||||
}
|
||||
blocks := model.SearchEmbedBlock(stmt, excludeIDs, headingMode)
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"blocks": blocks,
|
||||
}
|
||||
}
|
||||
|
||||
func searchRefBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
rootID := arg["rootID"].(string)
|
||||
id := arg["id"].(string)
|
||||
keyword := arg["k"].(string)
|
||||
beforeLen := int(arg["beforeLen"].(float64))
|
||||
blocks, newDoc := model.SearchRefBlock(id, rootID, keyword, beforeLen)
|
||||
ret.Data = map[string]interface{}{
|
||||
"blocks": blocks,
|
||||
"newDoc": newDoc,
|
||||
"k": html.EscapeHTMLStr(keyword),
|
||||
"reqId": arg["reqId"],
|
||||
}
|
||||
}
|
||||
|
||||
func fullTextSearchBlock(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
query := arg["query"].(string)
|
||||
pathArg := arg["path"]
|
||||
var path string
|
||||
if nil != pathArg {
|
||||
path = pathArg.(string)
|
||||
}
|
||||
var box string
|
||||
if "" != path {
|
||||
box = strings.Split(path, "/")[0]
|
||||
path = strings.TrimPrefix(path, box)
|
||||
}
|
||||
var types map[string]bool
|
||||
if nil != arg["types"] {
|
||||
typesArg := arg["types"].(map[string]interface{})
|
||||
types = map[string]bool{}
|
||||
for t, b := range typesArg {
|
||||
types[t] = b.(bool)
|
||||
}
|
||||
}
|
||||
querySyntaxArg := arg["querySyntax"]
|
||||
var querySyntax bool
|
||||
if nil != querySyntaxArg {
|
||||
querySyntax = querySyntaxArg.(bool)
|
||||
}
|
||||
blocks := model.FullTextSearchBlock(query, box, path, types, querySyntax)
|
||||
ret.Data = blocks
|
||||
}
|
||||
384
kernel/api/setting.go
Normal file
384
kernel/api/setting.go
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/conf"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/sql"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func setAccount(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
account := &conf.Account{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, account); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.Account = account
|
||||
model.Conf.Save()
|
||||
|
||||
ret.Data = model.Conf.Account
|
||||
}
|
||||
|
||||
func setEditor(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
oldGenerateHistoryInterval := model.Conf.Editor.GenerateHistoryInterval
|
||||
|
||||
editor := conf.NewEditor()
|
||||
if err = gulu.JSON.UnmarshalJSON(param, editor); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if "" == editor.PlantUMLServePath {
|
||||
editor.PlantUMLServePath = "https://www.plantuml.com/plantuml/svg/~1"
|
||||
}
|
||||
|
||||
model.Conf.Editor = editor
|
||||
model.Conf.Save()
|
||||
|
||||
if oldGenerateHistoryInterval != model.Conf.Editor.GenerateHistoryInterval {
|
||||
model.ChangeHistoryTick(editor.GenerateHistoryInterval)
|
||||
}
|
||||
|
||||
ret.Data = model.Conf.Editor
|
||||
}
|
||||
|
||||
func setExport(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
export := &conf.Export{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, export); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
if "" != export.PandocBin {
|
||||
if !util.IsValidPandocBin(export.PandocBin) {
|
||||
ret.Code = -1
|
||||
ret.Msg = fmt.Sprintf(model.Conf.Language(117), export.PandocBin)
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
model.Conf.Export = export
|
||||
model.Conf.Save()
|
||||
|
||||
ret.Data = model.Conf.Export
|
||||
}
|
||||
|
||||
func setFiletree(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
fileTree := &conf.FileTree{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, fileTree); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
fileTree.RefCreateSavePath = strings.TrimSpace(fileTree.RefCreateSavePath)
|
||||
if "" != fileTree.RefCreateSavePath {
|
||||
if !strings.HasSuffix(fileTree.RefCreateSavePath, "/") {
|
||||
fileTree.RefCreateSavePath += "/"
|
||||
}
|
||||
}
|
||||
|
||||
model.Conf.FileTree = fileTree
|
||||
model.Conf.Save()
|
||||
|
||||
ret.Data = model.Conf.FileTree
|
||||
}
|
||||
|
||||
func setSearch(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
s := &conf.Search{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, s); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
if 1 > s.Limit {
|
||||
s.Limit = 32
|
||||
}
|
||||
|
||||
model.Conf.Search = s
|
||||
model.Conf.Save()
|
||||
sql.SetCaseSensitive(s.CaseSensitive)
|
||||
sql.ClearVirtualRefKeywords()
|
||||
ret.Data = s
|
||||
}
|
||||
|
||||
func setKeymap(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg["data"])
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
keymap := &conf.Keymap{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, keymap); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.Keymap = keymap
|
||||
model.Conf.Save()
|
||||
}
|
||||
|
||||
func setAppearance(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
appearance := &conf.Appearance{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, appearance); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.Appearance = appearance
|
||||
model.Conf.Lang = appearance.Lang
|
||||
model.Conf.Save()
|
||||
model.InitAppearance()
|
||||
|
||||
ret.Data = model.Conf.Appearance
|
||||
}
|
||||
|
||||
func getCloudUser(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
t := arg["token"]
|
||||
var token string
|
||||
if nil != t {
|
||||
token = t.(string)
|
||||
}
|
||||
if err := model.RefreshUser(token); nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = model.Conf.User
|
||||
}
|
||||
|
||||
func logoutCloudUser(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
model.LogoutUser()
|
||||
}
|
||||
|
||||
func login2faCloudUser(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
token := arg["token"].(string)
|
||||
code := arg["code"].(string)
|
||||
data, err := model.Login2fa(token, code)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = data
|
||||
}
|
||||
|
||||
func getCustomCSS(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
themeName := arg["theme"].(string)
|
||||
customCSS, err := model.ReadCustomCSS(themeName)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
ret.Data = customCSS
|
||||
}
|
||||
|
||||
func setCustomCSS(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
themeName := arg["theme"].(string)
|
||||
css := arg["css"].(map[string]interface{})
|
||||
if err := model.WriteCustomCSS(themeName, css); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEmoji(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
argEmoji := arg["emoji"].([]interface{})
|
||||
var emoji []string
|
||||
for _, ae := range argEmoji {
|
||||
emoji = append(emoji, ae.(string))
|
||||
}
|
||||
|
||||
model.Conf.Editor.Emoji = emoji
|
||||
}
|
||||
|
||||
func setSearchCaseSensitive(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
caseSensitive := arg["caseSensitive"].(bool)
|
||||
model.Conf.Search.CaseSensitive = caseSensitive
|
||||
model.Conf.Save()
|
||||
sql.SetCaseSensitive(caseSensitive)
|
||||
}
|
||||
46
kernel/api/sql.go
Normal file
46
kernel/api/sql.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/sql"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func SQL(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
stmt := arg["stmt"].(string)
|
||||
result, err := sql.Query(stmt)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = result
|
||||
}
|
||||
154
kernel/api/sync.go
Normal file
154
kernel/api/sync.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getSyncDirection(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cloudDirName := arg["name"].(string)
|
||||
ret.Code, ret.Msg = model.GetSyncDirection(cloudDirName)
|
||||
}
|
||||
|
||||
func getBootSync(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
if 1 == model.BootSyncSucc {
|
||||
ret.Code = 1
|
||||
ret.Msg = model.Conf.Language(17)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func performSync(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
model.SyncData(false, false, true)
|
||||
}
|
||||
|
||||
func performBootSync(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
model.SyncData(true, false, true)
|
||||
ret.Code = model.BootSyncSucc
|
||||
}
|
||||
|
||||
func listCloudSyncDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
syncDirs, hSize, err := model.ListCloudSyncDir()
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"syncDirs": syncDirs,
|
||||
"hSize": hSize,
|
||||
"checkedSyncDir": model.Conf.Sync.CloudName,
|
||||
}
|
||||
}
|
||||
|
||||
func removeCloudSyncDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
name := arg["name"].(string)
|
||||
err := model.RemoveCloudSyncDir(name)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = model.Conf.Sync.CloudName
|
||||
}
|
||||
|
||||
func createCloudSyncDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
name := arg["name"].(string)
|
||||
err := model.CreateCloudSyncDir(name)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setSyncEnable(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
enabled := arg["enabled"].(bool)
|
||||
err := model.SetSyncEnable(enabled)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setCloudSyncDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
name := arg["name"].(string)
|
||||
model.SetCloudSyncDir(name)
|
||||
}
|
||||
397
kernel/api/system.go
Normal file
397
kernel/api/system.go
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/conf"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getEmojiConf(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
builtConfPath := filepath.Join(util.AppearancePath, "emojis", "conf.json")
|
||||
data, err := os.ReadFile(builtConfPath)
|
||||
if nil != err {
|
||||
util.LogErrorf("read emojis conf.json failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
var conf []map[string]interface{}
|
||||
if err = gulu.JSON.UnmarshalJSON(data, &conf); nil != err {
|
||||
util.LogErrorf("unmarshal emojis conf.json failed: %s", err)
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
customConfDir := filepath.Join(util.DataDir, "emojis")
|
||||
custom := map[string]interface{}{
|
||||
"id": "custom",
|
||||
"title": "Custom",
|
||||
"title_zh_cn": "自定义",
|
||||
}
|
||||
items := []map[string]interface{}{}
|
||||
custom["items"] = items
|
||||
if gulu.File.IsDir(customConfDir) {
|
||||
model.CustomEmojis = sync.Map{}
|
||||
customEmojis, err := os.ReadDir(customConfDir)
|
||||
if nil != err {
|
||||
util.LogErrorf("read custom emojis failed: %s", err)
|
||||
} else {
|
||||
for _, customEmoji := range customEmojis {
|
||||
name := customEmoji.Name()
|
||||
if strings.HasPrefix(name, ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
if customEmoji.IsDir() {
|
||||
// 子级
|
||||
subCustomEmojis, err := os.ReadDir(filepath.Join(customConfDir, name))
|
||||
if nil != err {
|
||||
util.LogErrorf("read custom emojis failed: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subCustomEmoji := range subCustomEmojis {
|
||||
name = subCustomEmoji.Name()
|
||||
if strings.HasPrefix(name, ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
addCustomEmoji(customEmoji.Name()+"/"+name, &items)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
addCustomEmoji(name, &items)
|
||||
}
|
||||
}
|
||||
}
|
||||
custom["items"] = items
|
||||
conf = append([]map[string]interface{}{custom}, conf...)
|
||||
|
||||
ret.Data = conf
|
||||
return
|
||||
}
|
||||
|
||||
func addCustomEmoji(name string, items *[]map[string]interface{}) {
|
||||
ext := filepath.Ext(name)
|
||||
nameWithoutExt := strings.TrimSuffix(name, ext)
|
||||
emoji := map[string]interface{}{
|
||||
"unicode": name,
|
||||
"description": nameWithoutExt,
|
||||
"description_zh_cn": nameWithoutExt,
|
||||
"keywords": nameWithoutExt,
|
||||
}
|
||||
*items = append(*items, emoji)
|
||||
|
||||
imgSrc := "/emojis/" + name
|
||||
model.CustomEmojis.Store(nameWithoutExt, imgSrc)
|
||||
}
|
||||
|
||||
func checkUpdate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
showMsg := arg["showMsg"].(bool)
|
||||
model.CheckUpdate(showMsg)
|
||||
}
|
||||
|
||||
func getConf(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = model.Conf
|
||||
}
|
||||
|
||||
func setUILayout(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
param, err := gulu.JSON.MarshalJSON(arg["layout"])
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
uiLayout := &conf.UILayout{}
|
||||
if err = gulu.JSON.UnmarshalJSON(param, uiLayout); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
model.Conf.UILayout = uiLayout
|
||||
model.Conf.Save()
|
||||
}
|
||||
|
||||
func setAccessAuthCode(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
aac := arg["accessAuthCode"].(string)
|
||||
model.Conf.AccessAuthCode = aac
|
||||
model.Conf.Save()
|
||||
|
||||
session := util.GetSession(c)
|
||||
session.AccessAuthCode = aac
|
||||
session.Save(c)
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
util.ReloadUI()
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func getSysFonts(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
ret.Data = util.GetSysFonts(model.Conf.Lang)
|
||||
}
|
||||
|
||||
func version(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = util.Ver
|
||||
}
|
||||
|
||||
func currentTime(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
ret.Data = util.CurrentTimeMillis()
|
||||
}
|
||||
|
||||
func bootProgress(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
progress, details := util.GetBootProgressDetails()
|
||||
ret.Data = map[string]interface{}{"progress": progress, "details": details}
|
||||
}
|
||||
|
||||
func setAppearanceMode(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
mode := int(arg["mode"].(float64))
|
||||
model.Conf.Appearance.Mode = mode
|
||||
if 0 == mode {
|
||||
model.Conf.Appearance.ThemeJS = gulu.File.IsExist(filepath.Join(util.ThemesPath, model.Conf.Appearance.ThemeLight, "theme.js"))
|
||||
} else {
|
||||
model.Conf.Appearance.ThemeJS = gulu.File.IsExist(filepath.Join(util.ThemesPath, model.Conf.Appearance.ThemeDark, "theme.js"))
|
||||
}
|
||||
model.Conf.Save()
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"appearance": model.Conf.Appearance,
|
||||
}
|
||||
}
|
||||
|
||||
func setNetworkServe(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
networkServe := arg["networkServe"].(bool)
|
||||
model.Conf.System.NetworkServe = networkServe
|
||||
model.Conf.Save()
|
||||
|
||||
util.PushMsg(model.Conf.Language(42), 1000*15)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func setUploadErrLog(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
uploadErrLog := arg["uploadErrLog"].(bool)
|
||||
model.Conf.System.UploadErrLog = uploadErrLog
|
||||
model.Conf.Save()
|
||||
|
||||
util.PushMsg(model.Conf.Language(42), 1000*15)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func setNetworkProxy(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
scheme := arg["scheme"].(string)
|
||||
host := arg["host"].(string)
|
||||
port := arg["port"].(string)
|
||||
model.Conf.System.NetworkProxy = &conf.NetworkProxy{
|
||||
Scheme: scheme,
|
||||
Host: host,
|
||||
Port: port,
|
||||
}
|
||||
model.Conf.Save()
|
||||
|
||||
util.PushMsg(model.Conf.Language(42), 1000*15)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func setE2EEPasswd(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var passwd string
|
||||
mode := int(arg["mode"].(float64))
|
||||
if 0 == mode { // 使用内建的密码生成
|
||||
passwd = model.GetBuiltInE2EEPasswd()
|
||||
} else { // 使用自定义密码
|
||||
passwd = arg["e2eePasswd"].(string)
|
||||
passwd = strings.TrimSpace(passwd)
|
||||
}
|
||||
|
||||
if "" == passwd {
|
||||
ret.Code = -1
|
||||
ret.Msg = model.Conf.Language(39)
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
|
||||
newPasswd := util.AESEncrypt(passwd)
|
||||
if model.Conf.E2EEPasswd == newPasswd {
|
||||
util.PushMsg(model.Conf.Language(92), 3000)
|
||||
return
|
||||
}
|
||||
|
||||
util.PushMsg(model.Conf.Language(102), 60*1000)
|
||||
if err := os.RemoveAll(model.Conf.Backup.GetSaveDir()); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(model.Conf.Backup.GetSaveDir(), 0755); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := os.RemoveAll(model.Conf.Sync.GetSaveDir()); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(model.Conf.Sync.GetSaveDir(), 0755); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(util.WorkspaceDir, "incremental")); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(util.WorkspaceDir, "incremental"), 0755); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
return
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
model.Conf.E2EEPasswd = newPasswd
|
||||
model.Conf.E2EEPasswdMode = mode
|
||||
model.Conf.Save()
|
||||
util.PushMsg(model.Conf.Language(92), 3000)
|
||||
time.Sleep(1 * time.Second)
|
||||
model.SyncData(false, false, true)
|
||||
|
||||
}
|
||||
|
||||
func addUIProcess(c *gin.Context) {
|
||||
pid := c.Query("pid")
|
||||
util.UIProcessIDs.Store(pid, true)
|
||||
}
|
||||
|
||||
func exit(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
forceArg := arg["force"]
|
||||
var force bool
|
||||
if nil != forceArg {
|
||||
force = forceArg.(bool)
|
||||
}
|
||||
|
||||
err := model.Close(force)
|
||||
if nil != err {
|
||||
ret.Code = 1
|
||||
ret.Msg = err.Error() + "<div class=\"fn__space\"></div><button class=\"b3-button b3-button--white\">" + model.Conf.Language(97) + "</button>"
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 0}
|
||||
return
|
||||
}
|
||||
}
|
||||
84
kernel/api/tag.go
Normal file
84
kernel/api/tag.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func getTag(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
sortParam := arg["sort"]
|
||||
sortMode := model.Conf.Tag.Sort
|
||||
if nil != sortParam {
|
||||
sortMode = int(sortParam.(float64))
|
||||
}
|
||||
|
||||
model.Conf.Tag.Sort = sortMode
|
||||
model.Conf.Save()
|
||||
|
||||
ret.Data = model.BuildTags()
|
||||
}
|
||||
|
||||
func renameTag(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
oldLabel := arg["oldLabel"].(string)
|
||||
newLabel := arg["newLabel"].(string)
|
||||
if err := model.RenameTag(oldLabel, newLabel); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func removeTag(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
label := arg["label"].(string)
|
||||
if err := model.RemoveTag(label); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = err.Error()
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
71
kernel/api/template.go
Normal file
71
kernel/api/template.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute/html"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func docSaveAsTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := arg["id"].(string)
|
||||
overwrite := arg["overwrite"].(bool)
|
||||
code, err := model.DocSaveAsTemplate(id, overwrite)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = html.EscapeString(err.Error())
|
||||
return
|
||||
}
|
||||
ret.Code = code
|
||||
}
|
||||
|
||||
func renderTemplate(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p := arg["path"].(string)
|
||||
id := arg["id"].(string)
|
||||
content, err := model.RenderTemplate(p, id)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = html.EscapeString(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = map[string]interface{}{
|
||||
"path": p,
|
||||
"content": content,
|
||||
}
|
||||
}
|
||||
108
kernel/api/transaction.go
Normal file
108
kernel/api/transaction.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/sql"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func performTransactions(c *gin.Context) {
|
||||
start := time.Now()
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
trans := arg["transactions"]
|
||||
data, err := gulu.JSON.MarshalJSON(trans)
|
||||
if nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "parses request failed"
|
||||
return
|
||||
}
|
||||
|
||||
var transactions []*model.Transaction
|
||||
if err = gulu.JSON.UnmarshalJSON(data, &transactions); nil != err {
|
||||
ret.Code = -1
|
||||
ret.Msg = "parses request failed"
|
||||
return
|
||||
}
|
||||
|
||||
if op := model.IsSetAttrs(&transactions); nil != op {
|
||||
attrs := map[string]string{}
|
||||
if err = gulu.JSON.UnmarshalJSON([]byte(op.Data.(string)), &attrs); nil != err {
|
||||
return
|
||||
}
|
||||
err = model.SetBlockAttrs(op.ID, attrs)
|
||||
} else {
|
||||
err = model.PerformTransactions(&transactions)
|
||||
}
|
||||
|
||||
if filesys.ErrUnableLockFile == err {
|
||||
ret.Code = 1
|
||||
return
|
||||
}
|
||||
if model.ErrNotFullyBoot == err {
|
||||
ret.Code = -1
|
||||
ret.Msg = fmt.Sprintf(model.Conf.Language(74), int(util.GetBootProgress()))
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
if nil != err {
|
||||
tx, txErr := sql.BeginTx()
|
||||
if nil != txErr {
|
||||
util.LogFatalf("transaction failed: %s", txErr)
|
||||
return
|
||||
}
|
||||
sql.ClearBoxHash(tx)
|
||||
sql.CommitTx(tx)
|
||||
util.LogFatalf("transaction failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
ret.Data = transactions
|
||||
|
||||
app := arg["app"].(string)
|
||||
session := arg["session"].(string)
|
||||
if model.IsFoldHeading(&transactions) || model.IsUnfoldHeading(&transactions) {
|
||||
model.WaitForWritingFiles()
|
||||
}
|
||||
pushTransactions(app, session, transactions)
|
||||
|
||||
elapsed := time.Now().Sub(start).Milliseconds()
|
||||
c.Header("Server-Timing", fmt.Sprintf("total;dur=%d", elapsed))
|
||||
}
|
||||
|
||||
func pushTransactions(app, session string, transactions []*model.Transaction) {
|
||||
evt := util.NewCmdResult("transactions", 0, util.PushModeBroadcastExcludeSelf, util.PushModeBroadcastExcludeSelf)
|
||||
evt.AppId = app
|
||||
evt.SessionId = session
|
||||
evt.Data = transactions
|
||||
util.PushEvent(evt)
|
||||
}
|
||||
107
kernel/api/workspace.go
Normal file
107
kernel/api/workspace.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/siyuan-note/siyuan/kernel/model"
|
||||
"github.com/siyuan-note/siyuan/kernel/util"
|
||||
)
|
||||
|
||||
func listWorkspaceDirs(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
userHomeConfDir := filepath.Join(util.HomeDir, ".config", "siyuan")
|
||||
workspaceConf := filepath.Join(userHomeConfDir, "workspace.json")
|
||||
data, err := os.ReadFile(workspaceConf)
|
||||
var workspacePaths []string
|
||||
if err = gulu.JSON.UnmarshalJSON(data, &workspacePaths); nil != err {
|
||||
util.LogErrorf("unmarshal workspace conf [%s] failed: %s", workspaceConf, err)
|
||||
return
|
||||
}
|
||||
ret.Data = workspacePaths
|
||||
}
|
||||
|
||||
func setWorkspaceDir(c *gin.Context) {
|
||||
ret := gulu.Ret.NewResult()
|
||||
defer c.JSON(http.StatusOK, ret)
|
||||
|
||||
arg, ok := util.JsonArg(c, ret)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
path := arg["path"].(string)
|
||||
if util.WorkspaceDir == path {
|
||||
ret.Code = -1
|
||||
ret.Msg = model.Conf.Language(78)
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 3000}
|
||||
return
|
||||
}
|
||||
|
||||
if gulu.OS.IsWindows() {
|
||||
installDir := filepath.Dir(util.WorkingDir)
|
||||
if strings.HasPrefix(path, installDir) {
|
||||
ret.Code = -1
|
||||
ret.Msg = model.Conf.Language(98)
|
||||
ret.Data = map[string]interface{}{"closeTimeout": 5000}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var workspacePaths []string
|
||||
workspaceConf := filepath.Join(util.HomeDir, ".config", "siyuan", "workspace.json")
|
||||
data, err := os.ReadFile(workspaceConf)
|
||||
if nil != err {
|
||||
util.LogErrorf("read workspace conf failed: %s", err)
|
||||
} else {
|
||||
if err = gulu.JSON.UnmarshalJSON(data, &workspacePaths); nil != err {
|
||||
util.LogErrorf("unmarshal workspace conf failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
workspacePaths = append(workspacePaths, path)
|
||||
workspacePaths = util.RemoveDuplicatedElem(workspacePaths)
|
||||
workspacePaths = util.RemoveElem(workspacePaths, path)
|
||||
workspacePaths = append(workspacePaths, path) // 切换的工作空间固定放在最后一个
|
||||
|
||||
if data, err = gulu.JSON.MarshalJSON(workspacePaths); nil != err {
|
||||
msg := fmt.Sprintf("marshal workspace conf [%s] failed: %s", workspaceConf, err)
|
||||
ret.Code = -1
|
||||
ret.Msg = msg
|
||||
return
|
||||
} else {
|
||||
if err = gulu.File.WriteFileSafer(workspaceConf, data, 0644); nil != err {
|
||||
msg := fmt.Sprintf("create workspace conf [%s] failed: %s", workspaceConf, err)
|
||||
ret.Code = -1
|
||||
ret.Msg = msg
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
util.PushMsg(model.Conf.Language(42), 1000*15)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue