fetch.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package bookstackclient
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "strings"
  8. "github.com/IceWreck/BookStack2Site/config"
  9. )
  10. // FetchBooks returns all the books in the wiki sorted by name.
  11. func FetchBooks(app *config.Application) (Books, error) {
  12. resStruct := Books{}
  13. count := 10 // limit
  14. offset := 0
  15. // keep doing subsequent requests until you get the entire queryset
  16. for {
  17. req, err := http.NewRequest("GET", fmt.Sprint(app.Config.BookStackEndpoint, "/api/books"), nil)
  18. if err != nil {
  19. return resStruct, err
  20. }
  21. q := req.URL.Query()
  22. q.Set("offset", fmt.Sprint(offset))
  23. q.Set("count", fmt.Sprint(count))
  24. q.Set("sort", "+name")
  25. req.URL.RawQuery = q.Encode()
  26. //app.Logger.Debug().Str("req", fmt.Sprint(req)).Msg("")
  27. res, err := authenticatedDo(app, req)
  28. if err != nil {
  29. app.Logger.Debug().Err(err).Msg("performing request")
  30. return resStruct, err
  31. }
  32. body, err := io.ReadAll(res.Body)
  33. if err != nil {
  34. app.Logger.Debug().Err(err).Msg("reading body")
  35. return resStruct, err
  36. }
  37. tmpStruct := Books{}
  38. err = json.Unmarshal(body, &tmpStruct)
  39. if err != nil {
  40. app.Logger.Debug().Err(err).Msg("unmarshaling JSON")
  41. app.Logger.Debug().Str("body", string(body)).Msg("")
  42. return resStruct, err
  43. }
  44. app.Logger.Debug().Str("tmpStruct", fmt.Sprint(tmpStruct)).Msg("")
  45. resStruct.Data = append(resStruct.Data, tmpStruct.Data...)
  46. // stop further requests if number of results < limit
  47. if len(tmpStruct.Data) == 0 {
  48. break
  49. }
  50. offset += count
  51. }
  52. return resStruct, nil
  53. }
  54. // FetchChapters returns all the chapters of a book sorted by priority.
  55. func FetchChapters(app *config.Application, bookID int) (Chapters, error) {
  56. resStruct := Chapters{}
  57. count := 10 // limit
  58. offset := 0
  59. // keep doing subsequent requests until you get the entire queryset
  60. for {
  61. req, err := http.NewRequest("GET", fmt.Sprint(app.Config.BookStackEndpoint, "/api/chapters"), nil)
  62. if err != nil {
  63. return resStruct, err
  64. }
  65. q := req.URL.Query()
  66. q.Set("offset", fmt.Sprint(offset))
  67. q.Set("count", fmt.Sprint(count))
  68. q.Set("count", fmt.Sprint(count))
  69. q.Set("sort", "+priority")
  70. q.Set("filter[book_id]", fmt.Sprint(bookID))
  71. req.URL.RawQuery = q.Encode()
  72. //app.Logger.Debug().Str("req", fmt.Sprint(req)).Msg("")
  73. res, err := authenticatedDo(app, req)
  74. if err != nil {
  75. app.Logger.Debug().Err(err).Msg("performing request")
  76. return resStruct, err
  77. }
  78. body, err := io.ReadAll(res.Body)
  79. if err != nil {
  80. app.Logger.Debug().Err(err).Msg("reading body")
  81. return resStruct, err
  82. }
  83. tmpStruct := Chapters{}
  84. err = json.Unmarshal(body, &tmpStruct)
  85. if err != nil {
  86. app.Logger.Debug().Err(err).Msg("unmarshaling JSON")
  87. app.Logger.Debug().Str("body", string(body)).Msg("")
  88. return resStruct, err
  89. }
  90. app.Logger.Debug().Str("tmpStruct", fmt.Sprint(tmpStruct)).Msg("")
  91. resStruct.Data = append(resStruct.Data, tmpStruct.Data...)
  92. // stop further requests if number of results < limit
  93. if len(tmpStruct.Data) == 0 {
  94. break
  95. }
  96. offset += count
  97. }
  98. return resStruct, nil
  99. }
  100. // FetchPages returns pages sorted by priority.
  101. // If chapterID = 0 then it returns independent (non chapter) pages of the book.
  102. // If chapterID != 0 then it returns pages of the chapter.
  103. func FetchPages(app *config.Application, bookID int, chapterID int) (Chapters, error) {
  104. resStruct := Chapters{}
  105. count := 10 // limit
  106. offset := 0
  107. // keep doing subsequent requests until you get the entire queryset
  108. for {
  109. req, err := http.NewRequest("GET", fmt.Sprint(app.Config.BookStackEndpoint, "/api/pages"), nil)
  110. if err != nil {
  111. return resStruct, err
  112. }
  113. q := req.URL.Query()
  114. q.Set("offset", fmt.Sprint(offset))
  115. q.Set("count", fmt.Sprint(count))
  116. q.Set("count", fmt.Sprint(count))
  117. q.Set("sort", "+priority")
  118. q.Set("filter[book_id]", fmt.Sprint(bookID))
  119. q.Set("filter[chapter_id]", fmt.Sprint(chapterID))
  120. req.URL.RawQuery = q.Encode()
  121. //app.Logger.Debug().Str("req", fmt.Sprint(req)).Msg("")
  122. res, err := authenticatedDo(app, req)
  123. if err != nil {
  124. app.Logger.Debug().Err(err).Msg("performing request")
  125. return resStruct, err
  126. }
  127. body, err := io.ReadAll(res.Body)
  128. if err != nil {
  129. app.Logger.Debug().Err(err).Msg("reading body")
  130. return resStruct, err
  131. }
  132. tmpStruct := Chapters{}
  133. err = json.Unmarshal(body, &tmpStruct)
  134. if err != nil {
  135. app.Logger.Debug().Err(err).Msg("unmarshaling JSON")
  136. app.Logger.Debug().Str("body", string(body)).Msg("")
  137. return resStruct, err
  138. }
  139. app.Logger.Debug().Str("tmpStruct", fmt.Sprint(tmpStruct)).Msg("")
  140. resStruct.Data = append(resStruct.Data, tmpStruct.Data...)
  141. // stop further requests if number of results < limit
  142. if len(tmpStruct.Data) == 0 {
  143. break
  144. }
  145. offset += count
  146. }
  147. return resStruct, nil
  148. }
  149. // FetchPageMarkdown hits the /api/pages/<pageid>/export/markdown endpoint
  150. // and returns a byte slice with APP_URL replaed by a /
  151. func FetchPageMarkdown(app *config.Application, pageID int) ([]byte, error) {
  152. req, err := http.NewRequest("GET", fmt.Sprint(app.Config.BookStackEndpoint, fmt.Sprint("/api/pages/", pageID, "/export/markdown")), nil)
  153. if err != nil {
  154. return []byte(""), err
  155. }
  156. res, err := authenticatedDo(app, req)
  157. if err != nil {
  158. app.Logger.Debug().Err(err).Msg("performing request")
  159. return []byte(""), err
  160. }
  161. body, err := io.ReadAll(res.Body)
  162. if err != nil {
  163. app.Logger.Debug().Err(err).Msg("reading body")
  164. return []byte(""), err
  165. }
  166. replacer := strings.NewReplacer(
  167. // fmt.Sprint("](", app.Config.BookStackEndpoint, "/books/"), "](/",
  168. // fmt.Sprint("](", app.Config.BookStackEndpoint, "/uploads"), "](/uploads",
  169. // fmt.Sprint(`src="`, app.Config.BookStackEndpoint, "/uploads"), `src="/uploads`,
  170. )
  171. // TODO: replace the uploads as well after implementing img downloads
  172. body = []byte(replacer.Replace(string(body)))
  173. return body, nil
  174. }
  175. func FetchWiki(app *config.Application) (Wiki, error) {
  176. w := Wiki{
  177. Name: "",
  178. Books: []WikiBook{},
  179. Shelves: nil,
  180. }
  181. books, err := FetchBooks(app)
  182. if err != nil {
  183. return w, err
  184. }
  185. for _, book := range books.Data {
  186. // create a temporary book and fill initial data
  187. tmpBook := WikiBook{
  188. BookID: book.ID,
  189. Name: book.Name,
  190. Slug: book.Slug,
  191. Chapters: []WikiChapter{},
  192. IndiePages: []WikiPage{},
  193. }
  194. // fetch chapter for that book
  195. chapters, err := FetchChapters(app, book.ID)
  196. if err != nil {
  197. return w, err
  198. }
  199. for _, chapter := range chapters.Data {
  200. tmpChapter := WikiChapter{
  201. ChapterID: chapter.ID,
  202. Name: chapter.Name,
  203. Slug: chapter.Slug,
  204. Priority: chapter.Priority,
  205. Pages: []WikiPage{},
  206. }
  207. // fetch pages for that chapter
  208. pages, err := FetchPages(app, book.ID, chapter.ID)
  209. if err != nil {
  210. return w, err
  211. }
  212. for _, page := range pages.Data {
  213. // add temporary page to the temporary chapter
  214. tmpChapter.Pages = append(tmpChapter.Pages, WikiPage{
  215. PageID: page.ID,
  216. Name: page.Name,
  217. Slug: page.Slug,
  218. Priority: page.Priority,
  219. })
  220. }
  221. // add temporary chapter to the temporary book
  222. tmpBook.Chapters = append(tmpBook.Chapters, tmpChapter)
  223. }
  224. // fetch independent pages for that book
  225. pages, err := FetchPages(app, book.ID, 0)
  226. if err != nil {
  227. return w, err
  228. }
  229. for _, page := range pages.Data {
  230. // add temporary independent page to the temporary book
  231. tmpBook.IndiePages = append(tmpBook.IndiePages, WikiPage{
  232. PageID: page.ID,
  233. Name: page.Name,
  234. Slug: page.Slug,
  235. Priority: page.Priority,
  236. })
  237. }
  238. // add temporary book to the wiki
  239. w.Books = append(w.Books, tmpBook)
  240. }
  241. // TODO: fill up shelves
  242. return w, nil
  243. }