The main server, and probably only repository in this org.
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.

195 lines
5.1 KiB

  1. package controllers
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "net/http"
  6. "strconv"
  7. "git.aiterp.net/lucifer/lucifer/light"
  8. "git.aiterp.net/lucifer/lucifer/internal/httperr"
  9. "git.aiterp.net/lucifer/lucifer/internal/respond"
  10. "git.aiterp.net/lucifer/lucifer/models"
  11. "github.com/gorilla/mux"
  12. "golang.org/x/sync/errgroup"
  13. )
  14. // The LightController is a controller for light matters.
  15. type LightController struct {
  16. service *light.Service
  17. groups models.GroupRepository
  18. users models.UserRepository
  19. lights models.LightRepository
  20. }
  21. // getLights (`GET /:id`): Get user by id
  22. func (c *LightController) getLights(w http.ResponseWriter, r *http.Request) {
  23. user := models.UserFromContext(r.Context())
  24. groups, err := c.groups.ListByUser(r.Context(), *user)
  25. if err != nil {
  26. httperr.Respond(w, err)
  27. return
  28. }
  29. allLights := make([]models.Light, 0, len(groups)*8)
  30. eg, egCtx := errgroup.WithContext(r.Context())
  31. for i := range groups {
  32. group := groups[i]
  33. eg.Go(func() error {
  34. lights, err := c.lights.ListByGroup(egCtx, group)
  35. if err != nil {
  36. return err
  37. }
  38. allLights = append(allLights, lights...)
  39. return nil
  40. })
  41. }
  42. if err := eg.Wait(); err != nil {
  43. httperr.Respond(w, err)
  44. return
  45. }
  46. respond.Data(w, allLights)
  47. }
  48. func (c *LightController) getLight(w http.ResponseWriter, r *http.Request) {
  49. _, light, err := c.findLight(r)
  50. if err != nil {
  51. httperr.Respond(w, err)
  52. return
  53. }
  54. respond.Data(w, light)
  55. }
  56. func (c *LightController) updateLight(w http.ResponseWriter, r *http.Request) {
  57. patch := struct {
  58. Color *string `json:"color,omitempty"`
  59. Brightness *int `json:"brightness,omitempty"`
  60. On *bool `json:"on,omitempty"`
  61. Name *string `json:"name,omitempty"`
  62. GroupID *int `json:"groupId,omitempty"`
  63. }{}
  64. if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
  65. respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.")
  66. return
  67. }
  68. group, light, err := c.findLight(r)
  69. if err != nil {
  70. httperr.Respond(w, err)
  71. return
  72. }
  73. if patch.Color != nil {
  74. err := light.SetColor(*patch.Color)
  75. if err != nil {
  76. httperr.Respond(w, err)
  77. return
  78. }
  79. }
  80. if patch.Name != nil {
  81. if len(*patch.Name) == 0 {
  82. respond.Error(w, 400, "invalid_name", "The name is empty.")
  83. return
  84. }
  85. light.Name = *patch.Name
  86. }
  87. if patch.Brightness != nil {
  88. if *patch.Brightness < 0 || *patch.Brightness > 255 {
  89. respond.Error(w, 400, "invalid_brightness", "The brightness must be a value between 0-255 inclusive.")
  90. return
  91. }
  92. light.Brightness = uint8(*patch.Brightness)
  93. }
  94. if patch.On != nil {
  95. light.On = *patch.On
  96. }
  97. if patch.GroupID != nil && *patch.GroupID != light.GroupID {
  98. user := models.UserFromContext(r.Context())
  99. if !group.Permission(user.ID).Delete {
  100. respond.Error(w, 403, "cannot_move_out", "You are not permitted to delete lights from group.")
  101. return
  102. }
  103. // Anyone is allowed to move lights TO group 0 (Lonely Lights) as it's the closest thing there is
  104. // to deleting lights.
  105. if *patch.GroupID != 0 {
  106. targetGroup, err := c.groups.FindByID(r.Context(), *patch.GroupID)
  107. if err != nil {
  108. respond.Error(w, 404, "group_not_found", "The group could not be found.")
  109. return
  110. }
  111. if !targetGroup.Permission(user.ID).Create {
  112. respond.Error(w, 403, "cannot_move_in", "You are not permitted to create lights in target group.")
  113. return
  114. }
  115. }
  116. light.GroupID = *patch.GroupID
  117. }
  118. err = c.service.UpdateLight(r.Context(), light)
  119. if err != nil {
  120. httperr.Respond(w, err)
  121. return
  122. }
  123. respond.Data(w, light)
  124. }
  125. // Mount mounts the controller
  126. func (c *LightController) Mount(router *mux.Router, prefix string) {
  127. sub := router.PathPrefix(prefix).Subrouter()
  128. sub.HandleFunc("/", c.getLights).Methods("GET")
  129. sub.HandleFunc("/{id}", c.getLight).Methods("GET")
  130. sub.HandleFunc("/{id}", c.updateLight).Methods("PATCH", "PUT")
  131. }
  132. func (c *LightController) findLight(r *http.Request) (models.Group, models.Light, error) {
  133. user := models.UserFromContext(r.Context())
  134. idStr := mux.Vars(r)["id"]
  135. id, err := strconv.ParseInt(idStr, 10, 32)
  136. if err != nil {
  137. return models.Group{}, models.Light{}, &httperr.Error{Status: http.StatusForbidden, Kind: "invalid_id", Message: "The light id " + idStr + " is not valid."}
  138. }
  139. light, err := c.lights.FindByID(r.Context(), int(id))
  140. if err == sql.ErrNoRows {
  141. return models.Group{}, models.Light{}, httperr.NotFound("Light")
  142. } else if err != nil {
  143. return models.Group{}, models.Light{}, err
  144. }
  145. group, err := c.groups.FindByID(r.Context(), light.GroupID)
  146. if err == sql.ErrNoRows {
  147. return models.Group{}, models.Light{}, httperr.NotFound("Group")
  148. } else if err != nil {
  149. return models.Group{}, models.Light{}, err
  150. }
  151. if !group.Permission(user.ID).Read {
  152. return models.Group{}, models.Light{}, &httperr.Error{Status: http.StatusForbidden, Kind: "permission_denied", Message: "Thou canst not see the light."}
  153. }
  154. return group, light, nil
  155. }
  156. // NewLightController creates a new LightController.
  157. func NewLightController(service *light.Service, groups models.GroupRepository, users models.UserRepository, lights models.LightRepository) *LightController {
  158. return &LightController{service: service, groups: groups, users: users, lights: lights}
  159. }