Implement WebSocket API

This commit is contained in:
2024-02-28 11:13:02 +01:00
parent dd665d9443
commit aee617cc88
3 changed files with 145 additions and 12 deletions

130
api.go
View File

@ -1,12 +1,22 @@
package main
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{}
type WsClient struct {
SendUpdates bool
}
var wsClients = make(map[*websocket.Conn]WsClient)
func initApi() {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
@ -26,6 +36,8 @@ func initApi() {
router.PUT("/api/routes", handleSetRoute)
router.DELETE("/api/routes", handleDeleteRoute)
router.GET("/ws", handleWs)
router.Run(fmt.Sprintf("%s:%d", config.Web.Bind, config.Web.Port))
}
@ -108,6 +120,7 @@ func createPort(body CreatePortRequest) (gin.H, gin.H) {
portConfig.Output = append(portConfig.Output, newPort)
}
saveConfig("ports", &portConfig)
onPortListChange()
return gin.H{"success": true, "UUID": id}, nil
}
@ -139,6 +152,7 @@ func deletePort(id string) gin.H {
unregisterPort(port)
saveConfig("ports", &portConfig)
onPortListChange()
return nil
}
@ -199,6 +213,7 @@ func setState(id string, body SetPortRequest) (gin.H, gin.H) {
}
saveConfig("ports", &portConfig)
onPortChange(port)
return gin.H{"success": true, "state": port.State}, nil
}
@ -236,6 +251,7 @@ func createRoute(id string, body CreateRouteRequest) gin.H {
from.Route = append(from.Route, PortRoute{ToUUID: body.To, Mute: true, Volume: 0, Balance: 0})
saveConfig("ports", &portConfig)
onPortChange(from)
return nil
}
@ -285,6 +301,7 @@ func setRoute(fromId string, toId string, body SetRouteRequest) (gin.H, gin.H) {
}
saveConfig("ports", &portConfig)
onPortChange(from)
return gin.H{"success": true, "state": from.Route[i]}, nil
}
@ -315,8 +332,121 @@ func deleteRoute(fromId string, toId string) gin.H {
from.Route = append(from.Route[:i], from.Route[i+1:]...)
saveConfig("ports", &portConfig)
onPortListChange()
return nil
}
return gin.H{"success": false, "error": "No such route exists!"}
}
type WebsocketMessage struct {
RequestId string `json:"requestId"`
Method string `json:"method"`
UUID string `json:"UUID"`
ToUUID string `json:"toUUID"`
PortData CreatePortRequest `json:"portData"`
StateData SetPortRequest `json:"stateData"`
RouteData CreateRouteRequest `json:"routeData"`
RouteStateData SetRouteRequest `json:"routeStateData"`
}
type WebsocketResponse struct {
Type string `json:"type"`
RequestId string `json:"requestId,omitempty"`
RequestMethod string `json:"requestMethod,omitempty"`
ResponseData interface{} `json:"responseData"`
}
func wsSendRes(conn *websocket.Conn, res gin.H, err gin.H, request *WebsocketMessage) {
if err != nil {
data, _ := json.Marshal(WebsocketResponse{
Type: "error",
RequestId: request.RequestId,
RequestMethod: request.Method,
ResponseData: err,
})
conn.WriteMessage(1, data)
return
}
response := WebsocketResponse{
Type: "response",
RequestId: request.RequestId,
RequestMethod: request.Method,
ResponseData: gin.H{"success": true},
}
if res != nil {
response.ResponseData = res
}
data, _ := json.Marshal(response)
conn.WriteMessage(1, data)
}
func handleWs(c *gin.Context) {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
fmt.Println("WARN! Handling websocket connection failed!")
c.JSON(500, gin.H{"success": false, "error": "Handling websocket connection failed!"})
}
wsClients[conn] = WsClient{SendUpdates: false}
for {
var content WebsocketMessage
jsonErr := conn.ReadJSON(&content)
if jsonErr != nil {
delete(wsClients, conn)
conn.Close()
break
}
var res, err gin.H = nil, nil
switch content.Method {
case "listPorts":
res = gin.H{"inputs": portConfig.Input, "outputs": portConfig.Output}
case "createPort":
res, err = createPort(content.PortData)
case "setPortState":
res, err = setState(content.UUID, content.StateData)
case "deletePort":
err = deletePort(content.UUID)
case "createRoute":
err = createRoute(content.UUID, content.RouteData)
case "setRouteState":
res, err = setRoute(content.UUID, content.ToUUID, content.RouteStateData)
case "deleteRoute":
err = deleteRoute(content.UUID, content.ToUUID)
case "enableUpdates":
if clientOpts, ok := wsClients[conn]; ok {
clientOpts.SendUpdates = true
wsClients[conn] = clientOpts
}
default:
err = gin.H{"success": false, "error": "Method is not implemented"}
}
wsSendRes(conn, res, err, &content)
}
}
func onPortChange(port *Port) {
for conn := range wsClients {
if !wsClients[conn].SendUpdates {
continue
}
jsonData, _ := json.Marshal(WebsocketResponse{Type: "portChange", ResponseData: port})
conn.WriteMessage(1, jsonData)
}
}
func onPortListChange() {
for conn := range wsClients {
if !wsClients[conn].SendUpdates {
continue
}
jsonData, _ := json.Marshal(WebsocketResponse{
Type: "postListChange",
ResponseData: gin.H{"inputs": portConfig.Input, "outputs": portConfig.Output},
})
conn.WriteMessage(1, jsonData)
}
}