lists.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "github.com/gofrs/uuid"
  7. "github.com/knadh/listmonk/models"
  8. "github.com/lib/pq"
  9. "github.com/labstack/echo"
  10. )
  11. type listsWrap struct {
  12. Results []models.List `json:"results"`
  13. Total int `json:"total"`
  14. PerPage int `json:"per_page"`
  15. Page int `json:"page"`
  16. }
  17. var (
  18. listQuerySortFields = []string{"name", "type", "subscriber_count", "created_at", "updated_at"}
  19. )
  20. // handleGetLists handles retrieval of lists.
  21. func handleGetLists(c echo.Context) error {
  22. var (
  23. app = c.Get("app").(*App)
  24. out listsWrap
  25. pg = getPagination(c.QueryParams(), 20)
  26. orderBy = c.FormValue("order_by")
  27. order = c.FormValue("order")
  28. listID, _ = strconv.Atoi(c.Param("id"))
  29. single = false
  30. )
  31. // Fetch one list.
  32. if listID > 0 {
  33. single = true
  34. }
  35. // Sort params.
  36. if !strSliceContains(orderBy, listQuerySortFields) {
  37. orderBy = "created_at"
  38. }
  39. if order != sortAsc && order != sortDesc {
  40. order = sortAsc
  41. }
  42. if err := db.Select(&out.Results, fmt.Sprintf(app.queries.QueryLists, orderBy, order), listID, pg.Offset, pg.Limit); err != nil {
  43. app.log.Printf("error fetching lists: %v", err)
  44. return echo.NewHTTPError(http.StatusInternalServerError,
  45. app.i18n.Ts("globals.messages.errorFetching",
  46. "name", "{globals.terms.lists}", "error", pqErrMsg(err)))
  47. }
  48. if single && len(out.Results) == 0 {
  49. return echo.NewHTTPError(http.StatusBadRequest,
  50. app.i18n.Ts("globals.messages.notFound", "name", "{globals.terms.list}"))
  51. }
  52. if len(out.Results) == 0 {
  53. return c.JSON(http.StatusOK, okResp{[]struct{}{}})
  54. }
  55. // Replace null tags.
  56. for i, v := range out.Results {
  57. if v.Tags == nil {
  58. out.Results[i].Tags = make(pq.StringArray, 0)
  59. }
  60. }
  61. if single {
  62. return c.JSON(http.StatusOK, okResp{out.Results[0]})
  63. }
  64. // Meta.
  65. out.Total = out.Results[0].Total
  66. out.Page = pg.Page
  67. out.PerPage = pg.PerPage
  68. return c.JSON(http.StatusOK, okResp{out})
  69. }
  70. // handleCreateList handles list creation.
  71. func handleCreateList(c echo.Context) error {
  72. var (
  73. app = c.Get("app").(*App)
  74. o = models.List{}
  75. )
  76. if err := c.Bind(&o); err != nil {
  77. return err
  78. }
  79. // Validate.
  80. if !strHasLen(o.Name, 1, stdInputMaxLen) {
  81. return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("lists.invalidName"))
  82. }
  83. uu, err := uuid.NewV4()
  84. if err != nil {
  85. app.log.Printf("error generating UUID: %v", err)
  86. return echo.NewHTTPError(http.StatusInternalServerError,
  87. app.i18n.Ts("globals.messages.errorUUID", "error", err.Error()))
  88. }
  89. // Insert and read ID.
  90. var newID int
  91. o.UUID = uu.String()
  92. if err := app.queries.CreateList.Get(&newID,
  93. o.UUID,
  94. o.Name,
  95. o.Type,
  96. o.Optin,
  97. pq.StringArray(normalizeTags(o.Tags))); err != nil {
  98. app.log.Printf("error creating list: %v", err)
  99. return echo.NewHTTPError(http.StatusInternalServerError,
  100. app.i18n.Ts("globals.messages.errorCreating",
  101. "name", "{globals.terms.list}", "error", pqErrMsg(err)))
  102. }
  103. // Hand over to the GET handler to return the last insertion.
  104. return handleGetLists(copyEchoCtx(c, map[string]string{
  105. "id": fmt.Sprintf("%d", newID),
  106. }))
  107. }
  108. // handleUpdateList handles list modification.
  109. func handleUpdateList(c echo.Context) error {
  110. var (
  111. app = c.Get("app").(*App)
  112. id, _ = strconv.Atoi(c.Param("id"))
  113. )
  114. if id < 1 {
  115. return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
  116. }
  117. // Incoming params.
  118. var o models.List
  119. if err := c.Bind(&o); err != nil {
  120. return err
  121. }
  122. res, err := app.queries.UpdateList.Exec(id,
  123. o.Name, o.Type, o.Optin, pq.StringArray(normalizeTags(o.Tags)))
  124. if err != nil {
  125. app.log.Printf("error updating list: %v", err)
  126. return echo.NewHTTPError(http.StatusInternalServerError,
  127. app.i18n.Ts("globals.messages.errorUpdating",
  128. "name", "{globals.terms.list}", "error", pqErrMsg(err)))
  129. }
  130. if n, _ := res.RowsAffected(); n == 0 {
  131. return echo.NewHTTPError(http.StatusBadRequest,
  132. app.i18n.Ts("globals.messages.notFound", "name", "{globals.terms.list}"))
  133. }
  134. return handleGetLists(c)
  135. }
  136. // handleDeleteLists handles list deletion, either a single one (ID in the URI), or a list.
  137. func handleDeleteLists(c echo.Context) error {
  138. var (
  139. app = c.Get("app").(*App)
  140. id, _ = strconv.ParseInt(c.Param("id"), 10, 64)
  141. ids pq.Int64Array
  142. )
  143. if id < 1 && len(ids) == 0 {
  144. return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID"))
  145. }
  146. if id > 0 {
  147. ids = append(ids, id)
  148. }
  149. if _, err := app.queries.DeleteLists.Exec(ids); err != nil {
  150. app.log.Printf("error deleting lists: %v", err)
  151. return echo.NewHTTPError(http.StatusInternalServerError,
  152. app.i18n.Ts("globals.messages.errorDeleting",
  153. "name", "{globals.terms.list}", "error", pqErrMsg(err)))
  154. }
  155. return c.JSON(http.StatusOK, okResp{true})
  156. }