package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/google/uuid" ) func initApi() { gin.SetMode(gin.ReleaseMode) router := gin.Default() router.SetTrustedProxies(nil) router.GET("/api/ports", func(c *gin.Context) { c.JSON(200, portConfig) }) router.GET("/api/port", handleGetPort) router.POST("/api/ports", handleCreatePort) router.DELETE("/api/ports", handleDeletePort) router.GET("/api/state", handleGetState) router.PUT("/api/state", handleSetState) router.POST("/api/routes", handleCreateRoute) router.PUT("/api/routes", handleSetRoute) router.DELETE("/api/routes", handleDeleteRoute) router.Run(fmt.Sprintf("%s:%d", config.Web.Bind, config.Web.Port)) } func httpSendRes(c *gin.Context, err gin.H, res gin.H) { if err != nil { c.JSON(400, err) return } if res == nil { c.JSON(200, gin.H{"success": true}) } else { c.JSON(200, res) } } func handleGetPort(c *gin.Context) { id := c.Query("UUID") if id == "" { c.JSON(400, gin.H{"success": false, "error": "`UUID` parameter missing!"}) return } port, portIndex, _ := findPort(id) if portIndex < 0 { c.JSON(400, gin.H{"success": false, "error": "Port with provided UUID does not exist!"}) return } c.JSON(200, port) } type CreatePortRequest struct { Name string `json:"name"` Backend string `json:"backend"` Channels uint8 `json:"channels"` Type string `json:"type"` } func handleCreatePort(c *gin.Context) { var body CreatePortRequest if err := c.BindJSON(&body); err != nil { c.JSON(500, gin.H{"success": false, "error": "Parsing request body failed!"}) return } res, err := createPort(body) httpSendRes(c, err, res) } func createPort(body CreatePortRequest) (gin.H, gin.H) { if body.Name == "" || body.Backend == "" || body.Channels == 0 || body.Type == "" { return nil, gin.H{"success": false, "error": "Required parameters missing!"} } if body.Type != "input" && body.Type != "output" { return nil, gin.H{"success": false, "error": "`Type` must either be `input` or `output`"} } id := uuid.New() newPort := Port{ UUID: id.String(), Name: body.Name, Properties: PortProperties{ Backend: body.Backend, Channels: body.Channels, }, State: PortState{ Mute: false, Volume: 0, Balance: 0, }, Route: []PortRoute{}, } if body.Type == "input" { registerPort(&newPort, "input") portConfig.Input = append(portConfig.Input, newPort) } else { registerPort(&newPort, "output") portConfig.Output = append(portConfig.Output, newPort) } saveConfig("ports", &portConfig) return gin.H{"success": true, "UUID": id}, nil } func handleDeletePort(c *gin.Context) { id := c.Query("UUID") res := deletePort(id) httpSendRes(c, res, nil) } func deletePort(id string) gin.H { if id == "" { return gin.H{"success": false, "error": "Required parameters missing!"} } port, portIndex, portType := findPort(id) if portIndex < 0 { return gin.H{"success": false, "error": "Port with provided UUID does not exist!"} } // Remove the port from the configuration if portType == "output" { portConfig.Output = append(portConfig.Output[:portIndex], portConfig.Output[portIndex+1:]...) } else { portConfig.Input = append(portConfig.Input[:portIndex], portConfig.Input[portIndex+1:]...) } // Unregister port unregisterPort(port) saveConfig("ports", &portConfig) return nil } func handleGetState(c *gin.Context) { id := c.Query("UUID") if id == "" { c.JSON(400, gin.H{"success": false, "error": "`UUID` parameter missing!"}) return } port, portIndex, _ := findPort(id) if portIndex < 0 { c.JSON(400, gin.H{"success": false, "error": "Port with provided UUID does not exist!"}) return } c.JSON(200, port.State) } type SetPortRequest struct { Mute *bool `json:"mute"` Volume *float32 `json:"volume"` Balance *float32 `json:"balance"` } func handleSetState(c *gin.Context) { var body SetPortRequest err := c.BindJSON(&body) if err != nil { c.JSON(500, gin.H{"success": false, "error": "Parsing request body failed!"}) return } id := c.Query("UUID") res, errMsg := setState(id, body) httpSendRes(c, errMsg, res) } func setState(id string, body SetPortRequest) (gin.H, gin.H) { if id == "" { return nil, gin.H{"success": false, "error": "`UUID` parameter missing!"} } port, portIndex, _ := findPort(id) if portIndex < 0 { return nil, gin.H{"success": false, "error": "Port with provided UUID does not exist!"} } if body.Mute != nil { port.State.Mute = *body.Mute } if body.Volume != nil { port.State.Volume = *body.Volume } if body.Balance != nil { port.State.Balance = *body.Balance } saveConfig("ports", &portConfig) return gin.H{"success": true, "state": port.State}, nil } type CreateRouteRequest struct { To string `json:"to"` } func handleCreateRoute(c *gin.Context) { var body CreateRouteRequest err := c.BindJSON(&body) if err != nil { c.JSON(500, gin.H{"success": false, "error": "Parsing request body failed!"}) return } id := c.Query("UUID") res := createRoute(id, body) httpSendRes(c, res, nil) } func createRoute(id string, body CreateRouteRequest) gin.H { if id == "" || body.To == "" { return gin.H{"success": false, "error": "Required parameters missing!"} } from, fromIndex, fromType := findPort(id) _, toIndex, toType := findPort(body.To) if fromIndex < 0 || toIndex < 0 { return gin.H{"success": false, "error": "One of `UUID` or `To` does not exist!"} } if fromType != "input" || toType != "output" { return gin.H{"success": false, "error": "`UUID` needs to be an input and `To` an output port!"} } from.Route = append(from.Route, PortRoute{ToUUID: body.To, Mute: true, Volume: 0, Balance: 0}) saveConfig("ports", &portConfig) return nil } type SetRouteRequest struct { Mute *bool `json:"mute"` Volume *float32 `json:"volume"` Balance *float32 `json:"balance"` } func handleSetRoute(c *gin.Context) { var body SetRouteRequest err := c.BindJSON(&body) if err != nil { c.JSON(500, gin.H{"success": false, "error": "Parsing request body failed!"}) return } fromId := c.Query("UUID") toId := c.Query("To") res, errMsg := setRoute(fromId, toId, body) httpSendRes(c, errMsg, res) } func setRoute(fromId string, toId string, body SetRouteRequest) (gin.H, gin.H) { if fromId == "" || toId == "" { return nil, gin.H{"success": false, "error": "Required parameters missing!"} } from, fromIndex, _ := findPort(fromId) if fromIndex < 0 { return nil, gin.H{"success": false, "error": "Port with provided UUID does not exist!"} } for i, r := range from.Route { if r.ToUUID != toId { continue } if body.Mute != nil { from.Route[i].Mute = *body.Mute } if body.Volume != nil { from.Route[i].Volume = *body.Volume } if body.Balance != nil { from.Route[i].Balance = *body.Balance } saveConfig("ports", &portConfig) return gin.H{"success": true, "state": from.Route[i]}, nil } return nil, gin.H{"success": false, "error": "No such route exists!"} } func handleDeleteRoute(c *gin.Context) { fromId := c.Query("UUID") toId := c.Query("To") res := deleteRoute(fromId, toId) httpSendRes(c, res, nil) } func deleteRoute(fromId string, toId string) gin.H { if fromId == "" || toId == "" { return gin.H{"success": false, "error": "Required parameters missing!"} } from, fromIndex, _ := findPort(fromId) if fromIndex < 0 { return gin.H{"success": false, "error": "Port with provided UUID does not exist!"} } for i, r := range from.Route { if r.ToUUID != toId { continue } from.Route = append(from.Route[:i], from.Route[i+1:]...) saveConfig("ports", &portConfig) return nil } return gin.H{"success": false, "error": "No such route exists!"} }