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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,36 +7,45 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/golib/notify"
|
||||
)
|
||||
|
||||
// Handles responses to the / path. Also serves a 404 Page for paths that
|
||||
// don't have explicit handlers
|
||||
func Test(server *hws.Server) http.Handler {
|
||||
func NotifyTester(server *hws.Server) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "GET" {
|
||||
page.Test().Render(r.Context(), w)
|
||||
} else {
|
||||
r.ParseForm()
|
||||
notifytype := r.Form.Get("type")
|
||||
target := r.Form.Get("target")
|
||||
title := r.Form.Get("title")
|
||||
level := map[string]notify.Level{
|
||||
"info": notify.LevelInfo,
|
||||
"success": notify.LevelSuccess,
|
||||
"warn": notify.LevelWarn,
|
||||
"error": notify.LevelError,
|
||||
}[r.Form.Get("type")]
|
||||
error := errors.New("This is a stack trace. No really i swear. Just pretend ok? Thanks")
|
||||
message := r.Form.Get("message")
|
||||
switch notifytype {
|
||||
case "error":
|
||||
err := errors.New(message)
|
||||
notifyInternalServiceError(server, w, title, err)
|
||||
return
|
||||
case "warn":
|
||||
notifyWarning(w, title, message)
|
||||
return
|
||||
case "info":
|
||||
notifyInfo(w, title, message)
|
||||
return
|
||||
case "success":
|
||||
notifySuccess(w, title, message)
|
||||
return
|
||||
nt := notify.Notification{
|
||||
Target: notify.Target(target),
|
||||
Title: title,
|
||||
Message: message,
|
||||
Level: level,
|
||||
Details: error.Error(),
|
||||
}
|
||||
if target == "" {
|
||||
server.NotifyAll(nt)
|
||||
} else {
|
||||
server.NotifySub(nt)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func notifyInfoWS() {
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user