Something went wrong. Try again.
A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685// SiYuan - Refactor your thinking// 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" "mime" "net/http" "os" "path" "path/filepath" "strings" "time"
"github.com/88250/gulu" "github.com/88250/lute/parse" "github.com/gin-gonic/gin" "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/model" "github.com/siyuan-note/siyuan/kernel/util")
func exportAttributeView(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret) if !ok { return }
avID := arg["id"].(string) blockID := arg["blockID"].(string) zipPath, err := model.ExportAv2CSV(avID, blockID) if err != nil { ret.Code = 1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return }
ret.Data = map[string]interface{}{ "zip": zipPath, }}
func exportEPUB(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.ExportPandocConvertZip([]string{id}, "epub", ".epub") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportRTF(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.ExportPandocConvertZip([]string{id}, "rtf", ".rtf") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportODT(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.ExportPandocConvertZip([]string{id}, "odt", ".odt") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportMediaWiki(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.ExportPandocConvertZip([]string{id}, "mediawiki", ".wiki") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportOrgMode(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.ExportPandocConvertZip([]string{id}, "org", ".org") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportOPML(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.ExportPandocConvertZip([]string{id}, "opml", ".opml") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportTextile(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.ExportPandocConvertZip([]string{id}, "textile", ".textile") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportAsciiDoc(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.ExportPandocConvertZip([]string{id}, "asciidoc", ".adoc") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportReStructuredText(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.ExportPandocConvertZip([]string{id}, "rst", ".rst") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func export2Liandi(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.Export2Liandi(id) if err != nil { ret.Code = -1 ret.Msg = err.Error() return }}
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) name, err := model.ExportDataInFolder(exportFolder) if err != nil { ret.Code = -1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } ret.Data = map[string]interface{}{ "name": name, }}
func exportData(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret)
zipPath, err := model.ExportData() if err != nil { ret.Code = 1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } ret.Data = map[string]interface{}{ "zip": zipPath, }}
func exportResources(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret) if !ok { return }
var name string if nil != arg["name"] { name = util.TruncateLenFileName(arg["name"].(string)) } if name == "" { name = time.Now().Format("export-2006-01-02_15-04-05") // 生成的 *.zip 文件主文件名 }
if nil == arg["paths"] { ret.Code = 1 ret.Data = "" ret.Msg = "paths is required" return }
var resourcePaths []string // 文件/文件夹在工作空间中的路径 for _, resourcePath := range arg["paths"].([]interface{}) { resourcePaths = append(resourcePaths, resourcePath.(string)) }
zipFilePath, err := model.ExportResources(resourcePaths, name) if err != nil { ret.Code = 1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } ret.Data = map[string]interface{}{ "path": zipFilePath, // 相对于工作空间目录的路径 }}
func exportNotebookMd(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) zipPath := model.ExportNotebookMarkdown(notebook) ret.Data = map[string]interface{}{ "name": path.Base(zipPath), "zip": zipPath, }}
func exportMds(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)) }
name, zipPath := model.ExportPandocConvertZip(ids, "", ".md") ret.Data = map[string]interface{}{ "name": name, "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.ExportPandocConvertZip([]string{id}, "", ".md") ret.Data = map[string]interface{}{ "name": name, "zip": zipPath, }}
func exportNotebookSY(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) zipPath := model.ExportNotebookSY(id) ret.Data = map[string]interface{}{ "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) if util.InvalidIDPattern(id, ret) { return }
refMode := model.Conf.Export.BlockRefMode if nil != arg["refMode"] { refMode = int(arg["refMode"].(float64)) }
embedMode := model.Conf.Export.BlockEmbedMode if nil != arg["embedMode"] { embedMode = int(arg["embedMode"].(float64)) }
yfm := true if nil != arg["yfm"] { yfm = arg["yfm"].(bool) }
hPath, content := model.ExportMarkdownContent(id, refMode, embedMode, yfm) 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) removeAssets := arg["removeAssets"].(bool) merge := false if nil != arg["merge"] { merge = arg["merge"].(bool) }
fullPath, err := model.ExportDocx(id, savePath, removeAssets, merge) if err != nil { ret.Code = -1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } ret.Data = map[string]interface{}{ "path": fullPath, }}
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, false) ret.Data = map[string]interface{}{ "id": id, "name": name, "content": content, }}
func exportTempContent(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret) if !ok { return }
content := arg["content"].(string) tmpExport := filepath.Join(util.TempDir, "export", "temp") if err := os.MkdirAll(tmpExport, 0755); err != nil { ret.Code = 1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } p := filepath.Join(tmpExport, gulu.Rand.String(7)) if err := os.WriteFile(p, []byte(content), 0644); err != nil { ret.Code = 1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 7000} return } url := path.Join("/export/temp/", filepath.Base(p)) ret.Data = map[string]interface{}{ "url": "http://" + util.LocalHost + ":" + util.ServerPort + url, }}
func exportPreviewHTML(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) keepFold := false if nil != arg["keepFold"] { keepFold = arg["keepFold"].(bool) } merge := false if nil != arg["merge"] { merge = arg["merge"].(bool) } image := false if nil != arg["image"] { image = arg["image"].(bool) } name, content, node := model.ExportHTML(id, "", true, image, keepFold, merge) // 导出 PDF 预览时点击块引转换后的脚注跳转不正确 https://github.com/siyuan-note/siyuan/issues/5894 content = strings.ReplaceAll(content, "http://"+util.LocalHost+":"+util.ServerPort+"/#", "#")
// Add `data-doc-type` and attribute when exporting image and PDF https://github.com/siyuan-note/siyuan/issues/9497 attrs := map[string]string{} var typ string if nil != node { attrs = parse.IAL2Map(node.KramdownIAL) typ = node.Type.String() }
ret.Data = map[string]interface{}{ "id": id, "name": name, "content": content, "attrs": attrs, "type": typ, }}
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) keepFold := false if nil != arg["keepFold"] { keepFold = arg["keepFold"].(bool) } merge := false if nil != arg["merge"] { merge = arg["merge"].(bool) } name, content, _ := model.ExportHTML(id, savePath, pdf, false, keepFold, merge) ret.Data = map[string]interface{}{ "id": id, "name": name, "content": content, }}
func processPDF(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) merge := false if nil != arg["merge"] { merge = arg["merge"].(bool) } removeAssets := arg["removeAssets"].(bool) watermark := arg["watermark"].(bool) err := model.ProcessPDF(id, path, merge, removeAssets, watermark) if err != nil { 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, }}
func exportAsFile(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret)
form, err := c.MultipartForm() if err != nil { logging.LogErrorf("export as file failed: %s", err) ret.Code = -1 ret.Msg = err.Error() return }
file := form.File["file"][0] reader, err := file.Open() if err != nil { logging.LogErrorf("export as file failed: %s", err) ret.Code = -1 ret.Msg = err.Error() return } defer reader.Close()
data, err := io.ReadAll(reader) if err != nil { logging.LogErrorf("export as file failed: %s", err) ret.Code = -1 ret.Msg = err.Error() return }
name := "file-" + file.Filename typ := form.Value["type"][0] exts, _ := mime.ExtensionsByType(typ) if 0 < len(exts) && filepath.Ext(name) != exts[0] { name += exts[0] } name = util.FilterFileName(name) name = strings.ReplaceAll(name, "#", "_") tmpDir := filepath.Join(util.TempDir, "export") if err = os.MkdirAll(tmpDir, 0755); err != nil { logging.LogErrorf("export as file failed: %s", err) ret.Code = -1 ret.Msg = err.Error() return }
tmp := filepath.Join(tmpDir, name) err = os.WriteFile(tmp, data, 0644) if err != nil { logging.LogErrorf("export as file failed: %s", err) ret.Code = -1 ret.Msg = err.Error() return }
ret.Data = map[string]interface{}{ "file": path.Join("/export/", name), }}