we now got websockets baby
This commit is contained in:
120
internal/handlers/notifswebsocket.go
Normal file
120
internal/handlers/notifswebsocket.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.haelnorr.com/h/golib/cookies"
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/golib/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/config"
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"git.haelnorr.com/h/oslstats/internal/view/component/popup"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func NotificationWS(
|
||||
s *hws.Server,
|
||||
cfg *config.Config,
|
||||
) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Upgrade") != "websocket" {
|
||||
throwNotFound(s, w, r, r.URL.Path)
|
||||
return
|
||||
}
|
||||
nc, err := setupClient(s, w, r)
|
||||
if err != nil {
|
||||
s.LogError(hws.HWSError{
|
||||
Message: "Failed to get notification client",
|
||||
Error: err,
|
||||
Level: hws.ErrorERROR,
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
ws, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
OriginPatterns: []string{cfg.HWSAuth.TrustedHost},
|
||||
})
|
||||
if err != nil {
|
||||
s.LogError(hws.HWSError{
|
||||
Message: "Failed to open websocket",
|
||||
Error: err,
|
||||
Level: hws.ErrorERROR,
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
defer ws.CloseNow()
|
||||
ctx := ws.CloseRead(r.Context())
|
||||
err = notifyLoop(ctx, nc, ws)
|
||||
if err != nil {
|
||||
s.LogError(hws.HWSError{
|
||||
Message: "Notification error",
|
||||
Error: err,
|
||||
Level: hws.ErrorERROR,
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func setupClient(s *hws.Server, w http.ResponseWriter, r *http.Request) (*hws.Client, error) {
|
||||
user := db.CurrentUser(r.Context())
|
||||
altID := ""
|
||||
if user != nil {
|
||||
altID = strconv.Itoa(user.ID)
|
||||
}
|
||||
subCookie, err := r.Cookie("ws_sub_id")
|
||||
subID := ""
|
||||
if err == nil {
|
||||
subID = subCookie.Value
|
||||
}
|
||||
nc, err := s.GetClient(subID, altID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "s.GetClient")
|
||||
}
|
||||
cookies.SetCookie(w, "ws_sub_id", "/", nc.ID(), 0)
|
||||
return nc, nil
|
||||
}
|
||||
|
||||
func notifyLoop(ctx context.Context, c *hws.Client, ws *websocket.Conn) error {
|
||||
notifs, stop := c.Listen()
|
||||
defer close(stop)
|
||||
count := 0
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case nt, ok := <-notifs:
|
||||
count++
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
w, err := ws.Writer(ctx, websocket.MessageText)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "ws.Writer")
|
||||
}
|
||||
switch nt.Level {
|
||||
case hws.LevelShutdown:
|
||||
err = popup.Toast(nt, count, 30000).Render(ctx, w)
|
||||
case notify.LevelWarn:
|
||||
err = popup.Toast(nt, count, 10000).Render(ctx, w)
|
||||
case notify.LevelError:
|
||||
// do error modal
|
||||
default:
|
||||
err = popup.Toast(nt, count, 6000).Render(ctx, w)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "popup.Toast")
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "w.Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user