package agentapi import ( "net" "net/http" "strconv" _ "embed" "github.com/gin-gonic/gin" "github.com/yusing/godoxy/agent/pkg/agent" "github.com/yusing/godoxy/internal/agentpool" apitypes "github.com/yusing/goutils/apitypes" ) type NewAgentRequest struct { Name string `json:"name" binding:"required"` Host string `json:"host" binding:"required"` Port int `json:"port" binding:"required,min=1,max=65535"` Type string `json:"type" binding:"required,oneof=docker system"` Nightly bool `json:"nightly" binding:"omitempty"` ContainerRuntime agent.ContainerRuntime `json:"container_runtime" binding:"omitempty,oneof=docker podman" default:"docker"` } // @name NewAgentRequest type NewAgentResponse struct { Compose string `json:"compose"` CA PEMPairResponse `json:"ca"` Client PEMPairResponse `json:"client"` } // @name NewAgentResponse // @x-id "create" // @BasePath /api/v1 // @Summary Create a new agent // @Description Create a new agent and return the docker compose file, encrypted CA and client PEMs // @Description The returned PEMs are encrypted with a random key and will be used for verification when adding a new agent // @Tags agent // @Accept json // @Produce json // @Param request body NewAgentRequest true "Request" // @Success 200 {object} NewAgentResponse // @Failure 400 {object} apitypes.ErrorResponse // @Failure 403 {object} apitypes.ErrorResponse // @Failure 409 {object} apitypes.ErrorResponse // @Failure 500 {object} apitypes.ErrorResponse // @Router /agent/create [post] func Create(c *gin.Context) { var request NewAgentRequest if err := c.ShouldBindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) return } hostport := net.JoinHostPort(request.Host, strconv.Itoa(request.Port)) if _, ok := agentpool.Get(hostport); ok { c.JSON(http.StatusConflict, apitypes.Error("agent already exists")) return } var image string if request.Nightly { image = agent.DockerImageNightly } else { image = agent.DockerImageProduction } ca, srv, client, err := agent.NewAgent() if err != nil { c.Error(apitypes.InternalServerError(err, "failed to create agent")) return } var cfg agent.Generator = &agent.AgentEnvConfig{ Name: request.Name, Port: request.Port, CACert: ca.String(), SSLCert: srv.String(), ContainerRuntime: request.ContainerRuntime, } if request.Type == "docker" { cfg = &agent.AgentComposeConfig{ Image: image, AgentEnvConfig: cfg.(*agent.AgentEnvConfig), } } template, err := cfg.Generate() if err != nil { c.Error(apitypes.InternalServerError(err, "failed to generate agent config")) return } key := getEncryptionKey() encCA, err := ca.Encrypt(key) if err != nil { c.Error(apitypes.InternalServerError(err, "failed to encrypt CA PEMs")) return } encClient, err := client.Encrypt(key) if err != nil { c.Error(apitypes.InternalServerError(err, "failed to encrypt client PEMs")) return } c.JSON(http.StatusOK, NewAgentResponse{ Compose: template, CA: toPEMPairResponse(encCA), Client: toPEMPairResponse(encClient), }) }