|
|
package controllers
import ( "database/sql" "encoding/json" "net/http" "strconv"
"git.aiterp.net/lucifer/lucifer/light"
"git.aiterp.net/lucifer/lucifer/internal/httperr"
"git.aiterp.net/lucifer/lucifer/internal/respond" "git.aiterp.net/lucifer/lucifer/models" "github.com/gorilla/mux" "golang.org/x/sync/errgroup" )
// The LightController is a controller for light matters.
type LightController struct { service *light.Service groups models.GroupRepository users models.UserRepository lights models.LightRepository }
// getLights (`GET /:id`): Get user by id
func (c *LightController) getLights(w http.ResponseWriter, r *http.Request) { user := models.UserFromContext(r.Context())
groups, err := c.groups.ListByUser(r.Context(), *user) if err != nil { httperr.Respond(w, err) return }
allLights := make([]models.Light, 0, len(groups)*8)
eg, egCtx := errgroup.WithContext(r.Context()) for i := range groups { group := groups[i]
eg.Go(func() error { lights, err := c.lights.ListByGroup(egCtx, group) if err != nil { return err }
allLights = append(allLights, lights...)
return nil }) }
if err := eg.Wait(); err != nil { httperr.Respond(w, err) return }
respond.Data(w, allLights) }
func (c *LightController) getLight(w http.ResponseWriter, r *http.Request) { _, light, err := c.findLight(r) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, light) }
func (c *LightController) updateLight(w http.ResponseWriter, r *http.Request) { patch := struct { Color *string `json:"color,omitempty"` Brightness *int `json:"brightness,omitempty"` On *bool `json:"on,omitempty"` Name *string `json:"name,omitempty"` GroupID *int `json:"groupId,omitempty"` }{}
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil { respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.") return }
group, light, err := c.findLight(r) if err != nil { httperr.Respond(w, err) return }
if patch.Color != nil { err := light.SetColor(*patch.Color) if err != nil { httperr.Respond(w, err) return } } if patch.Name != nil { if len(*patch.Name) == 0 { respond.Error(w, 400, "invalid_name", "The name is empty.") return }
light.Name = *patch.Name } if patch.Brightness != nil { if *patch.Brightness < 0 || *patch.Brightness > 255 { respond.Error(w, 400, "invalid_brightness", "The brightness must be a value between 0-255 inclusive.") return }
light.Brightness = uint8(*patch.Brightness) } if patch.On != nil { light.On = *patch.On } if patch.GroupID != nil && *patch.GroupID != light.GroupID { user := models.UserFromContext(r.Context())
if !group.Permission(user.ID).Delete { respond.Error(w, 403, "cannot_move_out", "You are not permitted to delete lights from group.") return }
// Anyone is allowed to move lights TO group 0 (Lonely Lights) as it's the closest thing there is
// to deleting lights.
if *patch.GroupID != 0 { targetGroup, err := c.groups.FindByID(r.Context(), *patch.GroupID) if err != nil { respond.Error(w, 404, "group_not_found", "The group could not be found.") return } if !targetGroup.Permission(user.ID).Create { respond.Error(w, 403, "cannot_move_in", "You are not permitted to create lights in target group.") return } }
light.GroupID = *patch.GroupID }
err = c.service.UpdateLight(r.Context(), light) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, light) }
// Mount mounts the controller
func (c *LightController) Mount(router *mux.Router, prefix string) { sub := router.PathPrefix(prefix).Subrouter()
sub.HandleFunc("/", c.getLights).Methods("GET") sub.HandleFunc("/{id}", c.getLight).Methods("GET") sub.HandleFunc("/{id}", c.updateLight).Methods("PATCH", "PUT") }
func (c *LightController) findLight(r *http.Request) (models.Group, models.Light, error) { user := models.UserFromContext(r.Context())
idStr := mux.Vars(r)["id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { return models.Group{}, models.Light{}, &httperr.Error{Status: http.StatusForbidden, Kind: "invalid_id", Message: "The light id " + idStr + " is not valid."} }
light, err := c.lights.FindByID(r.Context(), int(id)) if err == sql.ErrNoRows { return models.Group{}, models.Light{}, httperr.NotFound("Light") } else if err != nil { return models.Group{}, models.Light{}, err }
group, err := c.groups.FindByID(r.Context(), light.GroupID) if err == sql.ErrNoRows { return models.Group{}, models.Light{}, httperr.NotFound("Group") } else if err != nil { return models.Group{}, models.Light{}, err }
if !group.Permission(user.ID).Read { return models.Group{}, models.Light{}, &httperr.Error{Status: http.StatusForbidden, Kind: "permission_denied", Message: "Thou canst not see the light."} }
return group, light, nil }
// NewLightController creates a new LightController.
func NewLightController(service *light.Service, groups models.GroupRepository, users models.UserRepository, lights models.LightRepository) *LightController { return &LightController{service: service, groups: groups, users: users, lights: lights} }
|