|
|
@@ -2,11 +2,14 @@ package main |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
"context" |
|
|
"context" |
|
|
|
|
|
"encoding/json" |
|
|
"fmt" |
|
|
"fmt" |
|
|
|
|
|
"log" |
|
|
"net/http" |
|
|
"net/http" |
|
|
"os/signal" |
|
|
"os/signal" |
|
|
"sync" |
|
|
"sync" |
|
|
"syscall" |
|
|
"syscall" |
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
"github.com/AFASystems/presence/internal/pkg/common/appcontext" |
|
|
"github.com/AFASystems/presence/internal/pkg/common/appcontext" |
|
|
"github.com/AFASystems/presence/internal/pkg/config" |
|
|
"github.com/AFASystems/presence/internal/pkg/config" |
|
|
@@ -16,8 +19,14 @@ import ( |
|
|
"github.com/AFASystems/presence/internal/pkg/service" |
|
|
"github.com/AFASystems/presence/internal/pkg/service" |
|
|
"github.com/gorilla/handlers" |
|
|
"github.com/gorilla/handlers" |
|
|
"github.com/gorilla/mux" |
|
|
"github.com/gorilla/mux" |
|
|
|
|
|
"github.com/gorilla/websocket" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
var upgrader = websocket.Upgrader{ |
|
|
|
|
|
ReadBufferSize: 1024, |
|
|
|
|
|
WriteBufferSize: 1024, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
var wg sync.WaitGroup |
|
|
var wg sync.WaitGroup |
|
|
|
|
|
|
|
|
func main() { |
|
|
func main() { |
|
|
@@ -51,7 +60,7 @@ func main() { |
|
|
r := mux.NewRouter() |
|
|
r := mux.NewRouter() |
|
|
|
|
|
|
|
|
// For now just add beacon DELETE / GET / POST / PUT methods |
|
|
// For now just add beacon DELETE / GET / POST / PUT methods |
|
|
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsDeleteController(writer, ctx)).Methods("DELETE") |
|
|
|
|
|
|
|
|
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsDeleteController(writer, ctx, appState)).Methods("DELETE") |
|
|
r.HandleFunc("/api/beacons", controller.BeaconsListController(appState)).Methods("GET") |
|
|
r.HandleFunc("/api/beacons", controller.BeaconsListController(appState)).Methods("GET") |
|
|
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsListSingleController(appState)).Methods("GET") |
|
|
r.HandleFunc("/api/beacons/{beacon_id}", controller.BeaconsListSingleController(appState)).Methods("GET") |
|
|
r.HandleFunc("/api/beacons", controller.BeaconsAddController(writer, ctx)).Methods("POST") |
|
|
r.HandleFunc("/api/beacons", controller.BeaconsAddController(writer, ctx)).Methods("POST") |
|
|
@@ -60,6 +69,8 @@ func main() { |
|
|
r.HandleFunc("/api/settings", controller.SettingsListController(appState, client, ctx)).Methods("GET") |
|
|
r.HandleFunc("/api/settings", controller.SettingsListController(appState, client, ctx)).Methods("GET") |
|
|
r.HandleFunc("/api/settings", controller.SettingsEditController(settingsWriter, appState, client, ctx)).Methods("POST") |
|
|
r.HandleFunc("/api/settings", controller.SettingsEditController(settingsWriter, appState, client, ctx)).Methods("POST") |
|
|
|
|
|
|
|
|
|
|
|
r.HandleFunc("/api/beacons/ws", serveWs(appState, ctx)) |
|
|
|
|
|
|
|
|
http.ListenAndServe(cfg.HTTPAddr, handlers.CORS(originsOk, headersOk, methodsOk)(r)) |
|
|
http.ListenAndServe(cfg.HTTPAddr, handlers.CORS(originsOk, headersOk, methodsOk)(r)) |
|
|
|
|
|
|
|
|
eventLoop: |
|
|
eventLoop: |
|
|
@@ -87,3 +98,67 @@ eventLoop: |
|
|
fmt.Println("All kafka clients shutdown, starting shutdown of valkey client") |
|
|
fmt.Println("All kafka clients shutdown, starting shutdown of valkey client") |
|
|
appState.CleanValkeyClient() |
|
|
appState.CleanValkeyClient() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func serveWs(appstate *appcontext.AppState, ctx context.Context) http.HandlerFunc { |
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) { |
|
|
|
|
|
ws, err := upgrader.Upgrade(w, r, nil) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
if _, ok := err.(websocket.HandshakeError); !ok { |
|
|
|
|
|
log.Println(err) |
|
|
|
|
|
} |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
wg.Add(1) |
|
|
|
|
|
go writer(ws, appstate, ctx) |
|
|
|
|
|
reader(ws) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func writer(ws *websocket.Conn, appstate *appcontext.AppState, ctx context.Context) { |
|
|
|
|
|
pingTicker := time.NewTicker((60 * 9) / 10 * time.Second) |
|
|
|
|
|
beaconTicker := time.NewTicker(2 * time.Second) |
|
|
|
|
|
defer func() { |
|
|
|
|
|
pingTicker.Stop() |
|
|
|
|
|
beaconTicker.Stop() |
|
|
|
|
|
ws.Close() |
|
|
|
|
|
wg.Done() |
|
|
|
|
|
}() |
|
|
|
|
|
for { |
|
|
|
|
|
select { |
|
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
|
log.Println("WebSocket writer received shutdown signal.") |
|
|
|
|
|
ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) |
|
|
|
|
|
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) |
|
|
|
|
|
return |
|
|
|
|
|
case <-beaconTicker.C: |
|
|
|
|
|
beacons := appstate.GetAllBeacons() |
|
|
|
|
|
js, err := json.Marshal(beacons) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
js = []byte("error") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) |
|
|
|
|
|
if err := ws.WriteMessage(websocket.TextMessage, js); err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
case <-pingTicker.C: |
|
|
|
|
|
ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) |
|
|
|
|
|
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func reader(ws *websocket.Conn) { |
|
|
|
|
|
defer ws.Close() |
|
|
|
|
|
ws.SetReadLimit(512) |
|
|
|
|
|
ws.SetReadDeadline(time.Now().Add((60 * 9) / 10 * time.Second)) |
|
|
|
|
|
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add((60 * 9) / 10 * time.Second)); return nil }) |
|
|
|
|
|
for { |
|
|
|
|
|
_, _, err := ws.ReadMessage() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
break |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |