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.

277 lines
7.4 KiB

  1. package controllers
  2. import (
  3. "context"
  4. "database/sql"
  5. "encoding/json"
  6. "errors"
  7. "net/http"
  8. "strconv"
  9. "git.aiterp.net/lucifer/lucifer/internal/httperr"
  10. "git.aiterp.net/lucifer/lucifer/internal/respond"
  11. "git.aiterp.net/lucifer/lucifer/light"
  12. "git.aiterp.net/lucifer/lucifer/models"
  13. "github.com/gorilla/mux"
  14. )
  15. // The BridgeController is a controller for all bridge things.
  16. type BridgeController struct {
  17. service *light.Service
  18. groups models.GroupRepository
  19. }
  20. // getBridges (`GET /`): Get all bridges
  21. func (c *BridgeController) getBridges(w http.ResponseWriter, r *http.Request) {
  22. bridges, err := c.service.Bridges(r.Context())
  23. if err != nil {
  24. httperr.Respond(w, err)
  25. return
  26. }
  27. respond.Data(w, bridges)
  28. }
  29. // getBridge (`GET /:bridge_id`): Get bridge by ID
  30. func (c *BridgeController) getBridge(w http.ResponseWriter, r *http.Request) {
  31. idStr := mux.Vars(r)["bridge_id"]
  32. id, err := strconv.ParseInt(idStr, 10, 32)
  33. if err != nil {
  34. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
  35. return
  36. }
  37. bridge, err := c.service.Bridge(r.Context(), int(id))
  38. if err == sql.ErrNoRows {
  39. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  40. return
  41. } else if err != nil {
  42. httperr.Respond(w, err)
  43. return
  44. }
  45. respond.Data(w, bridge)
  46. }
  47. // postBridge (`POST /`): Post bridge
  48. func (c *BridgeController) postBridge(w http.ResponseWriter, r *http.Request) {
  49. postData := struct {
  50. Name string `json:"name"`
  51. Driver string `json:"driver"`
  52. Addr string `json:"addr"`
  53. }{}
  54. if err := json.NewDecoder(r.Body).Decode(&postData); err != nil || postData.Name == "" || postData.Driver == "" || postData.Addr == "" {
  55. respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.")
  56. return
  57. }
  58. bridge, err := c.service.DirectConnect(r.Context(), postData.Driver, postData.Addr, postData.Name)
  59. if err != nil {
  60. httperr.Respond(w, err)
  61. return
  62. }
  63. respond.Data(w, bridge)
  64. }
  65. // updateBridge (`PUT/PATCH /:bridge_id`): Update bridge by ID
  66. func (c *BridgeController) updateBridge(w http.ResponseWriter, r *http.Request) {
  67. idStr := mux.Vars(r)["bridge_id"]
  68. id, err := strconv.ParseInt(idStr, 10, 32)
  69. if err != nil {
  70. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
  71. return
  72. }
  73. putData := struct {
  74. Name string
  75. }{}
  76. if err := json.NewDecoder(r.Body).Decode(&putData); err != nil {
  77. respond.Error(w, http.StatusBadRequest, "invalid_json", "Input is not valid JSON.")
  78. return
  79. }
  80. if putData.Name == "" {
  81. respond.Error(w, http.StatusBadRequest, "invalid_name", "The name cannot be blank.")
  82. return
  83. }
  84. bridge, err := c.service.Bridge(r.Context(), int(id))
  85. if err == sql.ErrNoRows {
  86. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  87. return
  88. } else if err != nil {
  89. httperr.Respond(w, err)
  90. return
  91. }
  92. bridge.Name = putData.Name
  93. err = c.service.UpdateBridge(r.Context(), bridge)
  94. if err != nil {
  95. httperr.Respond(w, err)
  96. return
  97. }
  98. respond.Data(w, bridge)
  99. }
  100. // postBridgeDiscover (`POST /:bridge_id/discover`): Delete bridge by ID
  101. func (c *BridgeController) postBridgeDiscover(w http.ResponseWriter, r *http.Request) {
  102. idStr := mux.Vars(r)["bridge_id"]
  103. id, err := strconv.ParseInt(idStr, 10, 32)
  104. if err != nil {
  105. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
  106. return
  107. }
  108. bridge, err := c.service.Bridge(r.Context(), int(id))
  109. if err == sql.ErrNoRows {
  110. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  111. return
  112. } else if err != nil {
  113. httperr.Respond(w, err)
  114. return
  115. }
  116. err = c.service.DiscoverLights(r.Context(), bridge)
  117. if err != nil {
  118. httperr.Respond(w, err)
  119. return
  120. }
  121. respond.Data(w, bridge)
  122. }
  123. // deleteBridge (`DELETE /:bridge_id`): Delete bridge by ID
  124. func (c *BridgeController) deleteBridge(w http.ResponseWriter, r *http.Request) {
  125. idStr := mux.Vars(r)["bridge_id"]
  126. id, err := strconv.ParseInt(idStr, 10, 32)
  127. if err != nil {
  128. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
  129. return
  130. }
  131. bridge, err := c.service.Bridge(r.Context(), int(id))
  132. if err == sql.ErrNoRows {
  133. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  134. return
  135. } else if err != nil {
  136. httperr.Respond(w, err)
  137. return
  138. }
  139. err = c.service.DeleteBridge(r.Context(), bridge)
  140. if err != nil {
  141. httperr.Respond(w, err)
  142. return
  143. }
  144. respond.Data(w, bridge)
  145. }
  146. // deleteBridgeLight (`DELETE /:bridge_id/light/:light_id`): Delete bridge by ID
  147. func (c *BridgeController) deleteBridgeLight(w http.ResponseWriter, r *http.Request) {
  148. idStr := mux.Vars(r)["bridge_id"]
  149. id, err := strconv.ParseInt(idStr, 10, 32)
  150. if err != nil {
  151. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
  152. return
  153. }
  154. lightIDStr := mux.Vars(r)["light_id"]
  155. lightID, err := strconv.ParseInt(lightIDStr, 10, 32)
  156. if err != nil {
  157. respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+lightIDStr+" is not valid.")
  158. return
  159. }
  160. bridge, err := c.service.Bridge(r.Context(), int(id))
  161. if err == sql.ErrNoRows {
  162. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  163. return
  164. } else if err != nil {
  165. httperr.Respond(w, err)
  166. return
  167. }
  168. light, err := c.service.Light(r.Context(), int(lightID))
  169. if err != nil {
  170. httperr.Respond(w, err)
  171. return
  172. }
  173. if light.BridgeID != bridge.ID {
  174. respond.Error(w, 404, "bridge_not_found", "The bridge cannot be found.")
  175. return
  176. }
  177. err = c.service.DeleteBridgeLight(r.Context(), bridge, light)
  178. if err != nil {
  179. httperr.Respond(w, err)
  180. return
  181. }
  182. respond.Data(w, bridge)
  183. }
  184. // discoverBridges (`GET /discover/:driver`): Get all bridges
  185. func (c *BridgeController) discoverBridges(w http.ResponseWriter, r *http.Request) {
  186. newBridges, err := c.service.DiscoverBridges(r.Context(), mux.Vars(r)["driver_name"])
  187. if err != nil {
  188. httperr.Respond(w, err)
  189. return
  190. }
  191. respond.Data(w, newBridges)
  192. }
  193. // Mount mounts the controller
  194. func (c *BridgeController) Mount(router *mux.Router, prefix string) {
  195. sub := router.PathPrefix(prefix).Subrouter()
  196. sub.Use(c.adminMiddleware())
  197. sub.HandleFunc("/", c.getBridges).Methods("GET")
  198. sub.HandleFunc("/", c.postBridge).Methods("POST")
  199. sub.HandleFunc("/{bridge_id}", c.getBridge).Methods("GET")
  200. sub.HandleFunc("/{bridge_id}", c.updateBridge).Methods("PUT", "PATCH")
  201. sub.HandleFunc("/{bridge_id}", c.deleteBridge).Methods("DELETE")
  202. sub.HandleFunc("/{bridge_id}/light/{light_id}", c.deleteBridgeLight).Methods("DELETE")
  203. sub.HandleFunc("/{bridge_id}/discover", c.postBridgeDiscover).Methods("POST")
  204. sub.HandleFunc("/discover/{driver_name}", c.discoverBridges).Methods("GET")
  205. }
  206. func (c *BridgeController) adminMiddleware() mux.MiddlewareFunc {
  207. return func(next http.Handler) http.Handler {
  208. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  209. err := c.isAdmin(r.Context())
  210. if err != nil {
  211. httperr.Respond(w, err)
  212. return
  213. }
  214. next.ServeHTTP(w, r)
  215. })
  216. }
  217. }
  218. func (c *BridgeController) isAdmin(ctx context.Context) error {
  219. user := models.UserFromContext(ctx)
  220. lonelyLights, err := c.groups.FindByID(ctx, 0)
  221. if err != nil {
  222. return errors.New("Lonely Lights group could not be found")
  223. }
  224. if !lonelyLights.Permission(user.ID).Write {
  225. return httperr.ErrAccessDenied
  226. }
  227. return nil
  228. }
  229. // NewBridgeController makes a new bridge controller
  230. func NewBridgeController(service *light.Service, groups models.GroupRepository) *BridgeController {
  231. return &BridgeController{
  232. service: service,
  233. groups: groups,
  234. }
  235. }