lists.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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, 50)
  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.GetLists, 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. fmt.Sprintf("Error fetching lists: %s", pqErrMsg(err)))
  46. }
  47. if single && len(out.Results) == 0 {
  48. return echo.NewHTTPError(http.StatusBadRequest, "List not found.")
  49. }
  50. if len(out.Results) == 0 {
  51. return c.JSON(http.StatusOK, okResp{[]struct{}{}})
  52. }
  53. // Replace null tags.
  54. for i, v := range out.Results {
  55. if v.Tags == nil {
  56. out.Results[i].Tags = make(pq.StringArray, 0)
  57. }
  58. }
  59. if single {
  60. return c.JSON(http.StatusOK, okResp{out.Results[0]})
  61. }
  62. // Meta.
  63. out.Total = out.Results[0].Total
  64. out.Page = pg.Page
  65. out.PerPage = pg.PerPage
  66. return c.JSON(http.StatusOK, okResp{out})
  67. }
  68. // handleCreateList handles list creation.
  69. func handleCreateList(c echo.Context) error {
  70. var (
  71. app = c.Get("app").(*App)
  72. o = models.List{}
  73. )
  74. if err := c.Bind(&o); err != nil {
  75. return err
  76. }
  77. // Validate.
  78. if !strHasLen(o.Name, 1, stdInputMaxLen) {
  79. return echo.NewHTTPError(http.StatusBadRequest,
  80. "Invalid length for the name field.")
  81. }
  82. uu, err := uuid.NewV4()
  83. if err != nil {
  84. app.log.Printf("error generating UUID: %v", err)
  85. return echo.NewHTTPError(http.StatusInternalServerError, "Error generating UUID")
  86. }
  87. // Insert and read ID.
  88. var newID int
  89. o.UUID = uu.String()
  90. if err := app.queries.CreateList.Get(&newID,
  91. o.UUID,
  92. o.Name,
  93. o.Type,
  94. o.Optin,
  95. pq.StringArray(normalizeTags(o.Tags))); err != nil {
  96. app.log.Printf("error creating list: %v", err)
  97. return echo.NewHTTPError(http.StatusInternalServerError,
  98. fmt.Sprintf("Error creating list: %s", pqErrMsg(err)))
  99. }
  100. // Hand over to the GET handler to return the last insertion.
  101. c.SetParamNames("id")
  102. c.SetParamValues(fmt.Sprintf("%d", newID))
  103. return handleGetLists(c)
  104. }
  105. // handleUpdateList handles list modification.
  106. func handleUpdateList(c echo.Context) error {
  107. var (
  108. app = c.Get("app").(*App)
  109. id, _ = strconv.Atoi(c.Param("id"))
  110. )
  111. if id < 1 {
  112. return echo.NewHTTPError(http.StatusBadRequest, "Invalid ID.")
  113. }
  114. // Incoming params.
  115. var o models.List
  116. if err := c.Bind(&o); err != nil {
  117. return err
  118. }
  119. res, err := app.queries.UpdateList.Exec(id,
  120. o.Name, o.Type, o.Optin, pq.StringArray(normalizeTags(o.Tags)))
  121. if err != nil {
  122. app.log.Printf("error updating list: %v", err)
  123. return echo.NewHTTPError(http.StatusBadRequest,
  124. fmt.Sprintf("Error updating list: %s", pqErrMsg(err)))
  125. }
  126. if n, _ := res.RowsAffected(); n == 0 {
  127. return echo.NewHTTPError(http.StatusBadRequest, "List not found.")
  128. }
  129. return handleGetLists(c)
  130. }
  131. // handleDeleteLists handles deletion deletion,
  132. // either a single one (ID in the URI), or a list.
  133. func handleDeleteLists(c echo.Context) error {
  134. var (
  135. app = c.Get("app").(*App)
  136. id, _ = strconv.ParseInt(c.Param("id"), 10, 64)
  137. ids pq.Int64Array
  138. )
  139. if id < 1 && len(ids) == 0 {
  140. return echo.NewHTTPError(http.StatusBadRequest, "Invalid ID.")
  141. }
  142. if id > 0 {
  143. ids = append(ids, id)
  144. }
  145. if _, err := app.queries.DeleteLists.Exec(ids); err != nil {
  146. app.log.Printf("error deleting lists: %v", err)
  147. return echo.NewHTTPError(http.StatusInternalServerError,
  148. fmt.Sprintf("Error deleting: %v", err))
  149. }
  150. return c.JSON(http.StatusOK, okResp{true})
  151. }