|
|
package controllers
import ( "context" "database/sql" "encoding/json" "errors" "net/http" "strconv"
"git.aiterp.net/lucifer/lucifer/internal/httperr" "git.aiterp.net/lucifer/lucifer/internal/respond" "git.aiterp.net/lucifer/lucifer/light" "git.aiterp.net/lucifer/lucifer/models" "github.com/gorilla/mux" )
// The BridgeController is a controller for all bridge things.
type BridgeController struct { service *light.Service groups models.GroupRepository }
// getBridges (`GET /`): Get all bridges
func (c *BridgeController) getBridges(w http.ResponseWriter, r *http.Request) { bridges, err := c.service.Bridges(r.Context()) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridges) }
// getBridge (`GET /:bridge_id`): Get bridge by ID
func (c *BridgeController) getBridge(w http.ResponseWriter, r *http.Request) { idStr := mux.Vars(r)["bridge_id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.") return }
bridge, err := c.service.Bridge(r.Context(), int(id)) if err == sql.ErrNoRows { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return } else if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// postBridge (`POST /`): Post bridge
func (c *BridgeController) postBridge(w http.ResponseWriter, r *http.Request) { postData := struct { Name string `json:"name"` Driver string `json:"driver"` Addr string `json:"addr"` }{} if err := json.NewDecoder(r.Body).Decode(&postData); err != nil || postData.Name == "" || postData.Driver == "" || postData.Addr == "" { respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.") return }
bridge, err := c.service.DirectConnect(r.Context(), postData.Driver, postData.Addr, postData.Name) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// updateBridge (`PUT/PATCH /:bridge_id`): Update bridge by ID
func (c *BridgeController) updateBridge(w http.ResponseWriter, r *http.Request) { idStr := mux.Vars(r)["bridge_id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.") return }
putData := struct { Name string }{} if err := json.NewDecoder(r.Body).Decode(&putData); err != nil { respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.") return } if putData.Name == "" { respond.Error(w, http.StatusBadRequest, "invalid_name", "The name cannot be blank.") return }
bridge, err := c.service.Bridge(r.Context(), int(id)) if err == sql.ErrNoRows { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return } else if err != nil { httperr.Respond(w, err) return }
bridge.Name = putData.Name err = c.service.UpdateBridge(r.Context(), bridge) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// postBridgeDiscover (`POST /:bridge_id/discover`): Delete bridge by ID
func (c *BridgeController) postBridgeDiscover(w http.ResponseWriter, r *http.Request) { idStr := mux.Vars(r)["bridge_id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.") return }
bridge, err := c.service.Bridge(r.Context(), int(id)) if err == sql.ErrNoRows { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return } else if err != nil { httperr.Respond(w, err) return }
err = c.service.DiscoverLights(r.Context(), bridge) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// deleteBridge (`DELETE /:bridge_id`): Delete bridge by ID
func (c *BridgeController) deleteBridge(w http.ResponseWriter, r *http.Request) { idStr := mux.Vars(r)["bridge_id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.") return }
bridge, err := c.service.Bridge(r.Context(), int(id)) if err == sql.ErrNoRows { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return } else if err != nil { httperr.Respond(w, err) return }
err = c.service.DeleteBridge(r.Context(), bridge) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// deleteBridgeLight (`DELETE /:bridge_id/light/:light_id`): Delete bridge by ID
func (c *BridgeController) deleteBridgeLight(w http.ResponseWriter, r *http.Request) { idStr := mux.Vars(r)["bridge_id"] id, err := strconv.ParseInt(idStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.") return }
lightIDStr := mux.Vars(r)["light_id"] lightID, err := strconv.ParseInt(lightIDStr, 10, 32) if err != nil { respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+lightIDStr+" is not valid.") return }
bridge, err := c.service.Bridge(r.Context(), int(id)) if err == sql.ErrNoRows { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return } else if err != nil { httperr.Respond(w, err) return }
light, err := c.service.Light(r.Context(), int(lightID)) if err != nil { httperr.Respond(w, err) return } if light.BridgeID != bridge.ID { respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.") return }
err = c.service.DeleteBridgeLight(r.Context(), bridge, light) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, bridge) }
// discoverBridges (`GET /discover/:driver`): Get all bridges
func (c *BridgeController) discoverBridges(w http.ResponseWriter, r *http.Request) { newBridges, err := c.service.DiscoverBridges(r.Context(), mux.Vars(r)["driver_name"]) if err != nil { httperr.Respond(w, err) return }
respond.Data(w, newBridges) }
// Mount mounts the controller
func (c *BridgeController) Mount(router *mux.Router, prefix string) { sub := router.PathPrefix(prefix).Subrouter()
sub.Use(c.adminMiddleware())
sub.HandleFunc("/", c.getBridges).Methods("GET") sub.HandleFunc("/", c.postBridge).Methods("POST") sub.HandleFunc("/{bridge_id}", c.getBridge).Methods("GET") sub.HandleFunc("/{bridge_id}", c.updateBridge).Methods("PUT", "PATCH") sub.HandleFunc("/{bridge_id}", c.deleteBridge).Methods("DELETE") sub.HandleFunc("/{bridge_id}/light/{light_id}", c.deleteBridgeLight).Methods("DELETE") sub.HandleFunc("/{bridge_id}/discover", c.postBridgeDiscover).Methods("POST") sub.HandleFunc("/discover/{driver_name}", c.discoverBridges).Methods("GET") }
func (c *BridgeController) adminMiddleware() mux.MiddlewareFunc { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := c.isAdmin(r.Context()) if err != nil { httperr.Respond(w, err) return }
next.ServeHTTP(w, r) }) } }
func (c *BridgeController) isAdmin(ctx context.Context) error { user := models.UserFromContext(ctx)
lonelyLights, err := c.groups.FindByID(ctx, 0) if err != nil { return errors.New("Lonely Lights group could not be found") }
if !lonelyLights.Permission(user.ID).Write { return httperr.ErrAccessDenied }
return nil }
// NewBridgeController makes a new bridge controller
func NewBridgeController(service *light.Service, groups models.GroupRepository) *BridgeController { return &BridgeController{ service: service, groups: groups, } }
|