diff --git a/backend/app/dto/container.go b/backend/app/dto/container.go index 323c95456..40035333b 100644 --- a/backend/app/dto/container.go +++ b/backend/app/dto/container.go @@ -63,8 +63,10 @@ type VolumeHelper struct { Mode string `json:"mode"` } type PortHelper struct { - ContainerPort int `json:"containerPort"` - HostPort int `json:"hostPort"` + HostIP string `json:"hostIP"` + HostPort string `json:"hostPort"` + ContainerPort string `json:"containerPort"` + Protocol string `json:"protocol"` } type ContainerLog struct { diff --git a/backend/app/service/container.go b/backend/app/service/container.go index a8fb16631..b96d43847 100644 --- a/backend/app/service/container.go +++ b/backend/app/service/container.go @@ -168,8 +168,19 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) { func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error { if len(req.ExposedPorts) != 0 { for _, port := range req.ExposedPorts { - if common.ScanPort(port.HostPort) { - return buserr.WithDetail(constant.ErrPortInUsed, port.HostPort, nil) + if strings.Contains(port.HostPort, "-") { + portStart, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[0]) + portEnd, _ := strconv.Atoi(strings.Split(port.HostPort, "-")[1]) + for i := portStart; i <= portEnd; i++ { + if common.ScanPort(i) { + return buserr.WithDetail(constant.ErrPortInUsed, i, nil) + } + } + } else { + portItem, _ := strconv.Atoi(port.HostPort) + if common.ScanPort(portItem) { + return buserr.WithDetail(constant.ErrPortInUsed, portItem, nil) + } } } } @@ -202,8 +213,8 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error { if len(req.ExposedPorts) != 0 { hostConf.PortBindings = make(nat.PortMap) for _, port := range req.ExposedPorts { - bindItem := nat.PortBinding{HostPort: strconv.Itoa(port.HostPort)} - hostConf.PortBindings[nat.Port(fmt.Sprintf("%d/tcp", port.ContainerPort))] = []nat.PortBinding{bindItem} + bindItem := nat.PortBinding{HostPort: port.HostPort, HostIP: port.HostIP} + hostConf.PortBindings[nat.Port(fmt.Sprintf("%s/%s", port.ContainerPort, port.Protocol))] = []nat.PortBinding{bindItem} } } if len(req.Volumes) != 0 { diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index b6e4c2648..8453c2d99 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -27,8 +27,11 @@ export namespace Container { restartPolicy: string; } export interface Port { - containerPort: number; - hostPort: number; + host: string; + hostIP: string; + containerPort: string; + hostPort: string; + protocol: string; } export interface Volume { sourceDir: string; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index b5cc7df46..eec7a8cdc 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -455,10 +455,11 @@ const message = { containerTerminal: 'Terminal', port: 'Port', + server: 'Host', + serverExample: 'e.g. 80, 80-88, ip:80 or ip:80-88', + contianerExample: 'e.g. 80 or 80-88', exposePort: 'Expose port', exposeAll: 'Expose all', - containerPort: 'Container port', - serverPort: 'Host port', cmd: 'Command', cmdHelper: 'Example: echo "hello"', autoRemove: 'Auto remove', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 0ac9d4927..fa96d2d89 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -472,10 +472,11 @@ const message = { emptyUser: '为空时,将使用容器默认的用户登录', port: '端口', + server: '服务器', + serverExample: '例如: 80, 80-88, ip:80 或者 ip:80-88', + contianerExample: '例如: 80 或者 80-88', exposePort: '暴露端口', exposeAll: '暴露所有', - containerPort: '容器端口', - serverPort: '服务器端口', cmd: '启动命令', cmdHelper: '例:echo "hello"', autoRemove: '容器退出后自动删除容器', diff --git a/frontend/src/views/container/container/create/index.vue b/frontend/src/views/container/container/create/index.vue index 2895caaa7..3cefcda64 100644 --- a/frontend/src/views/container/container/create/index.vue +++ b/frontend/src/views/container/container/create/index.vue @@ -29,33 +29,38 @@ - - + - - +
- + + - + + + +
- + - + + + + + + {{ $t('commons.button.delete') }} @@ -204,6 +209,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue'; import { listImage, listVolume, createContainer } from '@/api/modules/container'; import { Container } from '@/api/interface/container'; import { MsgError, MsgSuccess } from '@/utils/message'; +import { checkIp, checkPort } from '@/utils/util'; const loading = ref(false); @@ -277,8 +283,11 @@ const formRef = ref(); const handlePortsAdd = () => { let item = { - containerPort: null, - hostPort: null, + host: '', + hostIP: '', + containerPort: '', + hostPort: '', + protocol: 'tcp', }; form.exposedPorts.push(item); }; @@ -327,6 +336,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => { if (form.cmdStr.length !== 0) { form.cmd = form.cmdStr.split(' '); } + if (!checkPortValid()) { + return; + } switch (form.memoryUnit) { case 'KB': form.memory = form.memoryItem * 1024; @@ -352,6 +364,54 @@ const onSubmit = async (formEl: FormInstance | undefined) => { }); }; +const checkPortValid = async () => { + if (form.exposedPorts.length === 0) { + return true; + } + for (const port of form.exposedPorts) { + if (port.host.indexOf(':') !== -1) { + port.hostIP = port.host.split(':')[0]; + if (checkIp(port.hostIP)) { + MsgError(i18n.global.t('firewall.addressFormatError')); + return false; + } + port.hostPort = port.host.split(':')[1]; + } else { + port.hostPort = port.host; + } + if (port.hostPort.indexOf('-') !== -1) { + if (checkPort(port.hostPort.split('-')[0])) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + if (checkPort(port.hostPort.split('-')[1])) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + } else { + if (checkPort(port.hostPort)) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + } + if (port.containerPort.indexOf('-') !== -1) { + if (checkPort(port.containerPort.split('-')[0])) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + if (checkPort(port.containerPort.split('-')[1])) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + } else { + if (checkPort(port.containerPort)) { + MsgError(i18n.global.t('firewall.portFormatError')); + return false; + } + } + } + return true; +}; defineExpose({ acceptParams, });