maplocation / internal /api /dashboard_ws.go
sarveshpatel's picture
Upload 28 files
58407a4 verified
Raw
History Blame Contribute Delete
1.98 kB
package api
import (
"encoding/json"
"log/slog"
"net/http"
"github.com/coder/websocket"
"gpsbackend/internal/hub"
"gpsbackend/internal/model"
)
// handleDashboardWS upgrades the connection to a WebSocket, immediately sends a
// snapshot of every device's last-known position, then hands the connection to
// the hub to receive live updates.
func (s *Server) handleDashboardWS(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
// LAN/localhost POC: accept any origin. Lock this down for production.
InsecureSkipVerify: true,
})
if err != nil {
slog.Warn("websocket accept failed", "err", err)
return
}
defer func() { _ = conn.CloseNow() }()
client := hub.NewClient(conn)
// Send the snapshot before registering so a broadcast can't slip in ahead
// of the initial state.
if snapshot, err := s.buildSnapshot(r); err == nil {
if err := client.SendNow(r.Context(), snapshot); err != nil {
slog.Debug("snapshot send failed", "err", err)
return
}
}
s.hub.Register(client)
defer s.hub.Unregister(client)
client.Run(r.Context()) // blocks until the dashboard disconnects
}
// buildSnapshot serialises every device's latest fix into a SnapshotMessage.
func (s *Server) buildSnapshot(r *http.Request) ([]byte, error) {
devices, err := s.store.ListDevices(r.Context())
if err != nil {
return nil, err
}
snap := model.SnapshotMessage{Type: "snapshot", Devices: []model.BroadcastMessage{}}
for _, d := range devices {
if d.Last == nil {
continue
}
snap.Devices = append(snap.Devices, model.BroadcastMessage{
Type: "location",
DeviceID: d.ID,
DeviceName: d.Name,
Lat: d.Last.Lat,
Lng: d.Last.Lng,
Accuracy: d.Last.Accuracy,
Speed: d.Last.Speed,
Bearing: d.Last.Bearing,
Timestamp: model.MillisToRFC3339(d.Last.Timestamp),
ReceivedAt: model.MillisToRFC3339(d.Last.ReceivedAt),
})
}
return json.Marshal(snap)
}