added websocket

main
alex 2023-04-20 22:34:06 +02:00
parent 8f76aa49bd
commit f5f9e4f619
15 changed files with 360 additions and 5 deletions

2
go.mod
View File

@ -4,11 +4,13 @@ go 1.20
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/fasthttp/websocket v1.5.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.12.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/gofiber/fiber/v2 v2.44.0 // indirect
github.com/gofiber/websocket/v2 v2.1.6 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect

4
go.sum
View File

@ -3,6 +3,8 @@ github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.2 h1:KdCb0EpLpdJpfE3IPA5YLK/aYBO3dhZcvwxz6tXe2LQ=
github.com/fasthttp/websocket v1.5.2/go.mod h1:S0KC1VBlx1SaXGXq7yi1wKz4jMub58qEnHQG9oHuqBw=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@ -14,6 +16,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.44.0 h1:Z90bEvPcJM5GFJnu1py0E1ojoerkyew3iiNJ78MQCM8=
github.com/gofiber/fiber/v2 v2.44.0/go.mod h1:VTMtb/au8g01iqvHyaCzftuM/xmZgKOZCtFzz6CdV9w=
github.com/gofiber/websocket/v2 v2.1.6 h1:k4z+YqzGUwbCQJCIW+mDJF2iCcBfRY7BJGUa2k+VHXo=
github.com/gofiber/websocket/v2 v2.1.6/go.mod h1:o+oXFwHjavIiM2KWo/MNpcIOruS0am16h3efqnjXLis=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=

View File

@ -0,0 +1,3 @@
{
"name": "Janex Device Acryl Led Lamp"
}

View File

@ -0,0 +1,46 @@
{
"name": "Produktionstask 1",
"inputs": [
{
"parameterName": "irgendwas",
"type": "string",
"displayName": "Irgendwas tolles"
},
{
"parameterName": "kiste",
"type": "number",
"displayName": "Nummer der Kiste"
}
],
"tasks": [
{
"name": "Bild zu Label konvertieren",
"onFinish": "pause",
"undoPossible": false,
"scriptPath": "",
"parameter": [
{
"parameterName": "labelformat",
"type": "string",
"displayName": "Format des Labels",
"global": false
},
{
"parameterName": "kiste",
"type": "number",
"displayName": "Nummer der Kiste",
"global": true
}
],
"feedback": "image"
},
{
"name": "Label drucken",
"onFinish": "nextStep",
"undoPossible": false,
"scriptPath": "",
"parameter": [],
"feedback": ""
}
]
}

View File

@ -0,0 +1,3 @@
{
"name": "Roese"
}

View File

@ -0,0 +1,3 @@
{
"name": "Umbach"
}

48
main.go
View File

