mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
1088 lines
30 KiB
Go
1088 lines
30 KiB
Go
package controllers
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/TheTNB/panel/pkg/tools"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/api/types/network"
|
|
"github.com/docker/docker/api/types/volume"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/goravel/framework/contracts/http"
|
|
"github.com/goravel/framework/support/carbon"
|
|
|
|
commonrequests "github.com/TheTNB/panel/app/http/requests/common"
|
|
requests "github.com/TheTNB/panel/app/http/requests/container"
|
|
"github.com/TheTNB/panel/internal/services"
|
|
)
|
|
|
|
type ContainerController struct {
|
|
container services.Container
|
|
}
|
|
|
|
func NewContainerController() *ContainerController {
|
|
return &ContainerController{
|
|
container: services.NewContainer(),
|
|
}
|
|
}
|
|
|
|
// ContainerList
|
|
//
|
|
// @Summary 获取容器列表
|
|
// @Description 获取所有容器列表
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query commonrequests.Paginate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/list [get]
|
|
func (r *ContainerController) ContainerList(ctx http.Context) http.Response {
|
|
var request commonrequests.Paginate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
containers, err := r.container.ContainerListAll()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
startIndex := (request.Page - 1) * request.Limit
|
|
endIndex := request.Page * request.Limit
|
|
if startIndex > len(containers) {
|
|
return Success(ctx, http.Json{
|
|
"total": 0,
|
|
"items": []any{},
|
|
})
|
|
}
|
|
if endIndex > len(containers) {
|
|
endIndex = len(containers)
|
|
}
|
|
paged := containers[startIndex:endIndex]
|
|
|
|
items := make([]any, 0)
|
|
for _, item := range paged {
|
|
var name string
|
|
if len(item.Names) > 0 {
|
|
name = item.Names[0]
|
|
}
|
|
items = append(items, map[string]any{
|
|
"id": item.ID,
|
|
"name": strings.TrimLeft(name, "/"),
|
|
"image": item.Image,
|
|
"image_id": item.ImageID,
|
|
"command": item.Command,
|
|
"created": carbon.FromTimestamp(item.Created).ToDateTimeString(),
|
|
"ports": item.Ports,
|
|
"labels": item.Labels,
|
|
"state": item.State,
|
|
"status": item.Status,
|
|
})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"total": len(containers),
|
|
"items": items,
|
|
})
|
|
}
|
|
|
|
// ContainerSearch
|
|
//
|
|
// @Summary 搜索容器
|
|
// @Description 根据容器名称搜索容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param name query string true "容器名称"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/search [get]
|
|
func (r *ContainerController) ContainerSearch(ctx http.Context) http.Response {
|
|
fields := strings.Fields(ctx.Request().Query("name"))
|
|
containers, err := r.container.ContainerListByNames(fields)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, containers)
|
|
}
|
|
|
|
// ContainerCreate
|
|
//
|
|
// @Summary 创建容器
|
|
// @Description 创建一个容器
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ContainerCreate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/create [post]
|
|
func (r *ContainerController) ContainerCreate(ctx http.Context) http.Response {
|
|
var request requests.ContainerCreate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
var hostConf container.HostConfig
|
|
var networkConf network.NetworkingConfig
|
|
|
|
portMap := make(nat.PortMap)
|
|
for _, port := range request.Ports {
|
|
if port.ContainerStart-port.ContainerEnd != port.HostStart-port.HostEnd {
|
|
return Error(ctx, http.StatusUnprocessableEntity, fmt.Sprintf("容器端口和主机端口数量不匹配(容器: %d 主机: %d)", port.ContainerStart-port.ContainerEnd, port.HostStart-port.HostEnd))
|
|
}
|
|
if port.ContainerStart > port.ContainerEnd || port.HostStart > port.HostEnd || port.ContainerStart < 1 || port.HostStart < 1 {
|
|
return Error(ctx, http.StatusUnprocessableEntity, "端口范围不正确")
|
|
}
|
|
|
|
count := 0
|
|
for host := port.HostStart; host <= port.HostEnd; host++ {
|
|
bindItem := nat.PortBinding{HostPort: strconv.Itoa(host), HostIP: port.Host}
|
|
portMap[nat.Port(fmt.Sprintf("%d/%s", port.ContainerStart+count, port.Protocol))] = []nat.PortBinding{bindItem}
|
|
count++
|
|
}
|
|
}
|
|
|
|
exposed := make(nat.PortSet)
|
|
for port := range portMap {
|
|
exposed[port] = struct{}{}
|
|
}
|
|
|
|
if request.Network != "" {
|
|
switch request.Network {
|
|
case "host", "none", "bridge":
|
|
hostConf.NetworkMode = container.NetworkMode(request.Network)
|
|
}
|
|
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{request.Network: {}}
|
|
} else {
|
|
networkConf = network.NetworkingConfig{}
|
|
}
|
|
|
|
hostConf.Privileged = request.Privileged
|
|
hostConf.AutoRemove = request.AutoRemove
|
|
hostConf.CPUShares = request.CPUShares
|
|
hostConf.PublishAllPorts = request.PublishAllPorts
|
|
hostConf.RestartPolicy = container.RestartPolicy{Name: container.RestartPolicyMode(request.RestartPolicy)}
|
|
if request.RestartPolicy == "on-failure" {
|
|
hostConf.RestartPolicy.MaximumRetryCount = 5
|
|
}
|
|
hostConf.NanoCPUs = request.CPUs * 1000000000
|
|
hostConf.Memory = request.Memory * 1024 * 1024
|
|
hostConf.MemorySwap = 0
|
|
hostConf.PortBindings = portMap
|
|
hostConf.Binds = []string{}
|
|
|
|
volumes := make(map[string]struct{})
|
|
for _, v := range request.Volumes {
|
|
volumes[v.Container] = struct{}{}
|
|
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", v.Host, v.Container, v.Mode))
|
|
}
|
|
|
|
id, err := r.container.ContainerCreate(request.Name,
|
|
container.Config{
|
|
Image: request.Image,
|
|
Env: r.container.KVToSlice(request.Env),
|
|
Entrypoint: request.Entrypoint,
|
|
Cmd: request.Command,
|
|
Labels: r.container.KVToMap(request.Labels),
|
|
ExposedPorts: exposed,
|
|
OpenStdin: request.OpenStdin,
|
|
Tty: request.Tty,
|
|
Volumes: volumes,
|
|
},
|
|
hostConf,
|
|
networkConf,
|
|
)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
if err = r.container.ContainerStart(id); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, id)
|
|
}
|
|
|
|
// ContainerRemove
|
|
//
|
|
// @Summary 删除容器
|
|
// @Description 删除一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/remove [post]
|
|
func (r *ContainerController) ContainerRemove(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerRemove(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerStart
|
|
//
|
|
// @Summary 启动容器
|
|
// @Description 启动一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/start [post]
|
|
func (r *ContainerController) ContainerStart(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerStart(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerStop
|
|
//
|
|
// @Summary 停止容器
|
|
// @Description 停止一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/stop [post]
|
|
func (r *ContainerController) ContainerStop(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerStop(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerRestart
|
|
//
|
|
// @Summary 重启容器
|
|
// @Description 重启一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/restart [post]
|
|
func (r *ContainerController) ContainerRestart(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerRestart(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerPause
|
|
//
|
|
// @Summary 暂停容器
|
|
// @Description 暂停一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
func (r *ContainerController) ContainerPause(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerPause(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerUnpause
|
|
//
|
|
// @Summary 取消暂停容器
|
|
// @Description 取消暂停一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
//
|
|
// @Router /panel/container/unpause [post]
|
|
func (r *ContainerController) ContainerUnpause(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerUnpause(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerInspect
|
|
//
|
|
// @Summary 查看容器
|
|
// @Description 查看一个容器的详细信息
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/inspect [get]
|
|
func (r *ContainerController) ContainerInspect(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.ContainerInspect(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// ContainerKill
|
|
//
|
|
// @Summary 杀死容器
|
|
// @Description 杀死一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/kill [post]
|
|
func (r *ContainerController) ContainerKill(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerKill(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerRename
|
|
//
|
|
// @Summary 重命名容器
|
|
// @Description 重命名一个容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ContainerRename true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/rename [post]
|
|
func (r *ContainerController) ContainerRename(ctx http.Context) http.Response {
|
|
var request requests.ContainerRename
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ContainerRename(request.ID, request.Name); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ContainerStats
|
|
//
|
|
// @Summary 查看容器状态
|
|
// @Description 查看一个容器的状态信息
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/stats [get]
|
|
func (r *ContainerController) ContainerStats(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.ContainerStats(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// ContainerExist
|
|
//
|
|
// @Summary 检查容器是否存在
|
|
// @Description 检查一个容器是否存在
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/exist [get]
|
|
func (r *ContainerController) ContainerExist(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
exist, err := r.container.ContainerExist(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, exist)
|
|
}
|
|
|
|
// ContainerLogs
|
|
//
|
|
// @Summary 查看容器日志
|
|
// @Description 查看一个容器的日志
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/logs [get]
|
|
func (r *ContainerController) ContainerLogs(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.ContainerLogs(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// ContainerPrune
|
|
//
|
|
// @Summary 清理容器
|
|
// @Description 清理无用的容器
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/prune [post]
|
|
func (r *ContainerController) ContainerPrune(ctx http.Context) http.Response {
|
|
if err := r.container.ContainerPrune(); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// NetworkList
|
|
//
|
|
// @Summary 获取网络列表
|
|
// @Description 获取所有网络列表
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query commonrequests.Paginate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/list [get]
|
|
func (r *ContainerController) NetworkList(ctx http.Context) http.Response {
|
|
var request commonrequests.Paginate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
networks, err := r.container.NetworkList()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
startIndex := (request.Page - 1) * request.Limit
|
|
endIndex := request.Page * request.Limit
|
|
if startIndex > len(networks) {
|
|
return Success(ctx, http.Json{
|
|
"total": 0,
|
|
"items": []any{},
|
|
})
|
|
}
|
|
if endIndex > len(networks) {
|
|
endIndex = len(networks)
|
|
}
|
|
paged := networks[startIndex:endIndex]
|
|
if paged == nil {
|
|
paged = []types.NetworkResource{}
|
|
}
|
|
|
|
items := make([]any, 0)
|
|
for _, item := range paged {
|
|
var ipamConfig []any
|
|
for _, v := range item.IPAM.Config {
|
|
ipamConfig = append(ipamConfig, map[string]any{
|
|
"subnet": v.Subnet,
|
|
"gateway": v.Gateway,
|
|
"ip_range": v.IPRange,
|
|
"aux_address": v.AuxAddress,
|
|
})
|
|
}
|
|
items = append(items, map[string]any{
|
|
"id": item.ID,
|
|
"name": item.Name,
|
|
"driver": item.Driver,
|
|
"ipv6": item.EnableIPv6,
|
|
"scope": item.Scope,
|
|
"internal": item.Internal,
|
|
"attachable": item.Attachable,
|
|
"ingress": item.Ingress,
|
|
"labels": item.Labels,
|
|
"options": item.Options,
|
|
"ipam": map[string]any{
|
|
"config": ipamConfig,
|
|
"driver": item.IPAM.Driver,
|
|
"options": item.IPAM.Options,
|
|
},
|
|
"created": carbon.FromStdTime(item.Created).ToDateTimeString(),
|
|
})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"total": len(networks),
|
|
"items": items,
|
|
})
|
|
}
|
|
|
|
// NetworkCreate
|
|
//
|
|
// @Summary 创建网络
|
|
// @Description 创建一个网络
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.NetworkCreate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/create [post]
|
|
func (r *ContainerController) NetworkCreate(ctx http.Context) http.Response {
|
|
var request requests.NetworkCreate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
id, err := r.container.NetworkCreate(request)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, id)
|
|
}
|
|
|
|
// NetworkRemove
|
|
//
|
|
// @Summary 删除网络
|
|
// @Description 删除一个网络
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/remove [post]
|
|
func (r *ContainerController) NetworkRemove(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.NetworkRemove(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// NetworkExist
|
|
//
|
|
// @Summary 检查网络是否存在
|
|
// @Description 检查一个网络是否存在
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/exist [get]
|
|
func (r *ContainerController) NetworkExist(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
exist, err := r.container.NetworkExist(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, exist)
|
|
}
|
|
|
|
// NetworkInspect
|
|
//
|
|
// @Summary 查看网络
|
|
// @Description 查看一个网络的详细信息
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/inspect [get]
|
|
func (r *ContainerController) NetworkInspect(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.NetworkInspect(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// NetworkConnect
|
|
//
|
|
// @Summary 连接容器到网络
|
|
// @Description 连接一个容器到一个网络
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.NetworkConnectDisConnect true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/connect [post]
|
|
func (r *ContainerController) NetworkConnect(ctx http.Context) http.Response {
|
|
var request requests.NetworkConnectDisConnect
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.NetworkConnect(request.Network, request.Container); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// NetworkDisconnect
|
|
//
|
|
// @Summary 从网络断开容器
|
|
// @Description 从一个网络断开一个容器
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.NetworkConnectDisConnect true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/disconnect [post]
|
|
func (r *ContainerController) NetworkDisconnect(ctx http.Context) http.Response {
|
|
var request requests.NetworkConnectDisConnect
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.NetworkDisconnect(request.Network, request.Container); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// NetworkPrune
|
|
//
|
|
// @Summary 清理网络
|
|
// @Description 清理无用的网络
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/network/prune [post]
|
|
func (r *ContainerController) NetworkPrune(ctx http.Context) http.Response {
|
|
if err := r.container.NetworkPrune(); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ImageList
|
|
//
|
|
// @Summary 获取镜像列表
|
|
// @Description 获取所有镜像列表
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query commonrequests.Paginate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/list [get]
|
|
func (r *ContainerController) ImageList(ctx http.Context) http.Response {
|
|
var request commonrequests.Paginate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
images, err := r.container.ImageList()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
startIndex := (request.Page - 1) * request.Limit
|
|
endIndex := request.Page * request.Limit
|
|
if startIndex > len(images) {
|
|
return Success(ctx, http.Json{
|
|
"total": 0,
|
|
"items": []any{},
|
|
})
|
|
}
|
|
if endIndex > len(images) {
|
|
endIndex = len(images)
|
|
}
|
|
paged := images[startIndex:endIndex]
|
|
if paged == nil {
|
|
paged = []image.Summary{}
|
|
}
|
|
|
|
items := make([]any, 0)
|
|
for _, item := range paged {
|
|
items = append(items, map[string]any{
|
|
"id": item.ID,
|
|
"created": carbon.FromTimestamp(item.Created).ToDateTimeString(),
|
|
"containers": item.Containers,
|
|
"size": tools.FormatBytes(float64(item.Size)),
|
|
"labels": item.Labels,
|
|
"repo_tags": item.RepoTags,
|
|
"repo_digests": item.RepoDigests,
|
|
})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"total": len(images),
|
|
"items": items,
|
|
})
|
|
}
|
|
|
|
// ImageExist
|
|
//
|
|
// @Summary 检查镜像是否存在
|
|
// @Description 检查一个镜像是否存在
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/exist [get]
|
|
func (r *ContainerController) ImageExist(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
exist, err := r.container.ImageExist(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, exist)
|
|
}
|
|
|
|
// ImagePull
|
|
//
|
|
// @Summary 拉取镜像
|
|
// @Description 拉取一个镜像
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ImagePull true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/pull [post]
|
|
func (r *ContainerController) ImagePull(ctx http.Context) http.Response {
|
|
var request requests.ImagePull
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ImagePull(request); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ImageRemove
|
|
//
|
|
// @Summary 删除镜像
|
|
// @Description 删除一个镜像
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/remove [post]
|
|
func (r *ContainerController) ImageRemove(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.ImageRemove(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ImagePrune
|
|
//
|
|
// @Summary 清理镜像
|
|
// @Description 清理无用的镜像
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/prune [post]
|
|
func (r *ContainerController) ImagePrune(ctx http.Context) http.Response {
|
|
if err := r.container.ImagePrune(); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// ImageInspect
|
|
//
|
|
// @Summary 查看镜像
|
|
// @Description 查看一个镜像的详细信息
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/image/inspect [get]
|
|
func (r *ContainerController) ImageInspect(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.ImageInspect(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// VolumeList
|
|
//
|
|
// @Summary 获取卷列表
|
|
// @Description 获取所有卷列表
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query commonrequests.Paginate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/list [get]
|
|
func (r *ContainerController) VolumeList(ctx http.Context) http.Response {
|
|
var request commonrequests.Paginate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
volumes, err := r.container.VolumeList()
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
startIndex := (request.Page - 1) * request.Limit
|
|
endIndex := request.Page * request.Limit
|
|
if startIndex > len(volumes) {
|
|
return Success(ctx, http.Json{
|
|
"total": 0,
|
|
"items": []any{},
|
|
})
|
|
}
|
|
if endIndex > len(volumes) {
|
|
endIndex = len(volumes)
|
|
}
|
|
paged := volumes[startIndex:endIndex]
|
|
if paged == nil {
|
|
paged = []*volume.Volume{}
|
|
}
|
|
|
|
items := make([]any, 0)
|
|
for _, item := range paged {
|
|
var usage any
|
|
if item.UsageData != nil {
|
|
usage = map[string]any{
|
|
"ref_count": item.UsageData.RefCount,
|
|
"size": tools.FormatBytes(float64(item.UsageData.Size)),
|
|
}
|
|
}
|
|
items = append(items, map[string]any{
|
|
"id": item.Name,
|
|
"created": carbon.Parse(item.CreatedAt).ToDateTimeString(),
|
|
"driver": item.Driver,
|
|
"mount": item.Mountpoint,
|
|
"labels": item.Labels,
|
|
"options": item.Options,
|
|
"scope": item.Scope,
|
|
"status": item.Status,
|
|
"usage": usage,
|
|
})
|
|
}
|
|
|
|
return Success(ctx, http.Json{
|
|
"total": len(volumes),
|
|
"items": items,
|
|
})
|
|
}
|
|
|
|
// VolumeCreate
|
|
//
|
|
// @Summary 创建卷
|
|
// @Description 创建一个卷
|
|
// @Tags 容器
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.VolumeCreate true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/create [post]
|
|
func (r *ContainerController) VolumeCreate(ctx http.Context) http.Response {
|
|
var request requests.VolumeCreate
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.VolumeCreate(request)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data.Name)
|
|
}
|
|
|
|
// VolumeExist
|
|
//
|
|
// @Summary 检查卷是否存在
|
|
// @Description 检查一个卷是否存在
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/exist [get]
|
|
func (r *ContainerController) VolumeExist(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
exist, err := r.container.VolumeExist(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, exist)
|
|
}
|
|
|
|
// VolumeInspect
|
|
//
|
|
// @Summary 查看卷
|
|
// @Description 查看一个卷的详细信息
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data query requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/inspect [get]
|
|
func (r *ContainerController) VolumeInspect(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
data, err := r.container.VolumeInspect(request.ID)
|
|
if err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, data)
|
|
}
|
|
|
|
// VolumeRemove
|
|
//
|
|
// @Summary 删除卷
|
|
// @Description 删除一个卷
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Param data body requests.ID true "request"
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/remove [post]
|
|
func (r *ContainerController) VolumeRemove(ctx http.Context) http.Response {
|
|
var request requests.ID
|
|
if sanitize := SanitizeRequest(ctx, &request); sanitize != nil {
|
|
return sanitize
|
|
}
|
|
|
|
if err := r.container.VolumeRemove(request.ID); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|
|
|
|
// VolumePrune
|
|
//
|
|
// @Summary 清理卷
|
|
// @Description 清理无用的卷
|
|
// @Tags 容器
|
|
// @Produce json
|
|
// @Security BearerToken
|
|
// @Success 200 {object} SuccessResponse
|
|
// @Router /panel/container/volume/prune [post]
|
|
func (r *ContainerController) VolumePrune(ctx http.Context) http.Response {
|
|
if err := r.container.VolumePrune(); err != nil {
|
|
return Error(ctx, http.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
return Success(ctx, nil)
|
|
}
|