25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

396 lines
10 KiB

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "log"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/AFASystems/presence/internal/pkg/config"
  11. "github.com/AFASystems/presence/internal/pkg/kafkaclient"
  12. "github.com/AFASystems/presence/internal/pkg/model"
  13. "github.com/gorilla/handlers"
  14. "github.com/gorilla/mux"
  15. "github.com/gorilla/websocket"
  16. "github.com/redis/go-redis/v9"
  17. "github.com/segmentio/kafka-go"
  18. )
  19. var upgrader = websocket.Upgrader{
  20. CheckOrigin: func(r *http.Request) bool { return true },
  21. }
  22. func main() {
  23. HttpServer("0.0.0.0:1902")
  24. }
  25. func HttpServer(addr string) {
  26. cfg := config.Load()
  27. headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
  28. originsOk := handlers.AllowedOrigins([]string{"*"})
  29. methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"})
  30. // Kafka writer that relays messages
  31. writer := kafkaclient.KafkaWriter(cfg.KafkaURL, "apibeacons")
  32. defer writer.Close()
  33. settingsWriter := kafkaclient.KafkaWriter(cfg.KafkaURL, "settings")
  34. defer settingsWriter.Close()
  35. // Kafka reader for Raw MQTT beacons
  36. locationReader := kafkaclient.KafkaReader(cfg.KafkaURL, "locevents", "gid-loc-serv")
  37. defer locationReader.Close()
  38. // Kafka reader for API server updates
  39. alertsReader := kafkaclient.KafkaReader(cfg.KafkaURL, "alertbeacons", "gid-alert-serv")
  40. defer alertsReader.Close()
  41. client := redis.NewClient(&redis.Options{
  42. Addr: "127.0.0.1:6379",
  43. Password: "",
  44. })
  45. ctx := context.Background()
  46. // Separate channel for location change?
  47. chLoc := make(chan model.HTTPLocation, 200)
  48. chEvents := make(chan model.BeaconEvent, 500)
  49. go kafkaclient.Consume(locationReader, chLoc)
  50. go kafkaclient.Consume(alertsReader, chEvents)
  51. go func() {
  52. for {
  53. select {
  54. case msg := <-chLoc:
  55. key := fmt.Sprintf("beacon:%s", msg.ID)
  56. hashM, err := msg.RedisHashable()
  57. if err != nil {
  58. fmt.Println("Error in converting location into hashmap for Redis insert: ", err)
  59. continue
  60. }
  61. err = client.HSet(ctx, key, hashM).Err()
  62. if err != nil {
  63. fmt.Println("Error in persisting set in Redis key: ", key)
  64. continue
  65. }
  66. case msg := <-chEvents:
  67. key := fmt.Sprintf("beacon:%s", msg.ID)
  68. hashM, err := msg.RedisHashable()
  69. if err != nil {
  70. fmt.Println("Error in converting location into hashmap for Redis insert: ", err)
  71. continue
  72. }
  73. err = client.HSet(ctx, key, hashM).Err()
  74. if err != nil {
  75. fmt.Println("Error in persisting set in Redis key: ", key)
  76. continue
  77. }
  78. }
  79. }
  80. }()
  81. r := mux.NewRouter()
  82. // declare WS clients list | do I need it though? or will locations worker send message
  83. // to kafka and then only this service (server) is being used for communication with the clients
  84. clients := make(map[*websocket.Conn]bool)
  85. // Declare broadcast channel
  86. broadcast := make(chan model.Message)
  87. // For now just add beacon DELETE / GET / POST / PUT methods
  88. r.HandleFunc("/api/beacons/{beacon_id}", beaconsDeleteHandler(writer)).Methods("DELETE")
  89. r.HandleFunc("/api/beacons/{beacon_id}", beaconsListHandler(ctx, client)).Methods("GET")
  90. r.HandleFunc("/api/beacons", beaconsAddHandler(writer)).Methods("POST")
  91. r.HandleFunc("/api/beacons", beaconsAddHandler(writer)).Methods("PUT")
  92. // r.HandleFunc("/api/settings", settingsListHandler(client)).Methods("GET")
  93. r.HandleFunc("/api/settings", settingsEditHandler(settingsWriter)).Methods("POST")
  94. // Handler for WS messages
  95. // No point in having seperate route for each message type, better to handle different message types in one connection
  96. // r.HandleFunc("/ws/api/beacons", serveWs(client))
  97. // r.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs(client))
  98. r.HandleFunc("/ws/broadcast", handleConnections(clients, broadcast))
  99. http.ListenAndServe(addr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
  100. }
  101. func beaconsListHandler(ctx context.Context, client *redis.Client) http.HandlerFunc {
  102. return func(w http.ResponseWriter, r *http.Request) {
  103. vars := mux.Vars(r)
  104. id := vars["beacon_id"]
  105. key := fmt.Sprintf("beacon:%s", id)
  106. beacon, err := client.HGetAll(ctx, key).Result()
  107. if err != nil {
  108. res := fmt.Sprintf("Error in getting beacon data (key: %s), error: %v", key, err)
  109. fmt.Println(res)
  110. http.Error(w, res, 500)
  111. }
  112. fmt.Printf("%+v", beacon)
  113. rData, err := json.Marshal(beacon)
  114. if err != nil {
  115. res := fmt.Sprintf("Error in marshaling beacon data (key: %s), error: %v", key, err)
  116. fmt.Println(res)
  117. http.Error(w, res, 500)
  118. }
  119. w.Write(rData)
  120. }
  121. }
  122. // Probably define value as interface and then reuse this writer in all of the functions
  123. func sendKafkaMessage(writer *kafka.Writer, value *model.ApiUpdate) bool {
  124. valueStr, err := json.Marshal(&value)
  125. if err != nil {
  126. fmt.Println("error in encoding: ", err)
  127. return false
  128. }
  129. msg := kafka.Message{
  130. Value: valueStr,
  131. }
  132. err = writer.WriteMessages(context.Background(), msg)
  133. if err != nil {
  134. fmt.Println("Error in sending kafka message: ")
  135. return false
  136. }
  137. return true
  138. }
  139. func beaconsDeleteHandler(writer *kafka.Writer) http.HandlerFunc {
  140. return func(w http.ResponseWriter, r *http.Request) {
  141. vars := mux.Vars(r)
  142. beaconId := vars["beacon_id"]
  143. apiUpdate := model.ApiUpdate{
  144. Method: "DELETE",
  145. ID: beaconId,
  146. }
  147. fmt.Println("Sending DELETE message")
  148. flag := sendKafkaMessage(writer, &apiUpdate)
  149. if !flag {
  150. fmt.Println("error in sending Kafka message")
  151. http.Error(w, "Error in sending kafka message", 500)
  152. return
  153. }
  154. w.Write([]byte("ok"))
  155. }
  156. }
  157. func beaconsAddHandler(writer *kafka.Writer) http.HandlerFunc {
  158. return func(w http.ResponseWriter, r *http.Request) {
  159. decoder := json.NewDecoder(r.Body)
  160. var inBeacon model.Beacon
  161. err := decoder.Decode(&inBeacon)
  162. if err != nil {
  163. http.Error(w, err.Error(), 400)
  164. return
  165. }
  166. fmt.Println("sending POST message")
  167. if (len(strings.TrimSpace(inBeacon.Name)) == 0) || (len(strings.TrimSpace(inBeacon.ID)) == 0) {
  168. http.Error(w, "name and beacon_id cannot be blank", 400)
  169. return
  170. }
  171. apiUpdate := model.ApiUpdate{
  172. Method: "POST",
  173. Beacon: inBeacon,
  174. }
  175. flag := sendKafkaMessage(writer, &apiUpdate)
  176. if !flag {
  177. fmt.Println("error in sending Kafka message")
  178. http.Error(w, "Error in sending kafka message", 500)
  179. return
  180. }
  181. w.Write([]byte("ok"))
  182. }
  183. }
  184. func settingsEditHandler(writer *kafka.Writer) http.HandlerFunc {
  185. return func(w http.ResponseWriter, r *http.Request) {
  186. decoder := json.NewDecoder(r.Body)
  187. var inSettings model.SettingsVal
  188. if err := decoder.Decode(&inSettings); err != nil {
  189. http.Error(w, err.Error(), 400)
  190. fmt.Println("Error in decoding Settings body: ", err)
  191. return
  192. }
  193. if !settingsCheck(inSettings) {
  194. http.Error(w, "values must be greater than 0", 400)
  195. fmt.Println("settings values must be greater than 0")
  196. return
  197. }
  198. valueStr, err := json.Marshal(&inSettings)
  199. if err != nil {
  200. http.Error(w, "Error in encoding settings", 500)
  201. fmt.Println("Error in encoding settings: ", err)
  202. return
  203. }
  204. msg := kafka.Message{
  205. Value: valueStr,
  206. }
  207. if err := writer.WriteMessages(context.Background(), msg); err != nil {
  208. fmt.Println("error in sending Kafka message")
  209. http.Error(w, "Error in sending kafka message", 500)
  210. return
  211. }
  212. w.Write([]byte("ok"))
  213. }
  214. }
  215. func settingsCheck(settings model.SettingsVal) bool {
  216. if settings.LocationConfidence <= 0 || settings.LastSeenThreshold <= 0 || settings.HASendInterval <= 0 {
  217. return false
  218. }
  219. return true
  220. }
  221. func serveWs(client *redis.Client) http.HandlerFunc {
  222. return func(w http.ResponseWriter, r *http.Request) {
  223. ws, err := upgrader.Upgrade(w, r, nil)
  224. if err != nil {
  225. if _, ok := err.(websocket.HandshakeError); !ok {
  226. log.Println(err)
  227. }
  228. return
  229. }
  230. go writer(ws, client)
  231. reader(ws)
  232. }
  233. }
  234. func writer(ws *websocket.Conn, client *redis.Client) {
  235. pingTicker := time.NewTicker((60 * time.Second * 9) / 10)
  236. beaconTicker := time.NewTicker(2 * time.Second)
  237. defer func() {
  238. pingTicker.Stop()
  239. beaconTicker.Stop()
  240. ws.Close()
  241. }()
  242. for {
  243. select {
  244. case <-beaconTicker.C:
  245. httpresults, err := client.Get(context.Background(), "httpresults").Result()
  246. if err == redis.Nil {
  247. fmt.Println("no beacons list, starting empty")
  248. } else if err != nil {
  249. panic(err)
  250. } else {
  251. ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
  252. if err := ws.WriteMessage(websocket.TextMessage, []byte(httpresults)); err != nil {
  253. return
  254. }
  255. }
  256. case <-pingTicker.C:
  257. ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
  258. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  259. return
  260. }
  261. }
  262. }
  263. }
  264. func serveLatestBeaconsWs(client *redis.Client) http.HandlerFunc {
  265. return func(w http.ResponseWriter, r *http.Request) {
  266. ws, err := upgrader.Upgrade(w, r, nil)
  267. if err != nil {
  268. if _, ok := err.(websocket.HandshakeError); !ok {
  269. log.Println(err)
  270. }
  271. return
  272. }
  273. go latestBeaconWriter(ws, client)
  274. reader(ws)
  275. }
  276. }
  277. // This and writer can be refactored in one function
  278. func latestBeaconWriter(ws *websocket.Conn, client *redis.Client) {
  279. pingTicker := time.NewTicker((60 * time.Second * 9) / 10)
  280. beaconTicker := time.NewTicker(2 * time.Second)
  281. defer func() {
  282. pingTicker.Stop()
  283. beaconTicker.Stop()
  284. ws.Close()
  285. }()
  286. for {
  287. select {
  288. case <-beaconTicker.C:
  289. latestbeacons, err := client.Get(context.Background(), "latestbeacons").Result()
  290. if err == redis.Nil {
  291. fmt.Println("no beacons list, starting empty")
  292. } else if err != nil {
  293. panic(err)
  294. } else {
  295. ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
  296. if err := ws.WriteMessage(websocket.TextMessage, []byte(latestbeacons)); err != nil {
  297. return
  298. }
  299. }
  300. case <-pingTicker.C:
  301. ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
  302. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  303. return
  304. }
  305. }
  306. }
  307. }
  308. func reader(ws *websocket.Conn) {
  309. defer ws.Close()
  310. ws.SetReadLimit(512)
  311. ws.SetReadDeadline(time.Now().Add(60 * time.Second))
  312. ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(60 * time.Second)); return nil })
  313. for {
  314. _, _, err := ws.ReadMessage()
  315. if err != nil {
  316. break
  317. }
  318. }
  319. }
  320. func handleConnections(clients map[*websocket.Conn]bool, broadcast chan model.Message) http.HandlerFunc {
  321. return func(w http.ResponseWriter, r *http.Request) {
  322. ws, err := upgrader.Upgrade(w, r, nil)
  323. if err != nil {
  324. log.Fatal(err)
  325. }
  326. defer ws.Close()
  327. clients[ws] = true
  328. for {
  329. var msg model.Message
  330. err := ws.ReadJSON(&msg)
  331. if err != nil {
  332. log.Printf("error: %v", err)
  333. delete(clients, ws)
  334. break
  335. }
  336. broadcast <- msg
  337. }
  338. }
  339. }