From 20105534c756d09a7f2e138c287a6477b332b0c2 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 4 Sep 2025 06:36:02 +0800 Subject: [PATCH] feat(api): add GetContainer endpoint and Docker host ID mapping - Implemented GetContainer function to retrieve container details by ID. - Introduced idDockerHostMap for mapping container IDs to Docker hosts. - Updated Container struct to allow omitting the State field in JSON responses. --- internal/api/v1/docker/container.go | 61 ++++++++++++++++++++++++++++ internal/api/v1/docker/containers.go | 2 +- internal/docker/id_lookup.go | 19 +++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 internal/api/v1/docker/container.go create mode 100644 internal/docker/id_lookup.go diff --git a/internal/api/v1/docker/container.go b/internal/api/v1/docker/container.go new file mode 100644 index 00000000..a92f5d18 --- /dev/null +++ b/internal/api/v1/docker/container.go @@ -0,0 +1,61 @@ +package dockerapi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + apitypes "github.com/yusing/go-proxy/internal/api/types" + "github.com/yusing/go-proxy/internal/docker" +) + +// @x-id "container" +// @BasePath /api/v1 +// @Summary Get container +// @Description Get container by container id +// @Tags docker +// @Produce json +// @Param id path string true "Container ID" +// @Success 200 {object} Container +// @Failure 403 {object} apitypes.ErrorResponse +// @Failure 500 {object} apitypes.ErrorResponse +// @Router /docker/container/{id} [get] +func GetContainer(c *gin.Context) { + id := c.Param("id") + if id == "" { + c.JSON(http.StatusBadRequest, apitypes.Error("id is required")) + return + } + + dockerHost, ok := docker.GetDockerHostByContainerID(id) + if !ok { + c.JSON(http.StatusNotFound, apitypes.Error("container not found")) + return + } + + client, err := docker.NewClient(dockerHost) + if err != nil { + c.Error(apitypes.InternalServerError(err, "failed to create docker client")) + return + } + + defer client.Close() + + cont, err := client.ContainerInspect(c.Request.Context(), id) + if err != nil { + c.Error(apitypes.InternalServerError(err, "failed to inspect container")) + return + } + + var state ContainerState + if cont.State != nil { + state = cont.State.Status + } + + c.JSON(http.StatusOK, &Container{ + Server: dockerHost, + Name: cont.Name, + ID: cont.ID, + Image: cont.Image, + State: state, + }) +} diff --git a/internal/api/v1/docker/containers.go b/internal/api/v1/docker/containers.go index 0a9c5574..e533a649 100644 --- a/internal/api/v1/docker/containers.go +++ b/internal/api/v1/docker/containers.go @@ -16,7 +16,7 @@ type Container struct { Name string `json:"name"` ID string `json:"id"` Image string `json:"image"` - State ContainerState `json:"state"` + State ContainerState `json:"state,omitempty" extensions:"x-nullable"` } // @name ContainerResponse // @x-id "containers" diff --git a/internal/docker/id_lookup.go b/internal/docker/id_lookup.go new file mode 100644 index 00000000..3f5601d3 --- /dev/null +++ b/internal/docker/id_lookup.go @@ -0,0 +1,19 @@ +package docker + +import ( + "github.com/puzpuzpuz/xsync/v4" +) + +var idDockerHostMap = xsync.NewMap[string, string]() + +func GetDockerHostByContainerID(id string) (string, bool) { + return idDockerHostMap.Load(id) +} + +func SetDockerHostByContainerID(id, host string) { + idDockerHostMap.Store(id, host) +} + +func DeleteDockerHostByContainerID(id string) { + idDockerHostMap.Delete(id) +}