mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-10-26 00:36:12 +08:00 
			
		
		
		
	
							parent
							
								
									055216604e
								
							
						
					
					
						commit
						a5a707b923
					
				
					 18 changed files with 938 additions and 61 deletions
				
			
		|  | @ -83,7 +83,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) { | |||
| // @Param request body dto.LogOption true "request" | ||||
| // @Success 200 | ||||
| // @Security ApiKeyAuth | ||||
| // @Router /containers/daemonjson/update [post] | ||||
| // @Router /containers/logoption/update [post] | ||||
| // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"} | ||||
| func (b *BaseApi) UpdateLogOption(c *gin.Context) { | ||||
| 	var req dto.LogOption | ||||
|  | @ -99,6 +99,29 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) { | |||
| 	helper.SuccessWithData(c, nil) | ||||
| } | ||||
| 
 | ||||
| // @Tags Container Docker | ||||
| // @Summary Update docker daemon.json ipv6 option | ||||
| // @Description 修改 docker ipv6 配置 | ||||
| // @Accept json | ||||
| // @Param request body dto.LogOption true "request" | ||||
| // @Success 200 | ||||
| // @Security ApiKeyAuth | ||||
| // @Router /containers/ipv6option/update [post] | ||||
| // @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"} | ||||
| func (b *BaseApi) UpdateIpv6Option(c *gin.Context) { | ||||
| 	var req dto.Ipv6Option | ||||
| 	if err := helper.CheckBind(&req, c); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err := dockerService.UpdateIpv6Option(req); err != nil { | ||||
| 		helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	helper.SuccessWithData(c, nil) | ||||
| } | ||||
| 
 | ||||
| // @Tags Container Docker | ||||
| // @Summary Update docker daemon.json by upload file | ||||
| // @Description 上传替换 docker 配置文件 | ||||
|  |  | |||
|  | @ -135,13 +135,21 @@ type Network struct { | |||
| 	Attachable bool      `json:"attachable"` | ||||
| } | ||||
| type NetworkCreate struct { | ||||
| 	Name    string   `json:"name" validate:"required"` | ||||
| 	Driver  string   `json:"driver" validate:"required"` | ||||
| 	Options []string `json:"options"` | ||||
| 	Subnet  string   `json:"subnet"` | ||||
| 	Gateway string   `json:"gateway"` | ||||
| 	IPRange string   `json:"ipRange"` | ||||
| 	Labels  []string `json:"labels"` | ||||
| 	Name       string          `json:"name" validate:"required"` | ||||
| 	Driver     string          `json:"driver" validate:"required"` | ||||
| 	Options    []string        `json:"options"` | ||||
| 	Ipv4       bool            `json:"ipv4"` | ||||
| 	Subnet     string          `json:"subnet"` | ||||
| 	Gateway    string          `json:"gateway"` | ||||
| 	IPRange    string          `json:"ipRange"` | ||||
| 	AuxAddress []SettingUpdate `json:"auxAddress"` | ||||
| 
 | ||||
| 	Ipv6         bool            `json:"ipv6"` | ||||
| 	SubnetV6     string          `json:"subnetV6"` | ||||
| 	GatewayV6    string          `json:"gatewayV6"` | ||||
| 	IPRangeV6    string          `json:"ipRangeV6"` | ||||
| 	AuxAddressV6 []SettingUpdate `json:"auxAddressV6"` | ||||
| 	Labels       []string        `json:"labels"` | ||||
| } | ||||
| 
 | ||||
| type Volume struct { | ||||
|  |  | |||
|  | @ -14,6 +14,11 @@ type DaemonJsonConf struct { | |||
| 	IPTables     bool     `json:"iptables"` | ||||
| 	CgroupDriver string   `json:"cgroupDriver"` | ||||
| 
 | ||||
| 	Ipv6         bool   `json:"ipv6"` | ||||
| 	FixedCidrV6  string `json:"fixedCidrV6"` | ||||
| 	Ip6Tables    bool   `json:"ip6Tables"` | ||||
| 	Experimental bool   `json:"experimental"` | ||||
| 
 | ||||
| 	LogMaxSize string `json:"logMaxSize"` | ||||
| 	LogMaxFile string `json:"logMaxFile"` | ||||
| } | ||||
|  | @ -23,6 +28,12 @@ type LogOption struct { | |||
| 	LogMaxFile string `json:"logMaxFile"` | ||||
| } | ||||
| 
 | ||||
| type Ipv6Option struct { | ||||
| 	FixedCidrV6  string `json:"fixedCidrV6"` | ||||
| 	Ip6Tables    bool   `json:"ip6Tables" validate:"required"` | ||||
| 	Experimental bool   `json:"experimental"` | ||||
| } | ||||
| 
 | ||||
| type DockerOperation struct { | ||||
| 	Operation string `json:"operation" validate:"required,oneof=start restart stop"` | ||||
| } | ||||
|  |  | |||
|  | @ -116,29 +116,57 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error { | |||
| 		return err | ||||
| 	} | ||||
| 	var ( | ||||
| 		ipam    network.IPAMConfig | ||||
| 		hasConf bool | ||||
| 		ipams    []network.IPAMConfig | ||||
| 		enableV6 bool | ||||
| 	) | ||||
| 	if len(req.Subnet) != 0 { | ||||
| 		ipam.Subnet = req.Subnet | ||||
| 		hasConf = true | ||||
| 	if req.Ipv4 { | ||||
| 		var itemIpam network.IPAMConfig | ||||
| 		if len(req.AuxAddress) != 0 { | ||||
| 			itemIpam.AuxAddress = make(map[string]string) | ||||
| 		} | ||||
| 		if len(req.Subnet) != 0 { | ||||
| 			itemIpam.Subnet = req.Subnet | ||||
| 		} | ||||
| 		if len(req.Gateway) != 0 { | ||||
| 			itemIpam.Gateway = req.Gateway | ||||
| 		} | ||||
| 		if len(req.IPRange) != 0 { | ||||
| 			itemIpam.IPRange = req.IPRange | ||||
| 		} | ||||
| 		for _, addr := range req.AuxAddress { | ||||
| 			itemIpam.AuxAddress[addr.Key] = addr.Value | ||||
| 		} | ||||
| 		ipams = append(ipams, itemIpam) | ||||
| 	} | ||||
| 	if len(req.Gateway) != 0 { | ||||
| 		ipam.Gateway = req.Gateway | ||||
| 		hasConf = true | ||||
| 	} | ||||
| 	if len(req.IPRange) != 0 { | ||||
| 		ipam.IPRange = req.IPRange | ||||
| 		hasConf = true | ||||
| 	if req.Ipv6 { | ||||
| 		enableV6 = true | ||||
| 		var itemIpam network.IPAMConfig | ||||
| 		if len(req.AuxAddress) != 0 { | ||||
| 			itemIpam.AuxAddress = make(map[string]string) | ||||
| 		} | ||||
| 		if len(req.SubnetV6) != 0 { | ||||
| 			itemIpam.Subnet = req.SubnetV6 | ||||
| 		} | ||||
| 		if len(req.GatewayV6) != 0 { | ||||
| 			itemIpam.Gateway = req.GatewayV6 | ||||
| 		} | ||||
| 		if len(req.IPRangeV6) != 0 { | ||||
| 			itemIpam.IPRange = req.IPRangeV6 | ||||
| 		} | ||||
| 		for _, addr := range req.AuxAddressV6 { | ||||
| 			itemIpam.AuxAddress[addr.Key] = addr.Value | ||||
| 		} | ||||
| 		ipams = append(ipams, itemIpam) | ||||
| 	} | ||||
| 
 | ||||
| 	options := types.NetworkCreate{ | ||||
| 		Driver:  req.Driver, | ||||
| 		Options: stringsToMap(req.Options), | ||||
| 		Labels:  stringsToMap(req.Labels), | ||||
| 		EnableIPv6: enableV6, | ||||
| 		Driver:     req.Driver, | ||||
| 		Options:    stringsToMap(req.Options), | ||||
| 		Labels:     stringsToMap(req.Labels), | ||||
| 	} | ||||
| 	if hasConf { | ||||
| 		options.IPAM = &network.IPAM{Config: []network.IPAMConfig{ipam}} | ||||
| 	if len(ipams) != 0 { | ||||
| 		options.IPAM = &network.IPAM{Config: ipams} | ||||
| 	} | ||||
| 	if _, err := client.NetworkCreate(context.TODO(), req.Name, options); err != nil { | ||||
| 		return err | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ type DockerService struct{} | |||
| type IDockerService interface { | ||||
| 	UpdateConf(req dto.SettingUpdate) error | ||||
| 	UpdateLogOption(req dto.LogOption) error | ||||
| 	UpdateIpv6Option(req dto.Ipv6Option) error | ||||
| 	UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error | ||||
| 	LoadDockerStatus() string | ||||
| 	LoadDockerConf() *dto.DaemonJsonConf | ||||
|  | @ -32,13 +33,17 @@ func NewIDockerService() IDockerService { | |||
| } | ||||
| 
 | ||||
| type daemonJsonItem struct { | ||||
| 	Status      string    `json:"status"` | ||||
| 	Mirrors     []string  `json:"registry-mirrors"` | ||||
| 	Registries  []string  `json:"insecure-registries"` | ||||
| 	LiveRestore bool      `json:"live-restore"` | ||||
| 	IPTables    bool      `json:"iptables"` | ||||
| 	ExecOpts    []string  `json:"exec-opts"` | ||||
| 	LogOption   logOption `json:"log-opts"` | ||||
| 	Status       string    `json:"status"` | ||||
| 	Mirrors      []string  `json:"registry-mirrors"` | ||||
| 	Registries   []string  `json:"insecure-registries"` | ||||
| 	LiveRestore  bool      `json:"live-restore"` | ||||
| 	Ipv6         bool      `json:"ipv6"` | ||||
| 	FixedCidrV6  string    `json:"fixed-cidr-v6"` | ||||
| 	Ip6Tables    bool      `json:"ip6tables"` | ||||
| 	Experimental bool      `json:"experimental"` | ||||
| 	IPTables     bool      `json:"iptables"` | ||||
| 	ExecOpts     []string  `json:"exec-opts"` | ||||
| 	LogOption    logOption `json:"log-opts"` | ||||
| } | ||||
| type logOption struct { | ||||
| 	LogMaxSize string `json:"max-size"` | ||||
|  | @ -110,6 +115,10 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf { | |||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	data.Ipv6 = conf.Ipv6 | ||||
| 	data.FixedCidrV6 = conf.FixedCidrV6 | ||||
| 	data.Ip6Tables = conf.Ip6Tables | ||||
| 	data.Experimental = conf.Experimental | ||||
| 	data.LogMaxSize = conf.LogOption.LogMaxSize | ||||
| 	data.LogMaxFile = conf.LogOption.LogMaxFile | ||||
| 	data.Mirrors = conf.Mirrors | ||||
|  | @ -149,6 +158,13 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error { | |||
| 		} else { | ||||
| 			daemonMap["registry-mirrors"] = strings.Split(req.Value, ",") | ||||
| 		} | ||||
| 	case "Ipv6": | ||||
| 		if req.Value == "disable" { | ||||
| 			delete(daemonMap, "ipv6") | ||||
| 			delete(daemonMap, "fixed-cidr-v6") | ||||
| 			delete(daemonMap, "ip6tables") | ||||
| 			delete(daemonMap, "experimental") | ||||
| 		} | ||||
| 	case "LogOption": | ||||
| 		if req.Value == "disable" { | ||||
| 			delete(daemonMap, "log-opts") | ||||
|  | @ -237,6 +253,48 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error { | ||||
| 	if _, err := os.Stat(constant.DaemonJsonPath); err != nil && os.IsNotExist(err) { | ||||
| 		if err = os.MkdirAll(path.Dir(constant.DaemonJsonPath), os.ModePerm); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, _ = os.Create(constant.DaemonJsonPath) | ||||
| 	} | ||||
| 
 | ||||
| 	file, err := os.ReadFile(constant.DaemonJsonPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	daemonMap := make(map[string]interface{}) | ||||
| 	_ = json.Unmarshal(file, &daemonMap) | ||||
| 
 | ||||
| 	daemonMap["ipv6"] = true | ||||
| 	daemonMap["fixed-cidr-v6"] = req.FixedCidrV6 | ||||
| 	if req.Ip6Tables { | ||||
| 		daemonMap["ip6tables"] = req.Ip6Tables | ||||
| 	} | ||||
| 	if req.Experimental { | ||||
| 		daemonMap["experimental"] = req.Experimental | ||||
| 	} | ||||
| 	if len(daemonMap) == 0 { | ||||
| 		_ = os.Remove(constant.DaemonJsonPath) | ||||
| 		return nil | ||||
| 	} | ||||
| 	newJson, err := json.MarshalIndent(daemonMap, "", "\t") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	stdout, err := cmd.Exec("systemctl restart docker") | ||||
| 	if err != nil { | ||||
| 		return errors.New(string(stdout)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error { | ||||
| 	if len(req.File) == 0 { | ||||
| 		_ = os.Remove(constant.DaemonJsonPath) | ||||
|  |  | |||
|  | @ -79,6 +79,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { | |||
| 		baRouter.POST("/docker/operate", baseApi.OperateDocker) | ||||
| 		baRouter.POST("/daemonjson/update", baseApi.UpdateDaemonJson) | ||||
| 		baRouter.POST("/logoption/update", baseApi.UpdateLogOption) | ||||
| 		baRouter.POST("/ipv6option/update", baseApi.UpdateIpv6Option) | ||||
| 		baRouter.POST("/daemonjson/update/byfile", baseApi.UpdateDaemonJsonByFile) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -1414,14 +1414,14 @@ const docTemplate = `{ | |||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker 日志配置", | ||||
|                 "description": "修改 docker 配置信息", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json log option", | ||||
|                 "summary": "Update docker daemon.json", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|  | @ -1429,7 +1429,7 @@ const docTemplate = `{ | |||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                             "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|  | @ -1440,9 +1440,12 @@ const docTemplate = `{ | |||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json log option", | ||||
|                     "formatZH": "更新 docker daemon.json 日志配置", | ||||
|                     "bodyKeys": [ | ||||
|                         "key", | ||||
|                         "value" | ||||
|                     ], | ||||
|                     "formatEN": "Updated the docker daemon.json configuration [key]=\u003e[value]", | ||||
|                     "formatZH": "更新 docker daemon.json 配置 [key]=\u003e[value]", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|  | @ -2029,6 +2032,46 @@ const docTemplate = `{ | |||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/ipv6option/update": { | ||||
|             "post": { | ||||
|                 "security": [ | ||||
|                     { | ||||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker ipv6 配置", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json ipv6 option", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|                         "name": "request", | ||||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 "responses": { | ||||
|                     "200": { | ||||
|                         "description": "OK" | ||||
|                     } | ||||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json ipv6 option", | ||||
|                     "formatZH": "更新 docker daemon.json ipv6 配置", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/limit": { | ||||
|             "get": { | ||||
|                 "security": [ | ||||
|  | @ -2128,6 +2171,46 @@ const docTemplate = `{ | |||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/logoption/update": { | ||||
|             "post": { | ||||
|                 "security": [ | ||||
|                     { | ||||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker 日志配置", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json log option", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|                         "name": "request", | ||||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 "responses": { | ||||
|                     "200": { | ||||
|                         "description": "OK" | ||||
|                     } | ||||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json log option", | ||||
|                     "formatZH": "更新 docker daemon.json 日志配置", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/network": { | ||||
|             "get": { | ||||
|                 "security": [ | ||||
|  | @ -14023,15 +14106,27 @@ const docTemplate = `{ | |||
|                 "cgroupDriver": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "experimental": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "fixedCidrV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "insecureRegistries": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "type": "string" | ||||
|                     } | ||||
|                 }, | ||||
|                 "ip6Tables": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "iptables": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "ipv6": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "isSwarm": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|  | @ -15530,15 +15625,39 @@ const docTemplate = `{ | |||
|                 "name" | ||||
|             ], | ||||
|             "properties": { | ||||
|                 "auxAddress": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                     } | ||||
|                 }, | ||||
|                 "auxAddressV6": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                     } | ||||
|                 }, | ||||
|                 "driver": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "gateway": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "gatewayV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipRange": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipRangeV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipv4": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "ipv6": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "labels": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|  | @ -15556,6 +15675,9 @@ const docTemplate = `{ | |||
|                 }, | ||||
|                 "subnet": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "subnetV6": { | ||||
|                     "type": "string" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|  |  | |||
|  | @ -1407,14 +1407,14 @@ | |||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker 日志配置", | ||||
|                 "description": "修改 docker 配置信息", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json log option", | ||||
|                 "summary": "Update docker daemon.json", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|  | @ -1422,7 +1422,7 @@ | |||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                             "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|  | @ -1433,9 +1433,12 @@ | |||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json log option", | ||||
|                     "formatZH": "更新 docker daemon.json 日志配置", | ||||
|                     "bodyKeys": [ | ||||
|                         "key", | ||||
|                         "value" | ||||
|                     ], | ||||
|                     "formatEN": "Updated the docker daemon.json configuration [key]=\u003e[value]", | ||||
|                     "formatZH": "更新 docker daemon.json 配置 [key]=\u003e[value]", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|  | @ -2022,6 +2025,46 @@ | |||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/ipv6option/update": { | ||||
|             "post": { | ||||
|                 "security": [ | ||||
|                     { | ||||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker ipv6 配置", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json ipv6 option", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|                         "name": "request", | ||||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 "responses": { | ||||
|                     "200": { | ||||
|                         "description": "OK" | ||||
|                     } | ||||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json ipv6 option", | ||||
|                     "formatZH": "更新 docker daemon.json ipv6 配置", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/limit": { | ||||
|             "get": { | ||||
|                 "security": [ | ||||
|  | @ -2121,6 +2164,46 @@ | |||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/logoption/update": { | ||||
|             "post": { | ||||
|                 "security": [ | ||||
|                     { | ||||
|                         "ApiKeyAuth": [] | ||||
|                     } | ||||
|                 ], | ||||
|                 "description": "修改 docker 日志配置", | ||||
|                 "consumes": [ | ||||
|                     "application/json" | ||||
|                 ], | ||||
|                 "tags": [ | ||||
|                     "Container Docker" | ||||
|                 ], | ||||
|                 "summary": "Update docker daemon.json log option", | ||||
|                 "parameters": [ | ||||
|                     { | ||||
|                         "description": "request", | ||||
|                         "name": "request", | ||||
|                         "in": "body", | ||||
|                         "required": true, | ||||
|                         "schema": { | ||||
|                             "$ref": "#/definitions/dto.LogOption" | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 "responses": { | ||||
|                     "200": { | ||||
|                         "description": "OK" | ||||
|                     } | ||||
|                 }, | ||||
|                 "x-panel-log": { | ||||
|                     "BeforeFunctions": [], | ||||
|                     "bodyKeys": [], | ||||
|                     "formatEN": "Updated the docker daemon.json log option", | ||||
|                     "formatZH": "更新 docker daemon.json 日志配置", | ||||
|                     "paramKeys": [] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "/containers/network": { | ||||
|             "get": { | ||||
|                 "security": [ | ||||
|  | @ -14016,15 +14099,27 @@ | |||
|                 "cgroupDriver": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "experimental": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "fixedCidrV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "insecureRegistries": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "type": "string" | ||||
|                     } | ||||
|                 }, | ||||
|                 "ip6Tables": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "iptables": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "ipv6": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "isSwarm": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|  | @ -15523,15 +15618,39 @@ | |||
|                 "name" | ||||
|             ], | ||||
|             "properties": { | ||||
|                 "auxAddress": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                     } | ||||
|                 }, | ||||
|                 "auxAddressV6": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|                         "$ref": "#/definitions/dto.SettingUpdate" | ||||
|                     } | ||||
|                 }, | ||||
|                 "driver": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "gateway": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "gatewayV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipRange": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipRangeV6": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "ipv4": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "ipv6": { | ||||
|                     "type": "boolean" | ||||
|                 }, | ||||
|                 "labels": { | ||||
|                     "type": "array", | ||||
|                     "items": { | ||||
|  | @ -15549,6 +15668,9 @@ | |||
|                 }, | ||||
|                 "subnet": { | ||||
|                     "type": "string" | ||||
|                 }, | ||||
|                 "subnetV6": { | ||||
|                     "type": "string" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|  |  | |||
|  | @ -678,12 +678,20 @@ definitions: | |||
|     properties: | ||||
|       cgroupDriver: | ||||
|         type: string | ||||
|       experimental: | ||||
|         type: boolean | ||||
|       fixedCidrV6: | ||||
|         type: string | ||||
|       insecureRegistries: | ||||
|         items: | ||||
|           type: string | ||||
|         type: array | ||||
|       ip6Tables: | ||||
|         type: boolean | ||||
|       iptables: | ||||
|         type: boolean | ||||
|       ipv6: | ||||
|         type: boolean | ||||
|       isSwarm: | ||||
|         type: boolean | ||||
|       liveRestore: | ||||
|  | @ -1694,12 +1702,28 @@ definitions: | |||
|     type: object | ||||
|   dto.NetworkCreate: | ||||
|     properties: | ||||
|       auxAddress: | ||||
|         items: | ||||
|           $ref: '#/definitions/dto.SettingUpdate' | ||||
|         type: array | ||||
|       auxAddressV6: | ||||
|         items: | ||||
|           $ref: '#/definitions/dto.SettingUpdate' | ||||
|         type: array | ||||
|       driver: | ||||
|         type: string | ||||
|       gateway: | ||||
|         type: string | ||||
|       gatewayV6: | ||||
|         type: string | ||||
|       ipRange: | ||||
|         type: string | ||||
|       ipRangeV6: | ||||
|         type: string | ||||
|       ipv4: | ||||
|         type: boolean | ||||
|       ipv6: | ||||
|         type: boolean | ||||
|       labels: | ||||
|         items: | ||||
|           type: string | ||||
|  | @ -1712,6 +1736,8 @@ definitions: | |||
|         type: array | ||||
|       subnet: | ||||
|         type: string | ||||
|       subnetV6: | ||||
|         type: string | ||||
|     required: | ||||
|     - driver | ||||
|     - name | ||||
|  | @ -5426,27 +5452,29 @@ paths: | |||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       description: 修改 docker 日志配置 | ||||
|       description: 修改 docker 配置信息 | ||||
|       parameters: | ||||
|       - description: request | ||||
|         in: body | ||||
|         name: request | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/dto.LogOption' | ||||
|           $ref: '#/definitions/dto.SettingUpdate' | ||||
|       responses: | ||||
|         "200": | ||||
|           description: OK | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: Update docker daemon.json log option | ||||
|       summary: Update docker daemon.json | ||||
|       tags: | ||||
|       - Container Docker | ||||
|       x-panel-log: | ||||
|         BeforeFunctions: [] | ||||
|         bodyKeys: [] | ||||
|         formatEN: Updated the docker daemon.json log option | ||||
|         formatZH: 更新 docker daemon.json 日志配置 | ||||
|         bodyKeys: | ||||
|         - key | ||||
|         - value | ||||
|         formatEN: Updated the docker daemon.json configuration [key]=>[value] | ||||
|         formatZH: 更新 docker daemon.json 配置 [key]=>[value] | ||||
|         paramKeys: [] | ||||
|   /containers/daemonjson/update/byfile: | ||||
|     post: | ||||
|  | @ -5820,6 +5848,32 @@ paths: | |||
|       summary: Container inspect | ||||
|       tags: | ||||
|       - Container | ||||
|   /containers/ipv6option/update: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       description: 修改 docker ipv6 配置 | ||||
|       parameters: | ||||
|       - description: request | ||||
|         in: body | ||||
|         name: request | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/dto.LogOption' | ||||
|       responses: | ||||
|         "200": | ||||
|           description: OK | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: Update docker daemon.json ipv6 option | ||||
|       tags: | ||||
|       - Container Docker | ||||
|       x-panel-log: | ||||
|         BeforeFunctions: [] | ||||
|         bodyKeys: [] | ||||
|         formatEN: Updated the docker daemon.json ipv6 option | ||||
|         formatZH: 更新 docker daemon.json ipv6 配置 | ||||
|         paramKeys: [] | ||||
|   /containers/limit: | ||||
|     get: | ||||
|       description: 获取容器限制 | ||||
|  | @ -5879,6 +5933,32 @@ paths: | |||
|       summary: Load container log | ||||
|       tags: | ||||
|       - Container | ||||
|   /containers/logoption/update: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       description: 修改 docker 日志配置 | ||||
|       parameters: | ||||
|       - description: request | ||||
|         in: body | ||||
|         name: request | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/dto.LogOption' | ||||
|       responses: | ||||
|         "200": | ||||
|           description: OK | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: Update docker daemon.json log option | ||||
|       tags: | ||||
|       - Container Docker | ||||
|       x-panel-log: | ||||
|         BeforeFunctions: [] | ||||
|         bodyKeys: [] | ||||
|         formatEN: Updated the docker daemon.json log option | ||||
|         formatZH: 更新 docker daemon.json 日志配置 | ||||
|         paramKeys: [] | ||||
|   /containers/network: | ||||
|     get: | ||||
|       consumes: | ||||
|  |  | |||
|  | @ -297,6 +297,12 @@ export namespace Container { | |||
|         liveRestore: boolean; | ||||
|         iptables: boolean; | ||||
|         cgroupDriver: string; | ||||
| 
 | ||||
|         ipv6: boolean; | ||||
|         fixedCidrV6: string; | ||||
|         ip6Tables: boolean; | ||||
|         experimental: boolean; | ||||
| 
 | ||||
|         logMaxSize: string; | ||||
|         logMaxFile: string; | ||||
|     } | ||||
|  |  | |||
|  | @ -179,6 +179,13 @@ export const updateDaemonJson = (key: string, value: string) => { | |||
| export const updateLogOption = (maxSize: string, maxFile: string) => { | ||||
|     return http.post(`/containers/logoption/update`, { logMaxSize: maxSize, logMaxFile: maxFile }, TimeoutEnum.T_60S); | ||||
| }; | ||||
| export const updateIpv6Option = (fixedCidrV6: string, ip6Tables: boolean, experimental: boolean) => { | ||||
|     return http.post( | ||||
|         `/containers/ipv6option/update`, | ||||
|         { fixedCidrV6: fixedCidrV6, ip6Tables: ip6Tables, experimental: experimental }, | ||||
|         TimeoutEnum.T_60S, | ||||
|     ); | ||||
| }; | ||||
| export const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile) => { | ||||
|     return http.post(`/containers/daemonjson/update/byfile`, params); | ||||
| }; | ||||
|  |  | |||
|  | @ -190,6 +190,7 @@ const message = { | |||
|             leechExts: 'Only support letters, numbers and,', | ||||
|             paramSimple: 'Support lowercase letters and numbers, length 1-128', | ||||
|             filePermission: 'File Permission Error', | ||||
|             formatErr: 'Format error, please check and retry', | ||||
|         }, | ||||
|         res: { | ||||
|             paramError: 'The request failed, please try again later!', | ||||
|  | @ -658,6 +659,7 @@ const message = { | |||
|         subnet: 'Subnet', | ||||
|         scope: 'IP Scope', | ||||
|         gateway: 'Gateway', | ||||
|         auxAddress: 'Exclude IP', | ||||
| 
 | ||||
|         volume: 'Volume', | ||||
|         volumeDir: 'Volume dir', | ||||
|  | @ -716,6 +718,12 @@ const message = { | |||
|             'The acceleration URL is preferred to perform operations. If this parameter is set to empty, mirror acceleration is disabled.', | ||||
|         mirrorsHelper2: 'For details, see the official documents, ', | ||||
|         registries: 'Insecure registries', | ||||
|         ipv6Helper: | ||||
|             'When enabling IPv6, you need to add an IPv6 container network. Refer to the official documentation for specific configuration steps.', | ||||
|         ipv6CidrHelper: 'IPv6 address pool range for containers', | ||||
|         ipv6TablesHelper: 'Automatic configuration of Docker IPv6 for iptables rules', | ||||
|         experimentalHelper: | ||||
|             'Enabling ip6tables requires this configuration to be turned on; otherwise, ip6tables will be ignored', | ||||
|         cutLog: 'Log option', | ||||
|         cutLogHelper1: 'The current configuration will only affect newly created containers.', | ||||
|         cutLogHelper2: 'Existing containers need to be recreated for the configuration to take effect.', | ||||
|  |  | |||
|  | @ -189,6 +189,7 @@ const message = { | |||
|             leechExts: '僅支持字母數字和,', | ||||
|             paramSimple: '支持小寫字母和數字,長度 1-128', | ||||
|             filePermission: '權限錯誤', | ||||
|             formatErr: '格式錯誤,檢查後重試', | ||||
|         }, | ||||
|         res: { | ||||
|             paramError: '請求失敗,請稍後重試!', | ||||
|  | @ -642,6 +643,7 @@ const message = { | |||
|         subnet: '子網', | ||||
|         scope: 'IP 範圍', | ||||
|         gateway: '網關', | ||||
|         auxAddress: '排除 IP', | ||||
| 
 | ||||
|         volume: '存儲卷', | ||||
|         volumeDir: '存儲卷目錄', | ||||
|  | @ -692,6 +694,10 @@ const message = { | |||
|         mirrorsHelper: '優先使用加速 URL 執行操作,設置為空則取消鏡像加速。', | ||||
|         mirrorsHelper2: '具體操作配置請參照官方文檔', | ||||
|         registries: '私有倉庫', | ||||
|         ipv6Helper: '開啟 IPv6 後,需要增加 IPv6 的容器網路,具體操作配置請參照官方文檔', | ||||
|         ipv6CidrHelper: '容器的 IPv6 地址池範圍', | ||||
|         ipv6TablesHelper: 'Docker IPv6 對 iptables 規則的自動配置', | ||||
|         experimentalHelper: '開啟 ip6tables 必須開啟此配置,否則 ip6tables 會被忽略', | ||||
|         cutLog: '日誌切割', | ||||
|         cutLogHelper1: '當前配置只會影響新創建的容器;', | ||||
|         cutLogHelper2: '已經創建的容器需要重新創建使配置生效;', | ||||
|  |  | |||
|  | @ -189,6 +189,7 @@ const message = { | |||
|             leechExts: '仅支持字母数字和,', | ||||
|             paramSimple: '支持小写字母和数字,长度1-128', | ||||
|             filePermission: '权限错误', | ||||
|             formatErr: '格式错误,检查后重试', | ||||
|         }, | ||||
|         res: { | ||||
|             paramError: '请求失败,请稍后重试!', | ||||
|  | @ -643,6 +644,7 @@ const message = { | |||
|         subnet: '子网', | ||||
|         scope: 'IP 范围', | ||||
|         gateway: '网关', | ||||
|         auxAddress: '排除 IP', | ||||
| 
 | ||||
|         volume: '存储卷', | ||||
|         volumeDir: '存储卷目录', | ||||
|  | @ -693,6 +695,10 @@ const message = { | |||
|         mirrorsHelper: '优先使用加速 URL 执行操作,设置为空则取消镜像加速。', | ||||
|         mirrorsHelper2: '具体操作配置请参照官方文档', | ||||
|         registries: '私有仓库', | ||||
|         ipv6Helper: '开启 IPv6 后,需要增加 IPv6 的容器网络,具体操作配置请参照官方文档', | ||||
|         ipv6CidrHelper: '容器的 IPv6 地址池范围', | ||||
|         ipv6TablesHelper: 'Docker IPv6 对 iptables 规则的自动配置', | ||||
|         experimentalHelper: '开启 ip6tables 必须开启此配置,否则 ip6tables 会被忽略', | ||||
|         cutLog: '日志切割', | ||||
|         cutLogHelper1: '当前配置只会影响新创建的容器;', | ||||
|         cutLogHelper2: '已经创建的容器需要重新创建使配置生效;', | ||||
|  |  | |||
|  | @ -247,6 +247,33 @@ export function checkIpV4V6(value: string): boolean { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| export function checkIpV6(value: string): boolean { | ||||
|     if (value === '' || typeof value === 'undefined' || value == null) { | ||||
|         return true; | ||||
|     } else { | ||||
|         const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; | ||||
|         const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`; | ||||
|         const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})'; | ||||
|         const IPv6AddressRegExp = new RegExp( | ||||
|             '^(' + | ||||
|                 `(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` + | ||||
|                 `(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` + | ||||
|                 `(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` + | ||||
|                 ')(%[0-9a-zA-Z-.:]{1,})?$', | ||||
|         ); | ||||
|         if (!IPv6AddressRegExp.test(value) && value !== '') { | ||||
|             return true; | ||||
|         } else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export function checkCidr(value: string): boolean { | ||||
|     if (value === '') { | ||||
|         return true; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
|     <el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="30%"> | ||||
|     <el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%"> | ||||
|         <template #header> | ||||
|             <DrawerHeader :header="$t('container.createNetwork')" :back="handleClose" /> | ||||
|         </template> | ||||
|  | @ -17,6 +17,99 @@ | |||
|                             <el-option label="overlay" value="overlay" /> | ||||
|                         </el-select> | ||||
|                     </el-form-item> | ||||
| 
 | ||||
|                     <el-checkbox v-model="form.ipv4">IPv4</el-checkbox> | ||||
|                     <div v-if="form.ipv4"> | ||||
|                         <el-row type="flex" justify="center" :gutter="20"> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.subnet')" prop="subnet"> | ||||
|                                     <el-input placeholder="172.16.10.0/24" clearable v-model.trim="form.subnet" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.gateway')" prop="gateway"> | ||||
|                                     <el-input placeholder="172.16.10.12" clearable v-model.trim="form.gateway" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.scope')" prop="scope"> | ||||
|                                     <el-input placeholder="172.16.10.0/16" clearable v-model.trim="form.scope" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"></el-col> | ||||
|                         </el-row> | ||||
|                         <el-form-item :label="$t('container.auxAddress')" prop="scopeV6"> | ||||
|                             <el-table :data="form.auxAddress" v-if="form.auxAddress.length !== 0"> | ||||
|                                 <el-table-column :label="$t('container.label')" min-width="100"> | ||||
|                                     <template #default="{ row }"> | ||||
|                                         <el-input placeholder="my-router" v-model="row.key" /> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                                 <el-table-column label="IP" min-width="150"> | ||||
|                                     <template #default="{ row }"> | ||||
|                                         <el-input placeholder="172.16.10.13" v-model="row.value" /> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                                 <el-table-column min-width="40"> | ||||
|                                     <template #default="scope"> | ||||
|                                         <el-button link type="primary" @click="handleV4Delete(scope.$index)"> | ||||
|                                             {{ $t('commons.button.delete') }} | ||||
|                                         </el-button> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                             </el-table> | ||||
|                             <el-button class="mt-2" @click="handleV4Add()"> | ||||
|                                 {{ $t('commons.button.add') }} | ||||
|                             </el-button> | ||||
|                         </el-form-item> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <el-checkbox class="mb-4" v-model="form.ipv6">IPv6</el-checkbox> | ||||
|                     <div v-if="form.ipv6"> | ||||
|                         <el-row type="flex" justify="center" :gutter="20"> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.subnet')" prop="subnetV6"> | ||||
|                                     <el-input placeholder="2408:400e::/48" clearable v-model.trim="form.subnetV6" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.gateway')" prop="gatewayV6"> | ||||
|                                     <el-input placeholder="2408:400e::1" clearable v-model.trim="form.gatewayV6" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"> | ||||
|                                 <el-form-item :label="$t('container.scope')" prop="scopeV6"> | ||||
|                                     <el-input placeholder="2408:400e::/64" clearable v-model.trim="form.scopeV6" /> | ||||
|                                 </el-form-item> | ||||
|                             </el-col> | ||||
|                             <el-col :span="12"></el-col> | ||||
|                         </el-row> | ||||
|                         <el-form-item :label="$t('container.auxAddress')" prop="scopeV6"> | ||||
|                             <el-table :data="form.auxAddressV6" v-if="form.auxAddressV6.length !== 0"> | ||||
|                                 <el-table-column :label="$t('container.label')" min-width="100"> | ||||
|                                     <template #default="{ row }"> | ||||
|                                         <el-input placeholder="my-router" v-model="row.key" /> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                                 <el-table-column label="IP" min-width="150"> | ||||
|                                     <template #default="{ row }"> | ||||
|                                         <el-input placeholder="2408:400e::3" v-model="row.value" /> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                                 <el-table-column min-width="40"> | ||||
|                                     <template #default="scope"> | ||||
|                                         <el-button link type="primary" @click="handleV6Delete(scope.$index)"> | ||||
|                                             {{ $t('commons.button.delete') }} | ||||
|                                         </el-button> | ||||
|                                     </template> | ||||
|                                 </el-table-column> | ||||
|                             </el-table> | ||||
|                             <el-button class="mt-2" @click="handleV6Add()"> | ||||
|                                 {{ $t('commons.button.add') }} | ||||
|                             </el-button> | ||||
|                         </el-form-item> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <el-form-item :label="$t('container.option')" prop="optionStr"> | ||||
|                         <el-input | ||||
|                             type="textarea" | ||||
|  | @ -25,15 +118,6 @@ | |||
|                             v-model="form.optionStr" | ||||
|                         /> | ||||
|                     </el-form-item> | ||||
|                     <el-form-item :label="$t('container.subnet')" prop="subnet"> | ||||
|                         <el-input clearable v-model.trim="form.subnet" /> | ||||
|                     </el-form-item> | ||||
|                     <el-form-item :label="$t('container.gateway')" prop="gateway"> | ||||
|                         <el-input clearable v-model.trim="form.gateway" /> | ||||
|                     </el-form-item> | ||||
|                     <el-form-item :label="$t('container.scope')" prop="scope"> | ||||
|                         <el-input clearable v-model.trim="form.scope" /> | ||||
|                     </el-form-item> | ||||
|                     <el-form-item :label="$t('container.tag')" prop="labelStr"> | ||||
|                         <el-input | ||||
|                             type="textarea" | ||||
|  | @ -66,6 +150,7 @@ import { ElForm } from 'element-plus'; | |||
| import { createNetwork } from '@/api/modules/container'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { checkIpV6 } from '@/utils/util'; | ||||
| 
 | ||||
| const loading = ref(false); | ||||
| 
 | ||||
|  | @ -77,9 +162,16 @@ const form = reactive({ | |||
|     optionStr: '', | ||||
|     options: [] as Array<string>, | ||||
|     driver: '', | ||||
|     ipv4: true, | ||||
|     subnet: '', | ||||
|     gateway: '', | ||||
|     scope: '', | ||||
|     auxAddress: [], | ||||
|     ipv6: false, | ||||
|     subnetV6: '', | ||||
|     gatewayV6: '', | ||||
|     scopeV6: '', | ||||
|     auxAddressV6: [], | ||||
| }); | ||||
| 
 | ||||
| const acceptParams = (): void => { | ||||
|  | @ -88,10 +180,17 @@ const acceptParams = (): void => { | |||
|     form.labels = []; | ||||
|     form.optionStr = ''; | ||||
|     form.options = []; | ||||
|     form.driver = ''; | ||||
|     form.driver = 'bridge'; | ||||
|     form.ipv4 = true; | ||||
|     form.subnet = ''; | ||||
|     form.gateway = ''; | ||||
|     form.scope = ''; | ||||
|     form.auxAddress = []; | ||||
|     form.ipv6 = false; | ||||
|     form.subnetV6 = ''; | ||||
|     form.gatewayV6 = ''; | ||||
|     form.scopeV6 = ''; | ||||
|     form.auxAddressV6 = []; | ||||
|     drawerVisible.value = true; | ||||
| }; | ||||
| const emit = defineEmits<{ (e: 'search'): void }>(); | ||||
|  | @ -103,8 +202,65 @@ const handleClose = () => { | |||
| const rules = reactive({ | ||||
|     name: [Rules.requiredInput], | ||||
|     driver: [Rules.requiredSelect], | ||||
|     subnet: [{ validator: checkCidr, trigger: 'blur' }], | ||||
|     gateway: [Rules.ip], | ||||
|     scope: [{ validator: checkCidr, trigger: 'blur' }], | ||||
|     subnetV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }], | ||||
|     gatewayV6: [Rules.ipV6], | ||||
|     scopeV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }], | ||||
| }); | ||||
| 
 | ||||
| function checkCidr(rule: any, value: any, callback: any) { | ||||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } | ||||
|     const reg = | ||||
|         /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/([0-9]|[1-2][0-9]|3[0-2]))?$/; | ||||
|     if (!reg.test(value)) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| function checkFixedCidrV6(rule: any, value: any, callback: any) { | ||||
|     if (value === '') { | ||||
|         callback(); | ||||
|     } | ||||
|     if (!form.subnetV6 || form.subnetV6.indexOf('/') === -1) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     if (checkIpV6(form.subnetV6.split('/')[0])) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/; | ||||
|     if (!reg.test(form.subnetV6.split('/')[1])) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| const handleV4Add = () => { | ||||
|     let item = { | ||||
|         key: '', | ||||
|         value: '', | ||||
|     }; | ||||
|     form.auxAddress.push(item); | ||||
| }; | ||||
| const handleV4Delete = (index: number) => { | ||||
|     form.auxAddress.splice(index, 1); | ||||
| }; | ||||
| 
 | ||||
| const handleV6Add = () => { | ||||
|     let item = { | ||||
|         key: '', | ||||
|         value: '', | ||||
|     }; | ||||
|     form.auxAddressV6.push(item); | ||||
| }; | ||||
| const handleV6Delete = (index: number) => { | ||||
|     form.auxAddressV6.splice(index, 1); | ||||
| }; | ||||
| 
 | ||||
| type FormInstance = InstanceType<typeof ElForm>; | ||||
| const formRef = ref<FormInstance>(); | ||||
| const onSubmit = async (formEl: FormInstance | undefined) => { | ||||
|  |  | |||
|  | @ -100,6 +100,19 @@ | |||
|                                 </el-input> | ||||
|                             </el-form-item> | ||||
| 
 | ||||
|                             <el-form-item label="ipv6" prop="ipv6"> | ||||
|                                 <el-switch v-model="form.ipv6" @change="handleIPv6"></el-switch> | ||||
|                                 <span class="input-help"></span> | ||||
|                                 <div v-if="ipv6OptionShow"> | ||||
|                                     <el-tag>{{ $t('container.subnet') }}: {{ form.fixedCidrV6 }}</el-tag> | ||||
|                                     <div> | ||||
|                                         <el-button @click="handleIPv6" type="primary" link> | ||||
|                                             {{ $t('commons.button.view') }} | ||||
|                                         </el-button> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </el-form-item> | ||||
| 
 | ||||
|                             <el-form-item :label="$t('container.cutLog')" prop="hasLogOption"> | ||||
|                                 <el-switch v-model="form.logOptionShow" @change="handleLogOption"></el-switch> | ||||
|                                 <span class="input-help"></span> | ||||
|  | @ -206,6 +219,8 @@ | |||
|         <Mirror ref="mirrorRef" @search="search" /> | ||||
|         <Registry ref="registriesRef" @search="search" /> | ||||
|         <LogOption ref="logOptionRef" @search="search" /> | ||||
|         <Ipv6Option ref="ipv6OptionRef" @search="search" /> | ||||
|         <ConfirmDialog ref="confirmDialogRefIpv6" @confirm="onSaveIPv6" @cancel="search" /> | ||||
|         <ConfirmDialog ref="confirmDialogRefIptable" @confirm="onSubmitOpenIPtable" @cancel="search" /> | ||||
|         <ConfirmDialog ref="confirmDialogRefLog" @confirm="onSubmitSaveLog" @cancel="search" /> | ||||
|         <ConfirmDialog ref="confirmDialogRefLive" @confirm="onSubmitSaveLive" @cancel="search" /> | ||||
|  | @ -224,6 +239,7 @@ import { oneDark } from '@codemirror/theme-one-dark'; | |||
| import Mirror from '@/views/container/setting/mirror/index.vue'; | ||||
| import Registry from '@/views/container/setting/registry/index.vue'; | ||||
| import LogOption from '@/views/container/setting/log/index.vue'; | ||||
| import Ipv6Option from '@/views/container/setting/ipv6/index.vue'; | ||||
| import ConfirmDialog from '@/components/confirm-dialog/index.vue'; | ||||
| import i18n from '@/lang'; | ||||
| import { | ||||
|  | @ -245,13 +261,16 @@ const extensions = [javascript(), oneDark]; | |||
| const confShowType = ref('base'); | ||||
| 
 | ||||
| const logOptionRef = ref(); | ||||
| const ipv6OptionRef = ref(); | ||||
| const confirmDialogRefLog = ref(); | ||||
| const mirrorRef = ref(); | ||||
| const registriesRef = ref(); | ||||
| const confirmDialogRefLive = ref(); | ||||
| const confirmDialogRefCgroup = ref(); | ||||
| const confirmDialogRefIptable = ref(); | ||||
| const confirmDialogRefIpv6 = ref(); | ||||
| const logOptionShow = ref(); | ||||
| const ipv6OptionShow = ref(); | ||||
| 
 | ||||
| const form = reactive({ | ||||
|     isSwarm: false, | ||||
|  | @ -262,6 +281,12 @@ const form = reactive({ | |||
|     liveRestore: false, | ||||
|     iptables: true, | ||||
|     cgroupDriver: '', | ||||
| 
 | ||||
|     ipv6: false, | ||||
|     fixedCidrV6: '', | ||||
|     ip6Tables: false, | ||||
|     experimental: false, | ||||
| 
 | ||||
|     logOptionShow: false, | ||||
|     logMaxSize: '', | ||||
|     logMaxFile: 3, | ||||
|  | @ -292,6 +317,27 @@ const onChangeMirrors = () => { | |||
| const onChangeRegistries = () => { | ||||
|     registriesRef.value.acceptParams({ registries: form.registries }); | ||||
| }; | ||||
| 
 | ||||
| const handleIPv6 = async () => { | ||||
|     if (form.ipv6) { | ||||
|         ipv6OptionRef.value.acceptParams({ | ||||
|             fixedCidrV6: form.fixedCidrV6, | ||||
|             ip6Tables: form.ip6Tables, | ||||
|             experimental: form.experimental, | ||||
|         }); | ||||
|         return; | ||||
|     } | ||||
|     let params = { | ||||
|         header: i18n.global.t('database.confChange'), | ||||
|         operationInfo: i18n.global.t('database.restartNowHelper'), | ||||
|         submitInputInfo: i18n.global.t('database.restartNow'), | ||||
|     }; | ||||
|     confirmDialogRefIpv6.value!.acceptParams(params); | ||||
| }; | ||||
| const onSaveIPv6 = () => { | ||||
|     save('Ipv6', 'disable'); | ||||
| }; | ||||
| 
 | ||||
| const handleLogOption = async () => { | ||||
|     if (form.logOptionShow) { | ||||
|         logOptionRef.value.acceptParams({ logMaxSize: form.logMaxSize, logMaxFile: form.logMaxFile }); | ||||
|  | @ -445,6 +491,11 @@ const search = async () => { | |||
|         form.logOptionShow = false; | ||||
|         logOptionShow.value = false; | ||||
|     } | ||||
|     form.ipv6 = res.data.ipv6; | ||||
|     ipv6OptionShow.value = form.ipv6; | ||||
|     form.fixedCidrV6 = res.data.fixedCidrV6; | ||||
|     form.ip6Tables = res.data.ip6Tables; | ||||
|     form.experimental = res.data.experimental; | ||||
| }; | ||||
| 
 | ||||
| onMounted(() => { | ||||
|  |  | |||
							
								
								
									
										157
									
								
								frontend/src/views/container/setting/ipv6/index.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								frontend/src/views/container/setting/ipv6/index.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,157 @@ | |||
| <template> | ||||
|     <div> | ||||
|         <el-drawer | ||||
|             v-model="drawerVisible" | ||||
|             :destroy-on-close="true" | ||||
|             :close-on-click-modal="false" | ||||
|             @close="handleClose" | ||||
|             size="30%" | ||||
|         > | ||||
|             <template #header> | ||||
|                 <DrawerHeader header="IPv6" :back="handleClose" /> | ||||
|             </template> | ||||
|             <el-alert class="common-prompt" :closable="false" type="warning"> | ||||
|                 <template #default> | ||||
|                     <span class="input-help"> | ||||
|                         {{ $t('container.ipv6Helper') }} | ||||
|                         <el-link | ||||
|                             style="font-size: 12px; margin-left: 5px" | ||||
|                             icon="Position" | ||||
|                             @click="toDoc()" | ||||
|                             type="primary" | ||||
|                         > | ||||
|                             {{ $t('firewall.quickJump') }} | ||||
|                         </el-link> | ||||
|                     </span> | ||||
|                 </template> | ||||
|             </el-alert> | ||||
| 
 | ||||
|             <el-form :model="form" ref="formRef" :rules="rules" v-loading="loading" label-position="top"> | ||||
|                 <el-row type="flex" justify="center"> | ||||
|                     <el-col :span="22"> | ||||
|                         <el-form-item prop="fixedCidrV6" :label="$t('container.subnet')"> | ||||
|                             <el-input v-model="form.fixedCidrV6" /> | ||||
|                             <span class="input-help">{{ $t('container.ipv6CidrHelper') }}</span> | ||||
|                         </el-form-item> | ||||
|                         <el-form-item> | ||||
|                             <el-checkbox v-model="showMore" :label="$t('app.advanced')" /> | ||||
|                         </el-form-item> | ||||
|                         <div v-if="showMore"> | ||||
|                             <el-form-item prop="ip6Tables" label="ip6tables"> | ||||
|                                 <el-switch v-model="form.ip6Tables"></el-switch> | ||||
|                                 <span class="input-help">{{ $t('container.ipv6TablesHelper') }}</span> | ||||
|                             </el-form-item> | ||||
|                             <el-form-item prop="experimental" label="experimental"> | ||||
|                                 <el-switch v-model="form.experimental"></el-switch> | ||||
|                                 <span class="input-help">{{ $t('container.experimentalHelper') }}</span> | ||||
|                             </el-form-item> | ||||
|                         </div> | ||||
|                     </el-col> | ||||
|                 </el-row> | ||||
|             </el-form> | ||||
|             <template #footer> | ||||
|                 <span class="dialog-footer"> | ||||
|                     <el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button> | ||||
|                     <el-button :disabled="loading" type="primary" @click="onSave(formRef)"> | ||||
|                         {{ $t('commons.button.confirm') }} | ||||
|                     </el-button> | ||||
|                 </span> | ||||
|             </template> | ||||
|         </el-drawer> | ||||
| 
 | ||||
|         <ConfirmDialog ref="confirmDialogRef" @confirm="onSubmitSave"></ConfirmDialog> | ||||
|     </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { reactive, ref } from 'vue'; | ||||
| import i18n from '@/lang'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { FormInstance } from 'element-plus'; | ||||
| import { updateIpv6Option } from '@/api/modules/container'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| import { checkIpV6 } from '@/utils/util'; | ||||
| 
 | ||||
| const loading = ref(); | ||||
| const drawerVisible = ref(); | ||||
| const confirmDialogRef = ref(); | ||||
| const formRef = ref(); | ||||
| const showMore = ref(false); | ||||
| 
 | ||||
| interface DialogProps { | ||||
|     fixedCidrV6: string; | ||||
|     ip6Tables: boolean; | ||||
|     experimental: boolean; | ||||
| } | ||||
| 
 | ||||
| const form = reactive({ | ||||
|     fixedCidrV6: '', | ||||
|     ip6Tables: false, | ||||
|     experimental: false, | ||||
| }); | ||||
| const rules = reactive({ | ||||
|     fixedCidrV6: [{ validator: checkFixedCidrV6, trigger: 'blur', required: true }], | ||||
| }); | ||||
| 
 | ||||
| function checkFixedCidrV6(rule: any, value: any, callback: any) { | ||||
|     if (!form.fixedCidrV6 || form.fixedCidrV6.indexOf('/') === -1) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     if (checkIpV6(form.fixedCidrV6.split('/')[0])) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/; | ||||
|     if (!reg.test(form.fixedCidrV6.split('/')[1])) { | ||||
|         return callback(new Error(i18n.global.t('commons.rule.formatErr'))); | ||||
|     } | ||||
|     callback(); | ||||
| } | ||||
| 
 | ||||
| const toDoc = () => { | ||||
|     window.open('https://1panel.cn/docs/user_manual/containers/setting/', '_blank', 'noopener,noreferrer'); | ||||
| }; | ||||
| 
 | ||||
| const emit = defineEmits<{ (e: 'search'): void }>(); | ||||
| 
 | ||||
| const acceptParams = (params: DialogProps): void => { | ||||
|     form.fixedCidrV6 = params.fixedCidrV6; | ||||
|     form.ip6Tables = params.ip6Tables; | ||||
|     form.experimental = params.experimental; | ||||
|     drawerVisible.value = true; | ||||
| }; | ||||
| 
 | ||||
| const onSave = async (formEl: FormInstance | undefined) => { | ||||
|     if (!formEl) return; | ||||
|     formEl.validate(async (valid) => { | ||||
|         if (!valid) return; | ||||
|         let params = { | ||||
|             header: i18n.global.t('database.confChange'), | ||||
|             operationInfo: i18n.global.t('database.restartNowHelper'), | ||||
|             submitInputInfo: i18n.global.t('database.restartNow'), | ||||
|         }; | ||||
|         confirmDialogRef.value!.acceptParams(params); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| const onSubmitSave = async () => { | ||||
|     loading.value = true; | ||||
|     await updateIpv6Option(form.fixedCidrV6, form.ip6Tables, form.experimental) | ||||
|         .then(() => { | ||||
|             loading.value = false; | ||||
|             drawerVisible.value = false; | ||||
|             emit('search'); | ||||
|             MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|             loading.value = false; | ||||
|         }); | ||||
| }; | ||||
| 
 | ||||
| const handleClose = () => { | ||||
|     emit('search'); | ||||
|     drawerVisible.value = false; | ||||
| }; | ||||
| 
 | ||||
| defineExpose({ | ||||
|     acceptParams, | ||||
| }); | ||||
| </script> | ||||
		Loading…
	
	Add table
		
		Reference in a new issue