package servers import ( "context" "encoding/json" "fmt" "strings" "acooldomain.co/backend/auth" "acooldomain.co/backend/models" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/volume" "github.com/docker/docker/client" "github.com/gin-gonic/gin" v1 "github.com/opencontainers/image-spec/specs-go/v1" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) type Connection struct { connection *mongo.Client apiClient *client.Client } type ContainerLabels struct { OwnerId string `json:"user_id"` ImageId string `json:"image_id"` VolumeId string `json:"volume_id"` Type string `json:"type"` } type VolumeLabels struct { OwnerId string `json:"user_id"` ImageId string `json:"image_id"` Type string `json:"type"` } type ImageLabels struct { Type string `json:"type"` } 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) imageNameAndVersion := strings.Split(volumeLabels.ImageId, ":") imageName := imageNameAndVersion[0] imageVersion := imageNameAndVersion[1] containers, err := con.apiClient.ContainerList(context.Background(), container.ListOptions{ All: true, Filters: filters.NewArgs(filters.Arg("label", "type=GAME"), filters.Arg("label", fmt.Sprintf("volume_id=%s", volume.Name))), }) var state bool var ports []models.Port if err != nil || len(containers) == 0 { state = false ports = nil } else { container := containers[0] 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, Image: models.ImageInfo{ Name: imageName, Version: imageVersion, }, OwnerId: volumeLabels.OwnerId, On: state, Ports: ports, Nickname: serverData.Nickname, } return &serverInfo, nil } func (con Connection) getServerInfoFromId(ServerId string) (*models.ServerInfo, error) { volume, err := con.apiClient.VolumeInspect(context.Background(), ServerId) if err != nil { return nil, err } return con.getServerInfo(volume) } func transformContainerPortsToModel(ports []types.Port) []models.Port { modelPorts := make([]models.Port, len(ports)) for index, port := range ports { modelPorts[index] = models.Port{ Number: int(port.PublicPort), Protocol: port.Type, } } return modelPorts } func (con Connection) StartServer(ctx *gin.Context) { serverId := ctx.Param("server_id") claims, exists := ctx.Get("claims") if !exists { ctx.AbortWithStatus(403) } // command := ctx.Param("command") serverInfo, err := con.getServerInfoFromId(serverId) if err != nil { ctx.AbortWithError(500, err) } if serverInfo.On { ctx.Status(200) return } imageId := serverInfo.Image.Name + ":" + serverInfo.Image.Version labels := ContainerLabels{ OwnerId: claims.(*auth.AuthClaims).Username, ImageId: imageId, VolumeId: serverInfo.Id, Type: "GAME", } jsonString, err := json.Marshal(labels) if err != nil { ctx.AbortWithError(500, err) } jsonLabels := make(map[string]string) json.Unmarshal(jsonString, &jsonLabels) volumes := make(map[string]struct{}) image, _, err := con.apiClient.ImageInspectWithRaw(context.Background(), imageId) if err != nil { ctx.AbortWithError(500, err) } response, err := con.apiClient.ContainerCreate( context.Background(), &container.Config{ AttachStdin: true, AttachStdout: true, AttachStderr: true, Tty: true, OpenStdin: false, StdinOnce: false, Image: imageId, Volumes: volumes, Labels: jsonLabels, }, &container.HostConfig{ AutoRemove: false, Mounts: []mount.Mount{{Source: serverInfo.Id, Target: image.Config.WorkingDir, Type: "volume"}}, }, &network.NetworkingConfig{}, &v1.Platform{}, "", ) if err != nil { ctx.AbortWithError(500, err) } ctx.JSON(200, response.ID) } func (con Connection) GetServers(ctx *gin.Context) { volumes, err := con.apiClient.VolumeList( context.TODO(), volume.ListOptions{ Filters: filters.NewArgs(filters.Arg("label", "type=GAME")), }, ) if err != nil { ctx.AbortWithError(500, err) } var servers []models.ServerInfo println("Found %d Containers", len(volumes.Volumes)) for _, volume := range volumes.Volumes { serverInfo, err := con.getServerInfo(*volume) if err != nil { continue } servers = append(servers, *serverInfo) } if err != nil { ctx.AbortWithError(500, err) } ctx.JSON(200, servers) } func LoadGroup(group *gin.RouterGroup, mongo_client *mongo.Client) { apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { panic(err) } defer apiClient.Close() connection := Connection{connection: mongo_client, apiClient: apiClient} group.Use(auth.AuthorizedTo(models.Create)).POST("/:server_id/start", connection.StartServer) group.GET("/", connection.GetServers) }