export.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // SiYuan - Build Your Eternal Digital Garden
  2. // Copyright (c) 2020-present, b3log.org
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package api
  17. import (
  18. "io"
  19. "net/http"
  20. "os"
  21. "path"
  22. "path/filepath"
  23. "strings"
  24. "github.com/88250/gulu"
  25. "github.com/gin-gonic/gin"
  26. "github.com/siyuan-note/logging"
  27. "github.com/siyuan-note/siyuan/kernel/model"
  28. "github.com/siyuan-note/siyuan/kernel/util"
  29. )
  30. func exportDataInFolder(c *gin.Context) {
  31. ret := gulu.Ret.NewResult()
  32. defer c.JSON(http.StatusOK, ret)
  33. arg, ok := util.JsonArg(c, ret)
  34. if !ok {
  35. return
  36. }
  37. exportFolder := arg["folder"].(string)
  38. name, err := model.ExportDataInFolder(exportFolder)
  39. if nil != err {
  40. ret.Code = -1
  41. ret.Msg = err.Error()
  42. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  43. return
  44. }
  45. ret.Data = map[string]interface{}{
  46. "name": name,
  47. }
  48. }
  49. func exportData(c *gin.Context) {
  50. ret := gulu.Ret.NewResult()
  51. defer c.JSON(http.StatusOK, ret)
  52. zipPath := model.ExportData()
  53. ret.Data = map[string]interface{}{
  54. "zip": zipPath,
  55. }
  56. }
  57. func batchExportMd(c *gin.Context) {
  58. ret := gulu.Ret.NewResult()
  59. defer c.JSON(http.StatusOK, ret)
  60. arg, ok := util.JsonArg(c, ret)
  61. if !ok {
  62. return
  63. }
  64. notebook := arg["notebook"].(string)
  65. p := arg["path"].(string)
  66. zipPath := model.BatchExportMarkdown(notebook, p)
  67. ret.Data = map[string]interface{}{
  68. "name": path.Base(zipPath),
  69. "zip": zipPath,
  70. }
  71. }
  72. func exportMd(c *gin.Context) {
  73. ret := gulu.Ret.NewResult()
  74. defer c.JSON(http.StatusOK, ret)
  75. arg, ok := util.JsonArg(c, ret)
  76. if !ok {
  77. return
  78. }
  79. id := arg["id"].(string)
  80. name, zipPath := model.ExportMarkdown(id)
  81. ret.Data = map[string]interface{}{
  82. "name": name,
  83. "zip": zipPath,
  84. }
  85. }
  86. func exportNotebookSY(c *gin.Context) {
  87. ret := gulu.Ret.NewResult()
  88. defer c.JSON(http.StatusOK, ret)
  89. arg, ok := util.JsonArg(c, ret)
  90. if !ok {
  91. return
  92. }
  93. id := arg["id"].(string)
  94. zipPath := model.ExportNotebookSY(id)
  95. ret.Data = map[string]interface{}{
  96. "zip": zipPath,
  97. }
  98. }
  99. func exportSY(c *gin.Context) {
  100. ret := gulu.Ret.NewResult()
  101. defer c.JSON(http.StatusOK, ret)
  102. arg, ok := util.JsonArg(c, ret)
  103. if !ok {
  104. return
  105. }
  106. id := arg["id"].(string)
  107. name, zipPath := model.ExportSY(id)
  108. ret.Data = map[string]interface{}{
  109. "name": name,
  110. "zip": zipPath,
  111. }
  112. }
  113. func exportMdContent(c *gin.Context) {
  114. ret := gulu.Ret.NewResult()
  115. defer c.JSON(http.StatusOK, ret)
  116. arg, ok := util.JsonArg(c, ret)
  117. if !ok {
  118. return
  119. }
  120. id := arg["id"].(string)
  121. hPath, content := model.ExportMarkdownContent(id)
  122. ret.Data = map[string]interface{}{
  123. "hPath": hPath,
  124. "content": content,
  125. }
  126. }
  127. func exportDocx(c *gin.Context) {
  128. ret := gulu.Ret.NewResult()
  129. defer c.JSON(http.StatusOK, ret)
  130. arg, ok := util.JsonArg(c, ret)
  131. if !ok {
  132. return
  133. }
  134. id := arg["id"].(string)
  135. savePath := arg["savePath"].(string)
  136. removeAssets := arg["removeAssets"].(bool)
  137. merge := false
  138. if nil != arg["merge"] {
  139. merge = arg["merge"].(bool)
  140. }
  141. err := model.ExportDocx(id, savePath, removeAssets, merge)
  142. if nil != err {
  143. ret.Code = 1
  144. ret.Msg = err.Error()
  145. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  146. return
  147. }
  148. }
  149. func exportMdHTML(c *gin.Context) {
  150. ret := gulu.Ret.NewResult()
  151. defer c.JSON(http.StatusOK, ret)
  152. arg, ok := util.JsonArg(c, ret)
  153. if !ok {
  154. return
  155. }
  156. id := arg["id"].(string)
  157. savePath := arg["savePath"].(string)
  158. name, content := model.ExportMarkdownHTML(id, savePath, false, false)
  159. ret.Data = map[string]interface{}{
  160. "id": id,
  161. "name": name,
  162. "content": content,
  163. }
  164. }
  165. func exportTempContent(c *gin.Context) {
  166. ret := gulu.Ret.NewResult()
  167. defer c.JSON(http.StatusOK, ret)
  168. arg, ok := util.JsonArg(c, ret)
  169. if !ok {
  170. return
  171. }
  172. content := arg["content"].(string)
  173. tmpExport := filepath.Join(util.TempDir, "export", "temp")
  174. if err := os.MkdirAll(tmpExport, 0755); nil != err {
  175. ret.Code = 1
  176. ret.Msg = err.Error()
  177. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  178. return
  179. }
  180. p := filepath.Join(tmpExport, gulu.Rand.String(7))
  181. if err := os.WriteFile(p, []byte(content), 0644); nil != err {
  182. ret.Code = 1
  183. ret.Msg = err.Error()
  184. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  185. return
  186. }
  187. url := path.Join("/export/temp/", filepath.Base(p))
  188. ret.Data = map[string]interface{}{
  189. "url": "http://" + util.LocalHost + ":" + util.ServerPort + url,
  190. }
  191. }
  192. func exportPreviewHTML(c *gin.Context) {
  193. ret := gulu.Ret.NewResult()
  194. defer c.JSON(http.StatusOK, ret)
  195. arg, ok := util.JsonArg(c, ret)
  196. if !ok {
  197. return
  198. }
  199. id := arg["id"].(string)
  200. keepFold := false
  201. if nil != arg["keepFold"] {
  202. keepFold = arg["keepFold"].(bool)
  203. }
  204. merge := false
  205. if nil != arg["merge"] {
  206. merge = arg["merge"].(bool)
  207. }
  208. name, content := model.ExportHTML(id, "", true, keepFold, merge)
  209. // 导出 PDF 预览时点击块引转换后的脚注跳转不正确 https://github.com/siyuan-note/siyuan/issues/5894
  210. content = strings.ReplaceAll(content, "http://"+util.LocalHost+":"+util.ServerPort+"/#", "#")
  211. ret.Data = map[string]interface{}{
  212. "id": id,
  213. "name": name,
  214. "content": content,
  215. }
  216. }
  217. func exportHTML(c *gin.Context) {
  218. ret := gulu.Ret.NewResult()
  219. defer c.JSON(http.StatusOK, ret)
  220. arg, ok := util.JsonArg(c, ret)
  221. if !ok {
  222. return
  223. }
  224. id := arg["id"].(string)
  225. pdf := arg["pdf"].(bool)
  226. savePath := arg["savePath"].(string)
  227. keepFold := false
  228. if nil != arg["keepFold"] {
  229. keepFold = arg["keepFold"].(bool)
  230. }
  231. merge := false
  232. if nil != arg["merge"] {
  233. merge = arg["merge"].(bool)
  234. }
  235. name, content := model.ExportHTML(id, savePath, pdf, keepFold, merge)
  236. ret.Data = map[string]interface{}{
  237. "id": id,
  238. "name": name,
  239. "content": content,
  240. }
  241. }
  242. func addPDFOutline(c *gin.Context) {
  243. ret := gulu.Ret.NewResult()
  244. defer c.JSON(http.StatusOK, ret)
  245. arg, ok := util.JsonArg(c, ret)
  246. if !ok {
  247. return
  248. }
  249. id := arg["id"].(string)
  250. path := arg["path"].(string)
  251. err := model.AddPDFOutline(id, path)
  252. if nil != err {
  253. ret.Code = -1
  254. ret.Msg = err.Error()
  255. return
  256. }
  257. }
  258. func exportPreview(c *gin.Context) {
  259. ret := gulu.Ret.NewResult()
  260. defer c.JSON(http.StatusOK, ret)
  261. arg, ok := util.JsonArg(c, ret)
  262. if !ok {
  263. return
  264. }
  265. id := arg["id"].(string)
  266. stdHTML := model.Preview(id)
  267. ret.Data = map[string]interface{}{
  268. "html": stdHTML,
  269. }
  270. }
  271. func exportAsFile(c *gin.Context) {
  272. ret := gulu.Ret.NewResult()
  273. defer c.JSON(http.StatusOK, ret)
  274. form, err := c.MultipartForm()
  275. if nil != err {
  276. logging.LogErrorf("export as file failed: %s", err)
  277. ret.Code = -1
  278. ret.Msg = err.Error()
  279. return
  280. }
  281. file := form.File["file"][0]
  282. reader, err := file.Open()
  283. if nil != err {
  284. logging.LogErrorf("export as file failed: %s", err)
  285. ret.Code = -1
  286. ret.Msg = err.Error()
  287. return
  288. }
  289. defer reader.Close()
  290. data, err := io.ReadAll(reader)
  291. if nil != err {
  292. logging.LogErrorf("export as file failed: %s", err)
  293. ret.Code = -1
  294. ret.Msg = err.Error()
  295. return
  296. }
  297. name := "file-" + file.Filename
  298. name = util.FilterFileName(name)
  299. tmpDir := filepath.Join(util.TempDir, "export")
  300. if err = os.MkdirAll(tmpDir, 0755); nil != err {
  301. logging.LogErrorf("export as file failed: %s", err)
  302. ret.Code = -1
  303. ret.Msg = err.Error()
  304. return
  305. }
  306. tmp := filepath.Join(tmpDir, name)
  307. err = os.WriteFile(tmp, data, 0644)
  308. if nil != err {
  309. logging.LogErrorf("export as file failed: %s", err)
  310. ret.Code = -1
  311. ret.Msg = err.Error()
  312. return
  313. }
  314. ret.Data = map[string]interface{}{
  315. "name": name,
  316. "file": path.Join("/export/", name),
  317. }
  318. }