feat: docker 配置界面样式统一 (#1173)

pull/1177/head
ssongliu 2 years ago committed by GitHub
parent 2d6925ac4f
commit ec843f2396
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -58,13 +58,13 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
// @Summary Update docker daemon.json
// @Description 修改 docker 配置信息
// @Accept json
// @Param request body dto.DaemonJsonConf true "request"
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"}
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
var req dto.DaemonJsonConf
var req dto.SettingUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@ -78,6 +78,30 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Container Docker
// @Summary Update docker daemon.json log option
// @Description 修改 docker 日志配置
// @Accept json
// @Param request body dto.LogOption true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := dockerService.UpdateLogOption(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 配置文件

@ -18,6 +18,11 @@ type DaemonJsonConf struct {
LogMaxFile string `json:"logMaxFile"`
}
type LogOption struct {
LogMaxSize string `json:"logMaxSize"`
LogMaxFile string `json:"logMaxFile"`
}
type DockerOperation struct {
Operation string `json:"operation" validate:"required,oneof=start restart stop"`
}

@ -4,6 +4,7 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"path"
"strings"
@ -18,7 +19,8 @@ import (
type DockerService struct{}
type IDockerService interface {
UpdateConf(req dto.DaemonJsonConf) error
UpdateConf(req dto.SettingUpdate) error
UpdateLogOption(req dto.LogOption) error
UpdateConfByFile(info dto.DaemonJsonUpdateByFile) error
LoadDockerStatus() string
LoadDockerConf() *dto.DaemonJsonConf
@ -95,6 +97,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
return &data
}
if err := json.Unmarshal(arr, &conf); err != nil {
fmt.Println(err)
return &data
}
if _, ok := deamonMap["iptables"]; !ok {
@ -116,7 +119,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
return &data
}
func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
func (u *DockerService) UpdateConf(req dto.SettingUpdate) 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
@ -131,45 +134,88 @@ func (u *DockerService) UpdateConf(req dto.DaemonJsonConf) error {
deamonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &deamonMap)
if len(req.Registries) == 0 {
switch req.Key {
case "Registries":
if len(req.Value) == 0 {
delete(deamonMap, "insecure-registries")
} else {
deamonMap["insecure-registries"] = req.Registries
deamonMap["insecure-registries"] = strings.Split(req.Value, ",")
}
if len(req.Mirrors) == 0 {
case "Mirrors":
if len(req.Value) == 0 {
delete(deamonMap, "registry-mirrors")
} else {
deamonMap["registry-mirrors"] = req.Mirrors
deamonMap["registry-mirrors"] = strings.Split(req.Value, ",")
}
changeLogOption(deamonMap, req.LogMaxFile, req.LogMaxSize)
if !req.LiveRestore {
case "LogOption":
if req.Value == "disable" {
delete(deamonMap, "log-opts")
}
case "LiveRestore":
if req.Value == "disable" {
delete(deamonMap, "live-restore")
} else {
deamonMap["live-restore"] = req.LiveRestore
deamonMap["live-restore"] = true
}
if req.IPTables {
case "IPtables":
if req.Value == "enable" {
delete(deamonMap, "iptables")
} else {
deamonMap["iptables"] = false
}
case "Dirver":
if opts, ok := deamonMap["exec-opts"]; ok {
if optsValue, isArray := opts.([]interface{}); isArray {
for i := 0; i < len(optsValue); i++ {
if opt, isStr := optsValue[i].(string); isStr {
if strings.HasPrefix(opt, "native.cgroupdriver=") {
optsValue[i] = "native.cgroupdriver=" + req.CgroupDriver
optsValue[i] = "native.cgroupdriver=" + req.Value
break
}
}
}
}
} else {
if req.CgroupDriver == "systemd" {
if req.Value == "systemd" {
deamonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
}
}
}
if len(deamonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
return nil
}
newJson, err := json.MarshalIndent(deamonMap, "", "\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) UpdateLogOption(req dto.LogOption) 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
}
deamonMap := make(map[string]interface{})
_ = json.Unmarshal(file, &deamonMap)
changeLogOption(deamonMap, req.LogMaxFile, req.LogMaxSize)
if len(deamonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
return nil

@ -379,7 +379,7 @@ func (u *FirewallService) pingStatus() string {
return constant.StatusDisable
}
func (u *FirewallService) updatePingStatus(enabel string) error {
func (u *FirewallService) updatePingStatus(enable string) error {
lineBytes, err := os.ReadFile(confPath)
if err != nil {
return err
@ -389,14 +389,14 @@ func (u *FirewallService) updatePingStatus(enabel string) error {
hasLine := false
for _, line := range files {
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
hasLine = true
} else {
newFiles = append(newFiles, line)
}
}
if !hasLine {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enabel)
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
}
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {

@ -68,6 +68,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.GET("/docker/status", baseApi.LoadDockerStatus)
baRouter.POST("/docker/operate", baseApi.OperateDocker)
baRouter.POST("/daemonjson/update", baseApi.UpdateDaemonJson)
baRouter.POST("/logoption/update", baseApi.UpdateLogOption)
baRouter.POST("/daemonjson/update/byfile", baseApi.UpdateDaemonJsonByFile)
}
}

@ -1241,14 +1241,14 @@ var doc = `{
"ApiKeyAuth": []
}
],
"description": "修改 docker 配置信息",
"description": "修改 docker 日志配置",
"consumes": [
"application/json"
],
"tags": [
"Container Docker"
],
"summary": "Update docker daemon.json",
"summary": "Update docker daemon.json log option",
"parameters": [
{
"description": "request",
@ -1256,7 +1256,7 @@ var doc = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.DaemonJsonConf"
"$ref": "#/definitions/dto.LogOption"
}
}
],
@ -1268,8 +1268,8 @@ var doc = `{
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "Updated the docker daemon.json configuration",
"formatZH": "更新 docker daemon.json 配置",
"formatEN": "Updated the docker daemon.json log option",
"formatZH": "更新 docker daemon.json 日志配置",
"paramKeys": []
}
}
@ -9040,6 +9040,72 @@ var doc = `{
}
}
},
"/websites/leech": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防盗链配置",
"consumes": [
"application/json"
],
"tags": [
"Website"
],
"summary": "Get AntiLeech conf",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.NginxCommonReq"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/websites/leech/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新防盗链配置",
"consumes": [
"application/json"
],
"tags": [
"Website"
],
"summary": "Update AntiLeech",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.NginxAntiLeechUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/websites/list": {
"get": {
"security": [
@ -10691,6 +10757,9 @@ var doc = `{
"script": {
"type": "string"
},
"second": {
"type": "integer"
},
"sourceDir": {
"type": "string"
},
@ -10770,6 +10839,9 @@ var doc = `{
"script": {
"type": "string"
},
"second": {
"type": "integer"
},
"sourceDir": {
"type": "string"
},
@ -11531,6 +11603,17 @@ var doc = `{
}
}
},
"dto.LogOption": {
"type": "object",
"properties": {
"logMaxFile": {
"type": "string"
},
"logMaxSize": {
"type": "string"
}
}
},
"dto.Login": {
"type": "object",
"properties": {
@ -12501,6 +12584,9 @@ var doc = `{
"mfaStatus": {
"type": "string"
},
"monitorInterval": {
"type": "string"
},
"monitorStatus": {
"type": "string"
},
@ -13634,6 +13720,53 @@ var doc = `{
}
}
},
"request.NginxAntiLeechUpdate": {
"type": "object",
"required": [
"enable",
"extends",
"return",
"websiteID"
],
"properties": {
"blocked": {
"type": "boolean"
},
"cache": {
"type": "boolean"
},
"cacheTime": {
"type": "integer"
},
"cacheUint": {
"type": "string"
},
"enable": {
"type": "boolean"
},
"extends": {
"type": "string"
},
"logEnable": {
"type": "boolean"
},
"noneRef": {
"type": "boolean"
},
"return": {
"type": "string"
},
"serverNames": {
"type": "array",
"items": {
"type": "string"
}
},
"websiteID": {
"type": "integer"
}
}
},
"request.NginxAuthReq": {
"type": "object",
"required": [
@ -13671,6 +13804,17 @@ var doc = `{
}
}
},
"request.NginxCommonReq": {
"type": "object",
"required": [
"websiteID"
],
"properties": {
"websiteID": {
"type": "integer"
}
}
},
"request.NginxConfigFileUpdate": {
"type": "object",
"required": [
@ -14198,9 +14342,15 @@ var doc = `{
"type": "object",
"required": [
"id",
"params"
"scope"
],
"properties": {
"disableFunctions": {
"type": "array",
"items": {
"type": "string"
}
},
"id": {
"type": "integer"
},
@ -14209,6 +14359,12 @@ var doc = `{
"additionalProperties": {
"type": "string"
}
},
"scope": {
"type": "string"
},
"uploadMaxSize": {
"type": "string"
}
}
},
@ -14806,11 +14962,20 @@ var doc = `{
"response.PHPConfig": {
"type": "object",
"properties": {
"disableFunctions": {
"type": "array",
"items": {
"type": "string"
}
},
"params": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"uploadMaxSize": {
"type": "string"
}
}
},

@ -1227,14 +1227,14 @@
"ApiKeyAuth": []
}
],
"description": "修改 docker 配置信息",
"description": "修改 docker 日志配置",
"consumes": [
"application/json"
],
"tags": [
"Container Docker"
],
"summary": "Update docker daemon.json",
"summary": "Update docker daemon.json log option",
"parameters": [
{
"description": "request",
@ -1242,7 +1242,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.DaemonJsonConf"
"$ref": "#/definitions/dto.LogOption"
}
}
],
@ -1254,8 +1254,8 @@
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "Updated the docker daemon.json configuration",
"formatZH": "更新 docker daemon.json 配置",
"formatEN": "Updated the docker daemon.json log option",
"formatZH": "更新 docker daemon.json 日志配置",
"paramKeys": []
}
}
@ -9026,6 +9026,72 @@
}
}
},
"/websites/leech": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取防盗链配置",
"consumes": [
"application/json"
],
"tags": [
"Website"
],
"summary": "Get AntiLeech conf",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.NginxCommonReq"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/websites/leech/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新防盗链配置",
"consumes": [
"application/json"
],
"tags": [
"Website"
],
"summary": "Update AntiLeech",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.NginxAntiLeechUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/websites/list": {
"get": {
"security": [
@ -10677,6 +10743,9 @@
"script": {
"type": "string"
},
"second": {
"type": "integer"
},
"sourceDir": {
"type": "string"
},
@ -10756,6 +10825,9 @@
"script": {
"type": "string"
},
"second": {
"type": "integer"
},
"sourceDir": {
"type": "string"
},
@ -11517,6 +11589,17 @@
}
}
},
"dto.LogOption": {
"type": "object",
"properties": {
"logMaxFile": {
"type": "string"
},
"logMaxSize": {
"type": "string"
}
}
},
"dto.Login": {
"type": "object",
"properties": {
@ -12487,6 +12570,9 @@
"mfaStatus": {
"type": "string"
},
"monitorInterval": {
"type": "string"
},
"monitorStatus": {
"type": "string"
},
@ -13620,6 +13706,53 @@
}
}
},
"request.NginxAntiLeechUpdate": {
"type": "object",
"required": [
"enable",
"extends",
"return",
"websiteID"
],
"properties": {
"blocked": {
"type": "boolean"
},
"cache": {
"type": "boolean"
},
"cacheTime": {
"type": "integer"
},
"cacheUint": {
"type": "string"
},
"enable": {
"type": "boolean"
},
"extends": {
"type": "string"
},
"logEnable": {
"type": "boolean"
},
"noneRef": {
"type": "boolean"
},
"return": {
"type": "string"
},
"serverNames": {
"type": "array",
"items": {
"type": "string"
}
},
"websiteID": {
"type": "integer"
}
}
},
"request.NginxAuthReq": {
"type": "object",
"required": [
@ -13657,6 +13790,17 @@
}
}
},
"request.NginxCommonReq": {
"type": "object",
"required": [
"websiteID"
],
"properties": {
"websiteID": {
"type": "integer"
}
}
},
"request.NginxConfigFileUpdate": {
"type": "object",
"required": [
@ -14184,9 +14328,15 @@
"type": "object",
"required": [
"id",
"params"
"scope"
],
"properties": {
"disableFunctions": {
"type": "array",
"items": {
"type": "string"
}
},
"id": {
"type": "integer"
},
@ -14195,6 +14345,12 @@
"additionalProperties": {
"type": "string"
}
},
"scope": {
"type": "string"
},
"uploadMaxSize": {
"type": "string"
}
}
},
@ -14792,11 +14948,20 @@
"response.PHPConfig": {
"type": "object",
"properties": {
"disableFunctions": {
"type": "array",
"items": {
"type": "string"
}
},
"params": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"uploadMaxSize": {
"type": "string"
}
}
},

@ -400,6 +400,8 @@ definitions:
type: integer
script:
type: string
second:
type: integer
sourceDir:
type: string
specType:
@ -454,6 +456,8 @@ definitions:
type: integer
script:
type: string
second:
type: integer
sourceDir:
type: string
specType:
@ -969,6 +973,13 @@ definitions:
type:
type: string
type: object
dto.LogOption:
properties:
logMaxFile:
type: string
logMaxSize:
type: string
type: object
dto.Login:
properties:
authMethod:
@ -1614,6 +1625,8 @@ definitions:
type: string
mfaStatus:
type: string
monitorInterval:
type: string
monitorStatus:
type: string
monitorStoreDays:
@ -2367,6 +2380,38 @@ definitions:
additionalProperties: true
type: object
type: object
request.NginxAntiLeechUpdate:
properties:
blocked:
type: boolean
cache:
type: boolean
cacheTime:
type: integer
cacheUint:
type: string
enable:
type: boolean
extends:
type: string
logEnable:
type: boolean
noneRef:
type: boolean
return:
type: string
serverNames:
items:
type: string
type: array
websiteID:
type: integer
required:
- enable
- extends
- return
- websiteID
type: object
request.NginxAuthReq:
properties:
websiteID:
@ -2392,6 +2437,13 @@ definitions:
- username
- websiteID
type: object
request.NginxCommonReq:
properties:
websiteID:
type: integer
required:
- websiteID
type: object
request.NginxConfigFileUpdate:
properties:
backup:
@ -2745,15 +2797,23 @@ definitions:
type: object
request.WebsitePHPConfigUpdate:
properties:
disableFunctions:
items:
type: string
type: array
id:
type: integer
params:
additionalProperties:
type: string
type: object
scope:
type: string
uploadMaxSize:
type: string
required:
- id
- params
- scope
type: object
request.WebsitePHPFileUpdate:
properties:
@ -3154,10 +3214,16 @@ definitions:
type: object
response.PHPConfig:
properties:
disableFunctions:
items:
type: string
type: array
params:
additionalProperties:
type: string
type: object
uploadMaxSize:
type: string
type: object
response.WebsiteAcmeAccountDTO:
properties:
@ -4058,27 +4124,27 @@ paths:
post:
consumes:
- application/json
description: 修改 docker 配置信息
description: 修改 docker 日志配置
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.DaemonJsonConf'
$ref: '#/definitions/dto.LogOption'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Update docker daemon.json
summary: Update docker daemon.json log option
tags:
- Container Docker
x-panel-log:
BeforeFuntions: []
bodyKeys: []
formatEN: Updated the docker daemon.json configuration
formatZH: 更新 docker daemon.json 配置
formatEN: Updated the docker daemon.json log option
formatZH: 更新 docker daemon.json 日志配置
paramKeys: []
/containers/daemonjson/update/byfile:
post:
@ -9016,6 +9082,46 @@ paths:
formatEN: Delete domain [domain]
formatZH: 删除域名 [domain]
paramKeys: []
/websites/leech:
post:
consumes:
- application/json
description: 获取防盗链配置
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.NginxCommonReq'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Get AntiLeech conf
tags:
- Website
/websites/leech/update:
post:
consumes:
- application/json
description: 更新防盗链配置
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.NginxAntiLeechUpdate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Update AntiLeech
tags:
- Website
/websites/list:
get:
description: 获取网站列表

@ -148,8 +148,11 @@ export const loadDaemonJsonFile = () => {
export const loadDockerStatus = () => {
return http.get<string>(`/containers/docker/status`);
};
export const updateDaemonJson = (params: Container.DaemonJsonConf) => {
return http.post(`/containers/daemonjson/update`, params);
export const updateDaemonJson = (key: string, value: string) => {
return http.post(`/containers/daemonjson/update`, { key: key, value: value }, 60000);
};
export const updateLogOption = (maxSize: string, maxFile: string) => {
return http.post(`/containers/logoption/update`, { maxSize: maxSize, maxFile: maxFile }, 60000);
};
export const updateDaemonJsonByfile = (params: Container.DaemonJsonUpdateByFile) => {
return http.post(`/containers/daemonjson/update/byfile`, params);

@ -605,9 +605,9 @@ const message = {
'Allows the running container state to be preserved in case of unexpected shutdown or crash of the Docker daemon',
liveWithSwarmHelper: 'live-restore daemon configuration is incompatible with swarm mode.',
iptablesDisable: 'Close iptables',
iptablesHelper1: 'This setting will disable Docker automatic configuration of iptables rules.',
iptablesHelper1: 'Automatic configuration of iptables rules for Docker.',
iptablesHelper2:
'This may cause the container to be unable to communicate with external networks. Do you want to continue?',
'Disabling iptables will result in the containers being unable to communicate with external networks.',
daemonJsonPath: 'Conf Path',
serviceUnavailable: 'Docker service is not started at present, please click',
startIn: ' to start',

@ -616,8 +616,8 @@ const message = {
liveHelper: ' Docker ',
liveWithSwarmHelper: 'live-restore Swarm ',
iptablesDisable: ' iptables',
iptablesHelper1: ' Docker iptables ',
iptablesHelper2: '',
iptablesHelper1: 'Docker iptables ',
iptablesHelper2: ' iptables ',
daemonJsonPath: '',
serviceUnavailable: ' Docker ',
startIn: '',

@ -10,7 +10,7 @@
<el-input clearable v-model.trim="form.name" />
</el-form-item>
<el-form-item :label="$t('container.image')" prop="image">
<el-select style="width: 100%" allow-create filterable v-model="form.image">
<el-select class="widthClass" allow-create filterable v-model="form.image">
<el-option
v-for="(item, index) of images"
:key="index"
@ -26,7 +26,7 @@
</el-radio-group>
</el-form-item>
<el-form-item v-if="!form.publishAllPorts">
<el-card style="width: 100%">
<el-card class="widthClass">
<table style="width: 100%" class="tab-table">
<tr v-if="form.exposedPorts.length !== 0">
<th scope="col" width="45%" align="left">
@ -107,21 +107,21 @@
<el-card style="width: 100%">
<table style="width: 100%" class="tab-table">
<tr v-if="form.volumes.length !== 0">
<th scope="col" width="42%" align="left">
<th scope="col" width="39%" align="left">
<label>{{ $t('container.serverPath') }}</label>
</th>
<th scope="col" width="12%" align="left">
<th scope="col" width="18%" align="left">
<label>{{ $t('container.mode') }}</label>
</th>
<th scope="col" width="42%" align="left">
<th scope="col" width="39%" align="left">
<label>{{ $t('container.containerDir') }}</label>
</th>
<th align="left"></th>
</tr>
<tr v-for="(row, index) in form.volumes" :key="index">
<td width="42%">
<td width="39%">
<el-select
style="width: 100%"
class="widthClass"
allow-create
clearable
:placeholder="$t('commons.msg.inputOrSelect')"
@ -136,13 +136,13 @@
/>
</el-select>
</td>
<td width="12%">
<el-select style="width: 100%" filterable v-model="row.mode">
<td width="18%">
<el-select class="widthClass" filterable v-model="row.mode">
<el-option value="rw" :label="$t('container.modeRW')" />
<el-option value="ro" :label="$t('container.modeR')" />
</el-select>
</td>
<td width="42%">
<td width="39%">
<el-input v-model="row.containerDir" />
</td>
<td>
@ -416,3 +416,9 @@ defineExpose({
acceptParams,
});
</script>
<style lang="scss" scoped>
.widthClass {
width: 100%;
}
</style>

@ -46,12 +46,31 @@
<el-col :span="10">
<el-form :model="form" label-position="left" :rules="rules" ref="formRef" label-width="150px">
<el-form-item :label="$t('container.mirrors')" prop="mirrors">
<div style="width: 100%">
<el-input
type="textarea"
:placeholder="$t('container.mirrorHelper')"
:autosize="{ minRows: 3, maxRows: 10 }"
:autosize="{ minRows: 3, maxRows: 5 }"
disabled
v-if="form.mirrors"
v-model="form.mirrors"
style="width: calc(100% - 80px)"
/>
<el-button
v-if="form.mirrors"
style="width: 80px"
@click="onChangeMirrors"
icon="Setting"
>
{{ $t('commons.button.set') }}
</el-button>
</div>
<el-input disabled v-if="!form.mirrors" v-model="unset">
<template #append>
<el-button @click="onChangeMirrors" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('container.mirrorsHelper') }}</span>
<span class="input-help">
{{ $t('container.mirrorsHelper2') }}
@ -66,60 +85,58 @@
</span>
</el-form-item>
<el-form-item :label="$t('container.registries')" prop="registries">
<div style="width: 100%">
<el-input
v-if="form.registries"
type="textarea"
:placeholder="$t('container.registrieHelper')"
:autosize="{ minRows: 3, maxRows: 10 }"
:autosize="{ minRows: 3, maxRows: 5 }"
disabled
v-model="form.registries"
style="width: calc(100% - 80px)"
/>
</el-form-item>
<el-form-item :label="$t('container.cutLog')" prop="hasLogOption">
<el-switch v-model="form.logOptionShow"></el-switch>
</el-form-item>
<div v-if="form.logOptionShow">
<el-form-item prop="logMaxSize">
<el-input v-model.number="form.logMaxSize">
<template #prepend>{{ $t('container.maxSize') }}</template>
<el-button
v-if="form.mirrors"
style="width: 80px"
@click="onChangeRegistries"
icon="Setting"
>
{{ $t('commons.button.set') }}
</el-button>
</div>
<el-input disabled v-if="!form.registries" v-model="unset">
<template #append>
<el-select v-model="form.sizeUnit" style="width: 70px">
<el-option label="b" value="b"></el-option>
<el-option label="k" value="k"></el-option>
<el-option label="m" value="m"></el-option>
<el-option label="g" value="g"></el-option>
<el-option label="t" value="t"></el-option>
</el-select>
<el-button @click="onChangeRegistries" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item prop="logMaxFile">
<el-input v-model.number="form.logMaxFile">
<template #prepend>{{ $t('container.maxFile') }}</template>
</el-input>
<el-form-item :label="$t('container.cutLog')" prop="hasLogOption">
<el-switch v-model="form.logOptionShow" @change="handleLogOption"></el-switch>
</el-form-item>
</div>
<el-form-item label="iptables" prop="iptables">
<el-switch v-model="form.iptables" @change="onChangeIptables"></el-switch>
<el-switch v-model="form.iptables" @change="handleIptables"></el-switch>
<span class="input-help">{{ $t('container.iptablesHelper1') }}</span>
</el-form-item>
<el-form-item label="live-restore" prop="liveRestore">
<el-switch :disabled="form.isSwarm" v-model="form.liveRestore"></el-switch>
<el-switch
:disabled="form.isSwarm"
v-model="form.liveRestore"
@change="handleLive"
></el-switch>
<span class="input-help">{{ $t('container.liveHelper') }}</span>
<span v-if="form.isSwarm" class="input-help">
{{ $t('container.liveWithSwarmHelper') }}
</span>
</el-form-item>
<el-form-item label="cgroup-driver" prop="cgroupDriver">
<el-radio-group v-model="form.cgroupDriver">
<el-radio-group v-model="form.cgroupDriver" @change="handleCgroup">
<el-radio label="cgroupfs">cgroupfs</el-radio>
<el-radio label="systemd">systemd</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button :disabled="loading" type="primary" @click="onSave(formRef)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
@ -154,21 +171,47 @@
:close-on-press-escape="false"
:show-close="false"
>
<span>{{ $t('container.iptablesHelper1') }}</span>
<div style="margin-top: 10px">
<span style="color: red; font-weight: 500">{{ $t('container.iptablesHelper2') }}</span>
<span style="color: red">{{ $t('container.iptablesHelper2') }}</span>
<div style="margin-top: 10px">
<span style="font-size: 12px">{{ $t('database.restartNowHelper') }}</span>
</div>
<div style="margin-top: 10px">
<span style="font-size: 12px">{{ $t('commons.msg.operateConfirm') }}</span>
<span style="font-size: 12px; color: red; font-weight: 500">'{{ $t('database.restartNow') }}'</span>
</div>
<el-input style="margin-top: 10px" v-model="submitInput"></el-input>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onSaveIptables(true)">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="onSaveIptables(false)">
<el-button
@click="
iptablesVisiable = false;
search();
"
>
{{ $t('commons.button.cancel') }}
</el-button>
<el-button
:disabled="submitInput !== $t('database.restartNow')"
type="primary"
@click="onSubmitCloseIPtable"
>
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmitSave"></ConfirmDialog>
<Mirror ref="mirrorRef" @search="search" />
<Registry ref="registriesRef" @search="search" />
<LogOption ref="logOptionRef" @search="search" />
<ConfirmDialog ref="confirmDialogRefIptable" @confirm="onSubmitOpenIPtable" @cancle="search" />
<ConfirmDialog ref="confirmDialogRefLog" @confirm="onSubmitSaveLog" @cancle="search" />
<ConfirmDialog ref="confirmDialogRefLive" @confirm="onSubmitSaveLive" @cancle="search" />
<ConfirmDialog ref="confirmDialogRefCgroup" @confirm="onSubmitSaveCgroup" @cancle="search" />
<ConfirmDialog ref="confirmDialogRefFile" @confirm="onSubmitSaveFile" @cancle="search" />
</div>
</template>
@ -178,6 +221,9 @@ import { onMounted, reactive, ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
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 ConfirmDialog from '@/components/confirm-dialog/index.vue';
import i18n from '@/lang';
import {
@ -190,11 +236,22 @@ import {
import { MsgSuccess } from '@/utils/message';
import { checkNumberRange } from '@/global/form-rules';
const unset = ref(i18n.global.t('setting.unSetting'));
const submitInput = ref();
const loading = ref(false);
const showDaemonJsonAlert = ref(false);
const extensions = [javascript(), oneDark];
const confShowType = ref('base');
const logOptionRef = ref();
const confirmDialogRefLog = ref();
const mirrorRef = ref();
const registriesRef = ref();
const confirmDialogRefLive = ref();
const confirmDialogRefCgroup = ref();
const confirmDialogRefIptable = ref();
const form = reactive({
isSwarm: false,
status: '',
@ -205,8 +262,7 @@ const form = reactive({
iptables: true,
cgroupDriver: '',
logOptionShow: false,
logMaxSize: 10,
sizeUnit: 'm',
logMaxSize: '',
logMaxFile: 3,
});
const rules = reactive({
@ -220,41 +276,96 @@ const confirmDialogRef = ref();
const iptablesVisiable = ref();
const onSave = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!valid) return;
const onSaveFile = async () => {
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 onSaveFile = async () => {
const onChangeMirrors = () => {
mirrorRef.value.acceptParams({ mirrors: form.mirrors });
};
const onChangeRegistries = () => {
registriesRef.value.acceptParams({ registries: form.registries });
};
const handleLogOption = async () => {
if (form.logOptionShow) {
logOptionRef.value.acceptParams({ logMaxSize: form.logMaxSize, logMaxFile: form.logMaxFile });
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);
confirmDialogRefLog.value!.acceptParams(params);
};
const toDoc = () => {
window.open('https://1panel.cn/docs/user_manual/containers/setting/', '_blank');
const onSubmitSaveLog = async () => {
save('LogOption', 'disable');
};
const onChangeIptables = () => {
if (!form.iptables) {
const handleIptables = () => {
if (form.iptables) {
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRefIptable.value!.acceptParams(params);
return;
} else {
iptablesVisiable.value = true;
}
};
const onSubmitCloseIPtable = () => {
save('IPtables', 'disable');
};
const onSubmitOpenIPtable = () => {
save('IPtables', 'enable');
};
const onSaveIptables = (status: boolean) => {
form.iptables = status;
iptablesVisiable.value = false;
const handleLive = async () => {
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRefLive.value!.acceptParams(params);
};
const onSubmitSaveLive = () => {
save('LiveRestore', form.liveRestore ? 'enable' : 'disable');
};
const handleCgroup = async () => {
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRefCgroup.value!.acceptParams(params);
};
const onSubmitSaveCgroup = () => {
save('Dirver', form.cgroupDriver);
};
const save = async (key: string, value: string) => {
loading.value = true;
await updateDaemonJson(key, value)
.then(() => {
loading.value = false;
search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
search();
loading.value = false;
});
};
const toDoc = () => {
window.open('https://1panel.cn/docs/user_manual/containers/setting/', '_blank');
};
const onOperator = async (operation: string) => {
@ -281,8 +392,7 @@ const onOperator = async (operation: string) => {
});
};
const onSubmitSave = async () => {
if (confShowType.value === 'all') {
const onSubmitSaveFile = async () => {
let param = { file: dockerConf.value };
loading.value = true;
await updateDaemonJsonByfile(param)
@ -294,40 +404,6 @@ const onSubmitSave = async () => {
loading.value = false;
});
return;
}
let itemMirrors = form.mirrors.split('\n');
let itemRegistries = form.registries.split('\n');
let param = {
isSwarm: form.isSwarm,
status: form.status,
version: '',
registryMirrors: itemMirrors.filter(function (el) {
return el !== null && el !== '' && el !== undefined;
}),
insecureRegistries: itemRegistries.filter(function (el) {
return el !== null && el !== '' && el !== undefined;
}),
liveRestore: form.liveRestore,
iptables: form.iptables,
cgroupDriver: form.cgroupDriver,
logMaxSize: form.logMaxSize + form.sizeUnit,
logMaxFile: form.logMaxFile + '',
};
if (!form.logOptionShow) {
param.logMaxFile = '';
param.logMaxSize = '';
}
loading.value = true;
await updateDaemonJson(param)
.then(() => {
loading.value = false;
search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
const loadDockerConf = async () => {
@ -349,49 +425,21 @@ const changeMode = async () => {
};
const search = async () => {
loading.value = true;
await loadDaemonJson()
.then((res) => {
loading.value = false;
const res = await loadDaemonJson();
form.isSwarm = res.data.isSwarm;
form.status = res.data.status;
form.version = res.data.version;
form.cgroupDriver = res.data.cgroupDriver;
form.cgroupDriver = res.data.cgroupDriver || 'cgroupfs';
form.liveRestore = res.data.liveRestore;
form.iptables = res.data.iptables;
form.mirrors = res.data.registryMirrors ? res.data.registryMirrors.join('\n') : '';
form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : '';
if (res.data.logMaxFile || res.data.logMaxSize) {
form.logOptionShow = true;
}
form.logMaxFile = Number(res.data.logMaxFile);
form.logMaxSize = loadSize(res.data.logMaxSize);
})
.catch(() => {
loading.value = false;
});
};
const loadSize = (value: string) => {
if (value.indexOf('b') !== -1 || value.indexOf('B') !== -1) {
form.sizeUnit = 'b';
return Number(value.replaceAll('b', '').replaceAll('B', ''));
}
if (value.indexOf('k') !== -1 || value.indexOf('K') !== -1) {
form.sizeUnit = 'k';
return Number(value.replaceAll('k', '').replaceAll('K', ''));
}
if (value.indexOf('m') !== -1 || value.indexOf('M') !== -1) {
form.sizeUnit = 'm';
return Number(value.replaceAll('m', '').replaceAll('M', ''));
}
if (value.indexOf('g') !== -1 || value.indexOf('G') !== -1) {
form.sizeUnit = 'g';
return Number(value.replaceAll('g', '').replaceAll('G', ''));
}
if (value.indexOf('t') !== -1 || value.indexOf('T') !== -1) {
form.sizeUnit = 't';
return Number(value.replaceAll('t', '').replaceAll('T', ''));
form.logMaxSize = res.data.logMaxSize;
} else {
form.logOptionShow = false;
}
};

@ -0,0 +1,147 @@
<template>
<div>
<el-drawer
v-model="drawerVisiable"
:destroy-on-close="true"
:close-on-click-modal="false"
@close="handleClose"
size="30%"
>
<template #header>
<DrawerHeader :header="$t('container.cutLog')" :back="handleClose" />
</template>
<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="logMaxSize" :label="$t('container.maxSize')">
<el-input v-model.number="form.logMaxSize">
<template #append>
<el-select v-model="form.sizeUnit" style="width: 70px">
<el-option label="B" value="B"></el-option>
<el-option label="KB" value="KB"></el-option>
<el-option label="MB" value="MB"></el-option>
<el-option label="GB" value="GB"></el-option>
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item prop="logMaxFile" :label="$t('container.maxFile')">
<el-input v-model.number="form.logMaxFile" />
</el-form-item>
</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 { Rules, checkNumberRange } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
import { updateLogOption } from '@/api/modules/container';
const loading = ref();
const drawerVisiable = ref();
const confirmDialogRef = ref();
const formRef = ref();
interface DialogProps {
logMaxSize: string;
logMaxFile: number;
}
const form = reactive({
logMaxSize: 10,
logMaxFile: 3,
sizeUnit: 'MB',
});
const rules = reactive({
logMaxSize: [checkNumberRange(1, 1024000), Rules.number],
logMaxFile: [checkNumberRange(1, 100), Rules.number],
});
const emit = defineEmits<{ (e: 'search'): void }>();
const acceptParams = (params: DialogProps): void => {
form.logMaxFile = params.logMaxFile || 3;
if (params.logMaxSize) {
form.logMaxSize = loadSize(params.logMaxSize);
} else {
form.logMaxSize = 10;
form.sizeUnit = 'MB';
}
drawerVisiable.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 updateLogOption(form.logMaxSize + '', form.logMaxFile + '')
.then(() => {
loading.value = false;
drawerVisiable.value = false;
emit('search');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
const loadSize = (value: string) => {
if (value.indexOf('b') !== -1 || value.indexOf('B') !== -1) {
form.sizeUnit = 'B';
return Number(value.replaceAll('b', '').replaceAll('B', ''));
}
if (value.indexOf('k') !== -1 || value.indexOf('K') !== -1) {
form.sizeUnit = 'KB';
return Number(value.replaceAll('k', '').replaceAll('K', ''));
}
if (value.indexOf('m') !== -1 || value.indexOf('M') !== -1) {
form.sizeUnit = 'MB';
return Number(value.replaceAll('m', '').replaceAll('M', ''));
}
if (value.indexOf('g') !== -1 || value.indexOf('G') !== -1) {
form.sizeUnit = 'GB';
return Number(value.replaceAll('g', '').replaceAll('G', ''));
}
};
const handleClose = () => {
emit('search');
drawerVisiable.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped>
.help-ul {
color: #8f959e;
}
</style>

@ -0,0 +1,86 @@
<template>
<div>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('container.mirrors')" :back="handleClose" />
</template>
<el-form label-position="top" @submit.prevent v-loading="loading">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('container.mirrors')">
<el-input
type="textarea"
:placeholder="$t('container.mirrorHelper')"
:autosize="{ minRows: 8, maxRows: 10 }"
v-model="mirrors"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onSave">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateDaemonJson } from '@/api/modules/container';
const emit = defineEmits<{ (e: 'search'): void }>();
const confirmDialogRef = ref();
const mirrors = ref();
interface DialogProps {
mirrors: string;
}
const drawerVisiable = ref();
const loading = ref();
const acceptParams = (params: DialogProps): void => {
mirrors.value = params.mirrors || params.mirrors.replaceAll(',', '\n');
drawerVisiable.value = true;
};
const onSave = async () => {
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 onSubmit = async () => {
loading.value = true;
await updateDaemonJson('Mirrors', mirrors.value.replaceAll('\n', ','))
.then(() => {
loading.value = false;
emit('search');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
const handleClose = () => {
drawerVisiable.value = false;
};
defineExpose({
acceptParams,
});
</script>

@ -0,0 +1,86 @@
<template>
<div>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('container.registries')" :back="handleClose" />
</template>
<el-form label-position="top" @submit.prevent v-loading="loading">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('container.registries')">
<el-input
type="textarea"
:placeholder="$t('container.registrieHelper')"
:autosize="{ minRows: 8, maxRows: 10 }"
v-model="registries"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onSave">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateDaemonJson } from '@/api/modules/container';
const emit = defineEmits<{ (e: 'search'): void }>();
const confirmDialogRef = ref();
const registries = ref();
interface DialogProps {
registries: string;
}
const drawerVisiable = ref();
const loading = ref();
const acceptParams = (params: DialogProps): void => {
registries.value = params.registries || params.registries.replaceAll(',', '\n');
drawerVisiable.value = true;
};
const onSave = async () => {
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 onSubmit = async () => {
loading.value = true;
await updateDaemonJson('Registries', registries.value.replaceAll('\n', ','))
.then(() => {
loading.value = false;
emit('search');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
const handleClose = () => {
drawerVisiable.value = false;
};
defineExpose({
acceptParams,
});
</script>
Loading…
Cancel
Save