From 3c08208a60d664f7f504e27839a941a0ece43cb9 Mon Sep 17 00:00:00 2001 From: ACoolName Date: Wed, 15 May 2024 21:54:30 +0300 Subject: [PATCH] Added more data to message info --- models/server.go | 15 +++-- servers/servers.go | 141 +++++++++++++++++++++++++++++---------------- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/models/server.go b/models/server.go index 664c717..d3984b3 100644 --- a/models/server.go +++ b/models/server.go @@ -12,12 +12,13 @@ type ImageInfo struct { } type ServerInfo struct { - Id string - OwnerId string - Image ImageInfo - On bool - Nickname string - Ports []Port + Id string + OwnerId string + DefaultCommand string + Image ImageInfo + On bool + Nickname string + Ports []Port } type FileBrowserInfo struct { @@ -33,4 +34,6 @@ type ServerData struct { VolumeId string Nickname string UserPermissions map[string]Permission + DefaultCommand string + DefaultPorts []Port } diff --git a/servers/servers.go b/servers/servers.go index 53e66b4..7ae0d39 100644 --- a/servers/servers.go +++ b/servers/servers.go @@ -61,33 +61,51 @@ var upgrader = websocket.Upgrader{ func (con Connection) getServerInfo(volume volume.Volume) (*models.ServerInfo, error) { var volumeLabels VolumeLabels - jsonData, err := json.Marshal(volume.Labels) - if err != nil { - return nil, err - } - json.Unmarshal(jsonData, &volumeLabels) - imageList, err := con.apiClient.ImageList(context.Background(), image.ListOptions{Filters: filters.NewArgs(filters.Arg("reference", volumeLabels.ImageId))}) - if len(imageList) == 0 { - return nil, fmt.Errorf("ImageId %s does not exist", volumeLabels.ImageId) - } - imageSummery := imageList[0] - imageInspect, _, err := con.apiClient.ImageInspectWithRaw(context.Background(), imageSummery.ID) - if err != nil { - return nil, err - } + var serverData models.ServerData - var imagePorts []models.Port = make([]models.Port, len(imageInspect.Config.ExposedPorts)) - i := 0 - for imagePort := range imageInspect.Config.ExposedPorts { - imagePorts[i] = models.Port{Protocol: imagePort.Proto(), Number: imagePort.Int()} - i += 1 + con.connection.Database("Backend").Collection("Servers").FindOne(context.TODO(), bson.D{{Key: "id", Value: volume.Name}}).Decode(&serverData) + jsonData, err := json.Marshal(volume.Labels) + + if err != nil { + return nil, err + } + err = json.Unmarshal(jsonData, &volumeLabels) + if err != nil { + return nil, err + + } + var imagePorts []models.Port + + if len(serverData.DefaultPorts) == 0 { + + imageList, err := con.apiClient.ImageList(context.TODO(), image.ListOptions{Filters: filters.NewArgs(filters.Arg("reference", volumeLabels.ImageId))}) + if err != nil { + return nil, err + } + if len(imageList) == 0 { + return nil, fmt.Errorf("ImageId %s does not exist", volumeLabels.ImageId) + } + imageSummery := imageList[0] + imageInspect, _, err := con.apiClient.ImageInspectWithRaw(context.TODO(), imageSummery.ID) + if err != nil { + return nil, err + } + + imagePorts = make([]models.Port, len(imageInspect.Config.ExposedPorts)) + i := 0 + for imagePort := range imageInspect.Config.ExposedPorts { + imagePorts[i] = models.Port{Protocol: imagePort.Proto(), Number: imagePort.Int()} + i += 1 + } + } else { + imagePorts = serverData.DefaultPorts } imageNameAndVersion := strings.Split(volumeLabels.ImageId, ":") imageName := imageNameAndVersion[0] imageVersion := imageNameAndVersion[1] - containers, err := con.apiClient.ContainerList(context.Background(), container.ListOptions{ + containers, err := con.apiClient.ContainerList(context.TODO(), container.ListOptions{ All: true, Filters: filters.NewArgs(filters.Arg("label", "type=GAME"), filters.Arg("label", fmt.Sprintf("volume_id=%s", volume.Name))), }) @@ -101,8 +119,6 @@ func (con Connection) getServerInfo(volume volume.Volume) (*models.ServerInfo, e state = container.State == "running" ports = transformContainerPortsToModel(container.Ports) } - var serverData models.ServerData - con.connection.Database("backend").Collection("servers").FindOne(context.TODO(), bson.D{{Key: "volume_id", Value: volume.Name}}).Decode(&serverData) serverInfo := models.ServerInfo{ Id: volume.Name, @@ -111,17 +127,18 @@ func (con Connection) getServerInfo(volume volume.Volume) (*models.ServerInfo, e Version: imageVersion, Ports: imagePorts, }, - OwnerId: volumeLabels.OwnerId, - On: state, - Ports: ports, - Nickname: serverData.Nickname, + OwnerId: volumeLabels.OwnerId, + On: state, + Ports: ports, + Nickname: serverData.Nickname, + DefaultCommand: serverData.DefaultCommand, } return &serverInfo, nil } func (con Connection) getServerInfoFromId(ServerId string) (*models.ServerInfo, error) { - volume, err := con.apiClient.VolumeInspect(context.Background(), ServerId) + volume, err := con.apiClient.VolumeInspect(context.TODO(), ServerId) if err != nil { return nil, err } @@ -151,7 +168,9 @@ func convertLabelsToMap(v any) (map[string]string, error) { } type CreateServerRequest struct { - ImageId string `json:"image_id"` + ImageId string `json:"image_id"` + DefaultPorts []models.Port `json:"default_ports"` + DefaultCommand string `json:"default_command"` } func (con Connection) CreateServer(ctx *gin.Context) { @@ -167,7 +186,7 @@ func (con Connection) CreateServer(ctx *gin.Context) { return } - imageList, err := con.apiClient.ImageList(context.Background(), image.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("reference", request.ImageId))}) + imageList, err := con.apiClient.ImageList(context.TODO(), image.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("reference", request.ImageId))}) if err != nil { ctx.AbortWithError(400, err) return @@ -182,13 +201,21 @@ func (con Connection) CreateServer(ctx *gin.Context) { ctx.AbortWithError(400, err) return } - volumeResponse, err := con.apiClient.VolumeCreate(context.Background(), volume.CreateOptions{ + volumeResponse, err := con.apiClient.VolumeCreate(context.TODO(), volume.CreateOptions{ Labels: labels, }) if err != nil { ctx.AbortWithError(500, err) return } + con.connection.Database("Backend").Collection("Servers").InsertOne(context.TODO(), models.ServerData{ + Id: volumeResponse.Name, + OwnerId: claims.(*auth.AuthClaims).Username, + Image: imageSummary.RepoTags[0], + VolumeId: volumeResponse.Name, + DefaultPorts: request.DefaultPorts, + DefaultCommand: request.DefaultCommand, + }) ctx.JSON(200, volumeResponse.Name) } @@ -225,6 +252,7 @@ func (con Connection) StartServer(ctx *gin.Context) { ctx.Status(200) return } + imageId := serverInfo.Image.Name + ":" + serverInfo.Image.Version labels := ContainerLabels{ OwnerId: claims.(*auth.AuthClaims).Username, @@ -243,7 +271,7 @@ func (con Connection) StartServer(ctx *gin.Context) { volumes := make(map[string]struct{}) - image, _, err := con.apiClient.ImageInspectWithRaw(context.Background(), imageId) + image, _, err := con.apiClient.ImageInspectWithRaw(context.TODO(), imageId) if err != nil { ctx.AbortWithError(500, err) return @@ -261,7 +289,7 @@ func (con Connection) StartServer(ctx *gin.Context) { } } else { for _, portCouple := range request.Ports { - portMapping[nat.Port(fmt.Sprintf("%d/%s", portCouple.Source.Number, portCouple.Source.Protocol))] = []nat.PortBinding{{HostPort: fmt.Sprint(portCouple.Destination.Number)}} + portMapping[nat.Port(fmt.Sprintf("%d/%s", portCouple.Source.Number, portCouple.Source.Protocol))] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: fmt.Sprint(portCouple.Destination.Number)}} } } @@ -274,9 +302,12 @@ func (con Connection) StartServer(ctx *gin.Context) { if len(words) == 0 { words = nil } - + portSet := make(nat.PortSet) + for port, _ := range portMapping { + portSet[port] = struct{}{} + } response, err := con.apiClient.ContainerCreate( - context.Background(), + context.TODO(), &container.Config{ AttachStdin: true, AttachStdout: true, @@ -288,6 +319,7 @@ func (con Connection) StartServer(ctx *gin.Context) { Volumes: volumes, Labels: jsonLabels, Cmd: words, + ExposedPorts: portSet, }, &container.HostConfig{ AutoRemove: true, @@ -310,7 +342,7 @@ func (con Connection) StartServer(ctx *gin.Context) { HostIP, hostIPexists := os.LookupEnv("HOST_IP") if exists && hostIPexists { time.Sleep(time.Millisecond * 100) - containerData, err := con.apiClient.ContainerInspect(context.Background(), response.ID) + containerData, err := con.apiClient.ContainerInspect(context.TODO(), response.ID) if err != nil { ctx.AbortWithError(500, err) return @@ -366,7 +398,7 @@ func (con Connection) GetServers(ctx *gin.Context) { func (con Connection) StopServer(ctx *gin.Context) { serverId := ctx.Param("server_id") - containersList, err := con.apiClient.ContainerList(context.Background(), container.ListOptions{ + containersList, err := con.apiClient.ContainerList(context.TODO(), container.ListOptions{ Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId), filters.Arg("label", "type=GAME")), }) if err != nil { @@ -379,29 +411,31 @@ func (con Connection) StopServer(ctx *gin.Context) { } for _, containerData := range containersList { - con.apiClient.ContainerStop(context.Background(), containerData.ID, container.StopOptions{}) + con.apiClient.ContainerStop(context.TODO(), containerData.ID, container.StopOptions{}) } ctx.Status(200) } func (con Connection) DeleteServer(ctx *gin.Context) { serverId := ctx.Param("server_id") - containers, err := con.apiClient.ContainerList(context.Background(), container.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId))}) + containers, err := con.apiClient.ContainerList(context.TODO(), container.ListOptions{All: true, Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId))}) if err != nil { ctx.AbortWithError(500, err) return } for _, containerInstance := range containers { - con.apiClient.ContainerStop(context.Background(), containerInstance.ID, container.StopOptions{}) - err := con.apiClient.ContainerRemove(context.Background(), containerInstance.ID, container.RemoveOptions{Force: true, RemoveLinks: true}) + con.apiClient.ContainerStop(context.TODO(), containerInstance.ID, container.StopOptions{}) + err := con.apiClient.ContainerRemove(context.TODO(), containerInstance.ID, container.RemoveOptions{Force: true, RemoveLinks: true}) if err != nil { ctx.AbortWithError(500, err) return } } - con.apiClient.VolumeRemove(context.Background(), serverId, false) + con.apiClient.VolumeRemove(context.TODO(), serverId, false) + con.connection.Database("Backend").Collection("Servers").FindOneAndDelete(context.TODO(), bson.D{{Key: "id", Value: serverId}}) + ctx.JSON(200, "ok") } @@ -412,16 +446,20 @@ type RunCommandRequest struct { func (con Connection) RunCommand(ctx *gin.Context) { var request RunCommandRequest err := json.NewDecoder(ctx.Request.Body).Decode(&request) + if err != nil { + ctx.AbortWithError(500, err) + } + serverId := ctx.Param("server_id") log.Print("Writing command \"", request.Command, "\"") - containers, err := con.apiClient.ContainerList(context.Background(), container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId))}) + containers, err := con.apiClient.ContainerList(context.TODO(), container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId))}) if err != nil { ctx.AbortWithError(500, err) return } for _, containerData := range containers { - hijacked, err := con.apiClient.ContainerAttach(context.Background(), containerData.ID, container.AttachOptions{Stream: true, Stdin: true}) + hijacked, err := con.apiClient.ContainerAttach(context.TODO(), containerData.ID, container.AttachOptions{Stream: true, Stdin: true}) defer func() { hijacked.Close(); hijacked.CloseWrite() }() if err != nil { ctx.AbortWithError(500, err) @@ -464,7 +502,7 @@ func (con Connection) AttachServer(ctx *gin.Context) { ctx.AbortWithStatus(404) return } - hijacked, err := con.apiClient.ContainerAttach(context.Background(), containers[0].ID, container.AttachOptions{Stream: true, Stdin: true, Stdout: true, Stderr: true, Logs: true}) + hijacked, err := con.apiClient.ContainerAttach(context.TODO(), containers[0].ID, container.AttachOptions{Stream: true, Stdin: true, Stdout: true, Stderr: true, Logs: true}) if err != nil { ctx.AbortWithError(500, err) return @@ -478,7 +516,7 @@ func (con Connection) AttachServer(ctx *gin.Context) { defer ws.Close() go func() { - data := make([]byte, 0, 1024) + data := make([]byte, 1024) for { if stop { break @@ -490,10 +528,10 @@ func (con Connection) AttachServer(ctx *gin.Context) { } if count > 0 { - log.Println("Got ", count, " bytes: ", data) + log.Println("Got ", count, " bytes: ", string(data[:count])) + containerRead <- string(data[:count]) } - containerRead <- string(data) } }() @@ -520,6 +558,10 @@ func (con Connection) AttachServer(ctx *gin.Context) { } for { + if stop { + break + } + select { case Command := <-websocketRead: switch Command.CommandType { @@ -532,13 +574,12 @@ func (con Connection) AttachServer(ctx *gin.Context) { case "close": stop = true - break } + case data := <-containerRead: err := ws.WriteJSON(data) if err != nil { stop = true - break } } } @@ -559,7 +600,7 @@ func (con Connection) serverAuthorized(permissions models.Permission) func(*gin. var serverData models.ServerData - con.connection.Database("Backend").Collection("Servers").FindOne(context.Background(), bson.D{{Key: "ServerId", Value: server_id}}).Decode(&serverData) + con.connection.Database("Backend").Collection("Servers").FindOne(context.TODO(), bson.D{{Key: "id", Value: server_id}}).Decode(&serverData) if serverData.OwnerId == claims.(*auth.AuthClaims).Username { return true