@ -4,12 +4,16 @@ import (
"janex/admin-dashboard-backend/modules/config"
"janex/admin-dashboard-backend/modules/database"
"janex/admin-dashboard-backend/modules/logger"
"janex/admin-dashboard-backend/modules/structs"
"janex/admin-dashboard-backend/modules/utils"
"janex/admin-dashboard-backend/routers/router"
"janex/admin-dashboard-backend/socketserver"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
flogger "github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/websocket/v2"
"github.com/rs/zerolog/log"
)
func init() {
@ -32,6 +36,50 @@ func main() {
router.SetupRoutes(app)
app.Use("/ws", func(c *fiber.Ctx) error {
// IsWebSocketUpgrade returns true if the client
// requested upgrade to the WebSocket protocol.
if websocket.IsWebSocketUpgrade(c) {
sessionId := c.Query("auth")
if len(sessionId) != utils.LenHeaderXAuthorization {
return c.SendStatus(fiber.StatusUnauthorized)
}
// validate ws session
var userSession structs.UserSession
database.DB.First(&userSession, "id = ?", sessionId)
if userSession.Id == "" {
return c.SendStatus(fiber.StatusUnauthorized)
}
log.Info().Msg("session id: " + userSession.Id + " user: " + userSession.Id)
var user structs.User
database.DB.First(&user, "id = ?", userSession.UserId)
if user.Id == "" {
return c.SendStatus(fiber.StatusInternalServerError)
}
log.Info().Msg("user " + user.Id + user.Username)
c.Locals("sessionId", sessionId)
c.Locals("userId", user.Id)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
go socketserver.RunHub()
go socketserver.WebSocketServer(app)
app.Listen(config.Cfg.Host + ":" + config.Cfg.Port)
}

64
modules/cache/cache.go vendored Normal file
View File

@ -0,0 +1,64 @@
package cache
import (
"janex/admin-dashboard-backend/modules/structs"
"sync"
"github.com/gofiber/websocket/v2"
)
var socketClients []*structs.SocketClient
var mu sync.RWMutex
func AddSocketClient(sessionId string, socketClient *structs.SocketClient) {
mu.Lock()
socketClients = append(socketClients, socketClient)
mu.Unlock()
}
func DeleteClientByConn(conn *websocket.Conn) {
mu.Lock()
for i := 0; i < len(socketClients); i++ {
if socketClients[i].Conn == conn {
socketClients = remove(socketClients, i)
break
}
}
mu.Unlock()
}
func remove(s []*structs.SocketClient, i int) []*structs.SocketClient {
return append(s[:i], s[i+1:]...)
}
func GetSocketClients() []*structs.SocketClient {
mu.RLock()
defer mu.RUnlock()
return socketClients
}
/*
func GetSocketClient(sessionId string) (socketClient *structs.SocketClient, ok bool) {
mu.RLock()
defer mu.RUnlock()
client, ok := socketClients[sessionId]
return client, ok
}
func GetSocketClientByConn(conn *websocket.Conn) (socketClient *structs.SocketClient, err error) {
mu.RLock()
defer mu.RUnlock()
for _, client := range socketClients {
if client.Conn == conn {
return client, nil
}
}
return nil, errors.New("Failed to find socket client by ws conn")
}*/

73
modules/structs/socket.go Normal file
View File

@ -0,0 +1,73 @@
package structs
import (
"encoding/json"
"errors"
"sync"
"github.com/gofiber/websocket/v2"
"github.com/rs/zerolog/log"
)
type SocketClient struct {
SessionId string
UserId string
Conn *websocket.Conn
connMu sync.Mutex
}
type SocketMessage struct {
Conn *websocket.Conn
Msg []byte
}
type SendSocketMessage struct {
Cmd int
Body any
}
type ReceivedMessage struct {
Cmd int
Body any
}
func (socketClient *SocketClient) SendCloseMessage() error {
return socketClient.writeMessage(websocket.CloseMessage, SendSocketMessage{}, true)
}
func (socketClient *SocketClient) SendMessage(message SendSocketMessage) error {
return socketClient.writeMessage(websocket.TextMessage, message, false)
}
func (socketClient *SocketClient) writeMessage(messageType int, message SendSocketMessage, closeMessage bool) error {
var marshaledMessage []byte
var err error
if closeMessage {
//marshaledMessage = websocket.FormatCloseMessage(utils.WsCloseCodeNewConnectionWasMade, "")
} else {
marshaledMessage, err = json.Marshal(message)
if err != nil {
log.Error().Msgf("Failed to marshal ws message, err: %s", err)
return err
}
}
socketClient.connMu.Lock()
defer socketClient.connMu.Unlock()
if socketClient.Conn == nil {
log.Error().Msgf("Failed to ws message because conn is nil")
return errors.New("ws client conn is nil")
}
err = socketClient.Conn.WriteMessage(messageType, marshaledMessage)
if err != nil {
log.Error().Msgf("Failed to write ws message, err: %s", err)
return err
}
return nil
}

View File

@ -2,12 +2,9 @@ package structs
import (
"time"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Id string
Username string
Email string
@ -16,7 +13,6 @@ type User struct {
}
type UserSession struct {
gorm.Model
Id string
UserId string
UserAgent string

View File

@ -8,7 +8,7 @@ import (
"github.com/gofiber/fiber/v2"
)
// Request on web startup
// Requested on web startup
func User(c *fiber.Ctx) error {
xAuthorization := utils.GetXAuhorizationHeader(c)

View File

@ -14,6 +14,7 @@ func SetupRoutes(app *fiber.App) {
u := v1.Group("/user")
u.Post("/auth/login", user.UserLogin)
u.Delete("/auth/logout", user.UserLogout)
u.Get("/", userSessionValidation, user.User)
}

View File

@ -0,0 +1,23 @@
package socketclients
import (
"janex/admin-dashboard-backend/modules/cache"
"janex/admin-dashboard-backend/modules/structs"
)
const (
SentCmdUpdateConnectedUsers = 1
)
func BroadcastMessage(sendSocketMessage structs.SendSocketMessage) {
for _, client := range cache.GetSocketClients() {
client.SendMessage(sendSocketMessage)
}
}
func UpdateConnectedUsers() {
BroadcastMessage(structs.SendSocketMessage{
Cmd: SentCmdUpdateConnectedUsers,
Body: len(cache.GetSocketClients()),
})
}

51
socketserver/hub.go Normal file
View File

@ -0,0 +1,51 @@
package socketserver
import (
"encoding/json"
"fmt"
"janex/admin-dashboard-backend/modules/cache"
"janex/admin-dashboard-backend/modules/structs"
"janex/admin-dashboard-backend/socketclients"
"github.com/gofiber/websocket/v2"
"github.com/rs/zerolog/log"
)
var register = make(chan *structs.SocketClient)
var broadcast = make(chan structs.SocketMessage)
var unregister = make(chan *websocket.Conn)
func RunHub() {
for {
select {
case newSocketClient := <-register:
userId := fmt.Sprintf("%v", newSocketClient.Conn.Locals("userId"))
sessionId := fmt.Sprintf("%v", newSocketClient.Conn.Locals("sessionId"))
newSocketClient.SessionId = sessionId
newSocketClient.UserId = userId
cache.AddSocketClient(sessionId, newSocketClient)
log.Debug().Msgf("clients: %d", len(cache.GetSocketClients()))
log.Debug().Msgf("REGISTER CLIENT: %s", sessionId)
socketclients.UpdateConnectedUsers()
case data := <-broadcast:
var receivedMessage structs.ReceivedMessage
if err := json.Unmarshal(data.Msg, &receivedMessage); err != nil {
log.Error().Msgf("Failed to unmarshal received msg, err: %s", err)
continue
}
log.Info().Msgf("Received message: %s", receivedMessage)
case connection := <-unregister:
cache.DeleteClientByConn(connection)
socketclients.UpdateConnectedUsers()
}
}
}

38
socketserver/server.go Normal file
View File

@ -0,0 +1,38 @@
package socketserver
import (
"janex/admin-dashboard-backend/modules/structs"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"github.com/rs/zerolog/log"
)
func WebSocketServer(app *fiber.App) {
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
defer func() {
unregister <- c
c.Close()
}()
register <- &structs.SocketClient{Conn: c}
for {
messageType, msg, err := c.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Error().Msgf("Read err: %s", err)
}
return
}
if messageType == websocket.TextMessage {
broadcast <- structs.SocketMessage{Conn: c, Msg: msg}
} else {
log.Error().Msgf("websocket message received of type %s", messageType)
}
}
}))
}