export.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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. err := model.ExportDocx(id, savePath, removeAssets)
  138. if nil != err {
  139. ret.Code = 1
  140. ret.Msg = err.Error()
  141. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  142. return
  143. }
  144. }
  145. func exportMdHTML(c *gin.Context) {
  146. ret := gulu.Ret.NewResult()
  147. defer c.JSON(http.StatusOK, ret)
  148. arg, ok := util.JsonArg(c, ret)
  149. if !ok {
  150. return
  151. }
  152. id := arg["id"].(string)
  153. savePath := arg["savePath"].(string)
  154. name, content := model.ExportMarkdownHTML(id, savePath, false)
  155. ret.Data = map[string]interface{}{
  156. "id": id,
  157. "name": name,
  158. "content": content,
  159. }
  160. }
  161. func exportTempContent(c *gin.Context) {
  162. ret := gulu.Ret.NewResult()
  163. defer c.JSON(http.StatusOK, ret)
  164. arg, ok := util.JsonArg(c, ret)
  165. if !ok {
  166. return
  167. }
  168. content := arg["content"].(string)
  169. tmpExport := filepath.Join(util.TempDir, "export", "temp")
  170. if err := os.MkdirAll(tmpExport, 0755); nil != err {
  171. ret.Code = 1
  172. ret.Msg = err.Error()
  173. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  174. return
  175. }
  176. p := filepath.Join(tmpExport, gulu.Rand.String(7))
  177. if err := os.WriteFile(p, []byte(content), 0644); nil != err {
  178. ret.Code = 1
  179. ret.Msg = err.Error()
  180. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  181. return
  182. }
  183. url := path.Join("/export/temp/", filepath.Base(p))
  184. ret.Data = map[string]interface{}{
  185. "url": "http://" + util.LocalHost + ":" + util.ServerPort + url,
  186. }
  187. }
  188. func exportPreviewHTML(c *gin.Context) {
  189. ret := gulu.Ret.NewResult()
  190. defer c.JSON(http.StatusOK, ret)
  191. arg, ok := util.JsonArg(c, ret)
  192. if !ok {
  193. return
  194. }
  195. id := arg["id"].(string)
  196. keepFold := false
  197. if arg["keepFold"] != nil {
  198. keepFold = arg["keepFold"].(bool)
  199. }
  200. name, content := model.ExportHTML(id, "", true, keepFold)
  201. // 导出 PDF 预览时点击块引转换后的脚注跳转不正确 https://github.com/siyuan-note/siyuan/issues/5894
  202. content = strings.ReplaceAll(content, "http://"+util.LocalHost+":"+util.ServerPort+"/#", "#")
  203. ret.Data = map[string]interface{}{
  204. "id": id,
  205. "name": name,
  206. "content": content,
  207. }
  208. }
  209. func exportHTML(c *gin.Context) {
  210. ret := gulu.Ret.NewResult()
  211. defer c.JSON(http.StatusOK, ret)
  212. arg, ok := util.JsonArg(c, ret)
  213. if !ok {
  214. return
  215. }
  216. id := arg["id"].(string)
  217. pdf := arg["pdf"].(bool)
  218. savePath := arg["savePath"].(string)
  219. keepFold := false
  220. if arg["keepFold"] != nil {
  221. keepFold = arg["keepFold"].(bool)
  222. }
  223. name, content := model.ExportHTML(id, savePath, pdf, keepFold)
  224. ret.Data = map[string]interface{}{
  225. "id": id,
  226. "name": name,
  227. "content": content,
  228. }
  229. }
  230. func addPDFOutline(c *gin.Context) {
  231. ret := gulu.Ret.NewResult()
  232. defer c.JSON(http.StatusOK, ret)
  233. arg, ok := util.JsonArg(c, ret)
  234. if !ok {
  235. return
  236. }
  237. id := arg["id"].(string)
  238. path := arg["path"].(string)
  239. err := model.AddPDFOutline(id, path)
  240. if nil != err {
  241. ret.Code = -1
  242. ret.Msg = err.Error()
  243. return
  244. }
  245. }
  246. func exportPreview(c *gin.Context) {
  247. ret := gulu.Ret.NewResult()
  248. defer c.JSON(http.StatusOK, ret)
  249. arg, ok := util.JsonArg(c, ret)
  250. if !ok {
  251. return
  252. }
  253. id := arg["id"].(string)
  254. stdHTML := model.Preview(id)
  255. ret.Data = map[string]interface{}{
  256. "html": stdHTML,
  257. }
  258. }
  259. func exportAsFile(c *gin.Context) {
  260. ret := gulu.Ret.NewResult()
  261. defer c.JSON(http.StatusOK, ret)
  262. form, err := c.MultipartForm()
  263. if nil != err {
  264. logging.LogErrorf("export as file failed: %s", err)
  265. ret.Code = -1
  266. ret.Msg = err.Error()
  267. return
  268. }
  269. file := form.File["file"][0]
  270. reader, err := file.Open()
  271. if nil != err {
  272. logging.LogErrorf("export as file failed: %s", err)
  273. ret.Code = -1
  274. ret.Msg = err.Error()
  275. return
  276. }
  277. defer reader.Close()
  278. data, err := io.ReadAll(reader)
  279. if nil != err {
  280. logging.LogErrorf("export as file failed: %s", err)
  281. ret.Code = -1
  282. ret.Msg = err.Error()
  283. return
  284. }
  285. name := "file-" + file.Filename
  286. name = util.FilterFileName(name)
  287. tmpDir := filepath.Join(util.TempDir, "export")
  288. if err = os.MkdirAll(tmpDir, 0755); nil != err {
  289. logging.LogErrorf("export as file failed: %s", err)
  290. ret.Code = -1
  291. ret.Msg = err.Error()
  292. return
  293. }
  294. tmp := filepath.Join(tmpDir, name)
  295. err = os.WriteFile(tmp, data, 0644)
  296. if nil != err {
  297. logging.LogErrorf("export as file failed: %s", err)
  298. ret.Code = -1
  299. ret.Msg = err.Error()
  300. return
  301. }
  302. ret.Data = map[string]interface{}{
  303. "name": name,
  304. "file": path.Join("/export/", name),
  305. }
  306. }