Loggest thy stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.8 KiB

  1. package api
  2. import (
  3. "git.aiterp.net/stufflog3/stufflog3-api/internal/auth"
  4. "git.aiterp.net/stufflog3/stufflog3-api/internal/database"
  5. "git.aiterp.net/stufflog3/stufflog3-api/internal/models"
  6. "git.aiterp.net/stufflog3/stufflog3-api/internal/slerrors"
  7. "github.com/gin-gonic/gin"
  8. "time"
  9. )
  10. func Items(g *gin.RouterGroup, db database.Database) {
  11. g.Use(scopeIDMiddleware(db))
  12. g.GET("/", handler("items", func(c *gin.Context) (res interface{}, err error) {
  13. mode := c.Query("mode")
  14. var fromTime, toTime time.Time
  15. var fromDate, toDate models.Date
  16. if mode != "scheduled" {
  17. if c.Query("to") == "" {
  18. toTime = time.Now()
  19. } else {
  20. toTime, err = time.Parse(time.RFC3339Nano, c.Query("to"))
  21. if err != nil {
  22. return nil, slerrors.BadRequest("Invalid value for to")
  23. }
  24. }
  25. if c.Query("from") == "" {
  26. fromTime = time.Now().Add(-time.Hour * 24 * 30)
  27. } else {
  28. fromTime, err = time.Parse(time.RFC3339Nano, c.Query("from"))
  29. if err != nil {
  30. return nil, slerrors.BadRequest("Invalid value for from")
  31. }
  32. }
  33. if toTime.After(toTime) {
  34. return nil, slerrors.BadRequest("the flow of time itself is convoluted, but not so for the database")
  35. }
  36. } else {
  37. if c.Query("to") == "" {
  38. toDate, _ = models.ParseDate(time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02"))
  39. } else {
  40. toDate, err = models.ParseDate(c.Query("to"))
  41. if err != nil {
  42. return nil, slerrors.BadRequest("Invalid value for to")
  43. }
  44. }
  45. if c.Query("from") == "" {
  46. fromDate, _ = models.ParseDate(time.Now().Format("2006-01-02"))
  47. } else {
  48. fromDate, err = models.ParseDate(c.Query("from"))
  49. if err != nil {
  50. return nil, slerrors.BadRequest("Invalid value for from")
  51. }
  52. }
  53. }
  54. switch mode {
  55. case "", "created":
  56. return db.Items(getScope(c).ID).ListCreated(c.Request.Context(), fromTime, toTime)
  57. case "acquired":
  58. return db.Items(getScope(c).ID).ListAcquired(c.Request.Context(), fromTime, toTime)
  59. case "loose":
  60. return db.Items(getScope(c).ID).ListLoose(c.Request.Context(), fromTime, toTime)
  61. case "scheduled":
  62. return db.Items(getScope(c).ID).ListScheduled(c.Request.Context(), fromDate, toDate)
  63. default:
  64. return nil, slerrors.BadRequest("unknown mode")
  65. }
  66. }))
  67. g.GET("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
  68. id, err := reqInt(c, "id")
  69. if err != nil {
  70. return nil, err
  71. }
  72. return db.Items(getScope(c).ID).Find(c.Request.Context(), id)
  73. }))
  74. g.POST("/", handler("item", func(c *gin.Context) (interface{}, error) {
  75. item := &models.Item{}
  76. err := c.BindJSON(item)
  77. if err != nil {
  78. return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
  79. }
  80. item.CreatedTime = time.Now()
  81. if item.Name == "" {
  82. return nil, slerrors.BadRequest("Blank item name not allowed")
  83. }
  84. if item.OwnerID == "" {
  85. item.OwnerID = auth.UserID(c)
  86. } else if getScope(c).HasMember(item.OwnerID) {
  87. return nil, slerrors.Forbidden("Owner is not part of scope.")
  88. }
  89. for _, stat := range item.Stats {
  90. if getScope(c).Stat(stat.ID) == nil {
  91. return nil, slerrors.Forbidden("One or more stats are not part of scope.")
  92. }
  93. if stat.Acquired == 0 && stat.Required == 0 {
  94. return nil, slerrors.BadRequest("0/0 stats are not allowed when creating items.")
  95. }
  96. }
  97. return db.Items(getScope(c).ID).Create(c.Request.Context(), *item)
  98. }))
  99. g.PUT("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
  100. id, err := reqInt(c, "id")
  101. if err != nil {
  102. return nil, err
  103. }
  104. update := &models.ItemUpdate{}
  105. err = c.BindJSON(update)
  106. if err != nil {
  107. return nil, slerrors.BadRequest("Invalid JSON input: " + err.Error())
  108. }
  109. item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id)
  110. if err != nil {
  111. return nil, err
  112. }
  113. if update.Name != nil && *update.Name == "" {
  114. return nil, slerrors.BadRequest("Blank item name not allowed")
  115. }
  116. if update.OwnerID != nil || !getScope(c).HasMember(item.OwnerID) {
  117. return nil, slerrors.Forbidden("New item is not part of scope.")
  118. }
  119. for _, stat := range update.Stats {
  120. statRef := getScope(c).Stat(stat.ID)
  121. if stat.Required != 0 && stat.Acquired != 0 && statRef == nil {
  122. return nil, slerrors.Forbidden("One or more stats are not part of the scope.")
  123. }
  124. if !statRef.AllowsAmount(stat.Required) {
  125. return nil, slerrors.Forbidden("One or more stats have a disallowed required amount.")
  126. }
  127. }
  128. return db.Items(getScope(c).ID).Update(c.Request.Context(), *item, *update)
  129. }))
  130. g.DELETE("/:id", handler("item", func(c *gin.Context) (interface{}, error) {
  131. id, err := reqInt(c, "id")
  132. if err != nil {
  133. return nil, err
  134. }
  135. item, err := db.Items(getScope(c).ID).Find(c.Request.Context(), id)
  136. if err != nil {
  137. return nil, err
  138. }
  139. err = db.Items(getScope(c).ID).Delete(c.Request.Context(), *item)
  140. if err != nil {
  141. return nil, err
  142. }
  143. return item, nil
  144. }))
  145. }