1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: Merge code from dev (#7726)

This commit is contained in:
ssongliu 2025-01-15 14:24:24 +08:00 committed by GitHub
parent bb250557a2
commit b90a90c0c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 124 additions and 103 deletions

View File

@ -7,24 +7,23 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"io" "io"
"os" "os"
"path" "path"
"strings" "strings"
"time" "time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/1Panel-dev/1Panel/agent/app/dto" "github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task" "github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n" "github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/docker" "github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/homedir"
@ -419,6 +418,9 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) (dto.ContainerPruneRepor
} }
return report, buserr.WithDetail(constant.ErrInUsed, id, nil) return report, buserr.WithDetail(constant.ErrInUsed, id, nil)
} }
if strings.Contains(err.Error(), "image has dependent") {
return report, buserr.New(constant.ErrObjectBeDependent)
}
return report, err return report, err
} }
report.DeletedNumber++ report.DeletedNumber++

View File

@ -105,6 +105,7 @@ var (
var ( var (
ErrInUsed = "ErrInUsed" ErrInUsed = "ErrInUsed"
ErrObjectInUsed = "ErrObjectInUsed" ErrObjectInUsed = "ErrObjectInUsed"
ErrObjectBeDependent = "ErrObjectBeDependent"
ErrPortRules = "ErrPortRules" ErrPortRules = "ErrPortRules"
ErrPgImagePull = "ErrPgImagePull" ErrPgImagePull = "ErrPgImagePull"
) )

View File

@ -157,6 +157,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
#container #container
ErrInUsed: "{{ .detail }} is in use and cannot be deleted" ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
ErrObjectInUsed: "This object is in use and cannot be deleted" ErrObjectInUsed: "This object is in use and cannot be deleted"
ErrObjectBeDependent: "This image is dependent on other images and can't be deleted"
ErrPortRules: "The number of ports does not match, please re-enter!" ErrPortRules: "The number of ports does not match, please re-enter!"
ErrPgImagePull: "Image pull timeout. Please configure image acceleration or manually pull the postgres:16.0-alpine image and try again" ErrPgImagePull: "Image pull timeout. Please configure image acceleration or manually pull the postgres:16.0-alpine image and try again"

View File

@ -158,6 +158,7 @@ ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後
#container #container
ErrInUsed: "{{ .detail }} 正被使用,無法刪除" ErrInUsed: "{{ .detail }} 正被使用,無法刪除"
ErrObjectInUsed: "該對象正被使用,無法刪除" ErrObjectInUsed: "該對象正被使用,無法刪除"
ErrObjectBeDependent: "該鏡像依賴於其他鏡像,無法刪除"
ErrPortRules: "端口數目不匹配,請重新輸入!" ErrPortRules: "端口數目不匹配,請重新輸入!"
ErrPgImagePull: "鏡像拉取超時,請配置鏡像加速或手動拉取 postgres:16.0-alpine 鏡像後重試" ErrPgImagePull: "鏡像拉取超時,請配置鏡像加速或手動拉取 postgres:16.0-alpine 鏡像後重試"

View File

@ -156,8 +156,9 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
#container #container
ErrInUsed: "{{ .detail }} 正被使用,无法删除" ErrInUsed: "{{ .detail }} 正被使用,无法删除"
ErrObjectInUsed: "该对象正被使用,无法删除" ErrObjectInUsed: "该对象正被使用,无法删除"
ErrObjectBeDependent: "该镜像依赖于其他镜像,无法删除"
ErrPortRules: "端口数目不匹配,请重新输入!" ErrPortRules: "端口数目不匹配,请重新输入!"
ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 postgres:16.0-alpine 镜像后重试" ErrPgImagePull: "镜像拉取超时,请配置镜像加速或手动拉取 {{ .name }} 镜像后重试"
#runtime #runtime
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"

View File

@ -25,12 +25,12 @@ func (f *Firewall) Name() string {
} }
func (f *Firewall) Status() (bool, error) { func (f *Firewall) Status() (bool, error) {
stdout, _ := cmd.Exec("firewall-cmd --state") stdout, _ := cmd.Exec("LANGUAGE=en_US:en firewall-cmd --state")
return stdout == "running\n", nil return stdout == "running\n", nil
} }
func (f *Firewall) Version() (string, error) { func (f *Firewall) Version() (string, error) {
stdout, err := cmd.Exec("firewall-cmd --version") stdout, err := cmd.Exec("LANGUAGE=en_US:en firewall-cmd --version")
if err != nil { if err != nil {
return "", fmt.Errorf("load the firewall version failed, err: %s", stdout) return "", fmt.Errorf("load the firewall version failed, err: %s", stdout)
} }

View File

@ -17,9 +17,9 @@ type Ufw struct {
func NewUfw() (*Ufw, error) { func NewUfw() (*Ufw, error) {
var ufw Ufw var ufw Ufw
if cmd.HasNoPasswordSudo() { if cmd.HasNoPasswordSudo() {
ufw.CmdStr = "sudo ufw" ufw.CmdStr = "LANGUAGE=en_US:en sudo ufw"
} else { } else {
ufw.CmdStr = "ufw" ufw.CmdStr = "LANGUAGE=en_US:en ufw"
} }
return &ufw, nil return &ufw, nil
} }

View File

@ -306,7 +306,7 @@ func loadImageTag() (string, error) {
defer cancel() defer cancel()
if _, err := client.ImagePull(ctx, itemTag, image.PullOptions{}); err != nil { if _, err := client.ImagePull(ctx, itemTag, image.PullOptions{}); err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) { if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return itemTag, buserr.New(constant.ErrPgImagePull) return itemTag, buserr.WithName(constant.ErrPgImagePull, itemTag)
} }
global.LOG.Errorf("image %s pull failed, err: %v", itemTag, err) global.LOG.Errorf("image %s pull failed, err: %v", itemTag, err)
return itemTag, fmt.Errorf("image %s pull failed, err: %v", itemTag, err) return itemTag, fmt.Errorf("image %s pull failed, err: %v", itemTag, err)

View File

@ -20,10 +20,7 @@
"prettier": "prettier --write ." "prettier": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-json": "^6.0.1", "@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-php": "^6.0.1",
"@codemirror/language": "^6.10.2", "@codemirror/language": "^6.10.2",
"@codemirror/legacy-modes": "^6.4.0", "@codemirror/legacy-modes": "^6.4.0",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",

View File

@ -134,7 +134,6 @@ const onCheck = async (key: any, name: any) => {
}; };
const onOperate = async (operation: string) => { const onOperate = async (operation: string) => {
em('update:maskShow', false);
operateReq.operate = operation; operateReq.operate = operation;
ElMessageBox.confirm( ElMessageBox.confirm(
i18n.global.t('app.operatorHelper', [i18n.global.t('app.' + operation)]), i18n.global.t('app.operatorHelper', [i18n.global.t('app.' + operation)]),
@ -144,8 +143,7 @@ const onOperate = async (operation: string) => {
cancelButtonText: i18n.global.t('commons.button.cancel'), cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info', type: 'info',
}, },
) ).then(() => {
.then(() => {
em('update:maskShow', true); em('update:maskShow', true);
em('update:loading', true); em('update:loading', true);
em('before'); em('before');
@ -159,9 +157,6 @@ const onOperate = async (operation: string) => {
.catch(() => { .catch(() => {
em('update:loading', false); em('update:loading', false);
}); });
})
.catch(() => {
em('update:maskShow', true);
}); });
}; };

View File

@ -130,7 +130,6 @@ const taskLogRef = ref();
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
cacheSizeKey: 'backup-page-size',
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,

View File

@ -8,13 +8,13 @@
import { CSSProperties } from 'vue'; import { CSSProperties } from 'vue';
import { basicSetup, EditorView } from 'codemirror'; import { basicSetup, EditorView } from 'codemirror';
import { EditorState } from '@codemirror/state'; import { EditorState } from '@codemirror/state';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { StreamLanguage } from '@codemirror/language'; import { StreamLanguage } from '@codemirror/language';
import { nginx } from './nginx'; import { nginx } from './nginx';
import { yaml } from '@codemirror/legacy-modes/mode/yaml'; import { yaml } from '@codemirror/legacy-modes/mode/yaml';
import { shell } from '@codemirror/legacy-modes/mode/shell'; import { shell } from '@codemirror/legacy-modes/mode/shell';
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile'; import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
import { javascript } from '@codemirror/legacy-modes/mode/javascript';
import { placeholder } from '@codemirror/view'; import { placeholder } from '@codemirror/view';
import { json } from '@codemirror/lang-json'; import { json } from '@codemirror/lang-json';
@ -94,7 +94,7 @@ const initCodeMirror = () => {
extensions.push(StreamLanguage.define(dockerFile)); extensions.push(StreamLanguage.define(dockerFile));
break; break;
case 'javascript': case 'javascript':
extensions.push(javascript()); extensions.push(StreamLanguage.define(javascript));
break; break;
case 'nginx': case 'nginx':
extensions.push(StreamLanguage.define(nginx)); extensions.push(StreamLanguage.define(nginx));

View File

@ -1,6 +1,6 @@
<template> <template>
<el-button v-if="type == 'icon'" link @click="copyText(content)" icon="DocumentCopy" class="ml-1.5"></el-button> <el-button v-if="type == 'icon'" link @click="copyText(content)" icon="DocumentCopy" class="ml-1.5"></el-button>
<el-button type="primary" @click="copyText(content)" v-else>{{ $t('commons.button.copy') }}</el-button> <el-button @click="copyText(content)" v-else>{{ $t('commons.button.copy') }}</el-button>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -153,7 +153,6 @@ const open = ref();
const data = ref(); const data = ref();
const title = ref(); const title = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
cacheSizeKey: 'upload-page-size',
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,

View File

@ -224,6 +224,19 @@ const checkDBName = (rule: any, value: any, callback: any) => {
} }
}; };
const checkComposeName = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.composeName')));
} else {
const reg = /^[a-z0-9]{1}[a-z0-9_-]{0,256}$/;
if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.composeName')));
} else {
callback();
}
}
};
const checkImageName = (rule: any, value: any, callback: any) => { const checkImageName = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.imageName'))); callback(new Error(i18n.global.t('commons.rule.imageName')));
@ -568,6 +581,7 @@ interface CommonRule {
simplePassword: FormItemRule; simplePassword: FormItemRule;
dbName: FormItemRule; dbName: FormItemRule;
imageName: FormItemRule; imageName: FormItemRule;
composeName: FormItemRule;
volumeName: FormItemRule; volumeName: FormItemRule;
linuxName: FormItemRule; linuxName: FormItemRule;
password: FormItemRule; password: FormItemRule;
@ -643,6 +657,11 @@ export const Rules: CommonRule = {
validator: checkDBName, validator: checkDBName,
trigger: 'blur', trigger: 'blur',
}, },
composeName: {
required: true,
validator: checkComposeName,
trigger: 'blur',
},
imageName: { imageName: {
required: true, required: true,
validator: checkImageName, validator: checkImageName,

View File

@ -201,6 +201,8 @@ const message = {
simpleName: 'Supports non-underscore starting, English, numbers, _, length 3-30', simpleName: 'Supports non-underscore starting, English, numbers, _, length 3-30',
simplePassword: 'Supports non-underscore starting, English, numbers, _, length 1-30', simplePassword: 'Supports non-underscore starting, English, numbers, _, length 1-30',
dbName: 'Supports non-special character starting, including English, Chinese, numbers, .-_, with a length of 1-64', dbName: 'Supports non-special character starting, including English, Chinese, numbers, .-_, with a length of 1-64',
composeName:
'Supports non-special characters at the beginning, lowercase letters, numbers, - and _, length 1-256',
imageName: 'Support English, numbers, :@/.-_, length 1-256', imageName: 'Support English, numbers, :@/.-_, length 1-256',
volumeName: 'Support English, numbers, .-_, length 2-30', volumeName: 'Support English, numbers, .-_, length 2-30',
supervisorName: 'Supports non-special characters starting with English, numbers, - and _, length 1-128', supervisorName: 'Supports non-special characters starting with English, numbers, - and _, length 1-128',
@ -949,6 +951,14 @@ const message = {
retainCopiesUnit: ' copies (View)', retainCopiesUnit: ' copies (View)',
cronSpecRule: 'The execution period format in line {0} is incorrect. Please check and try again!', cronSpecRule: 'The execution period format in line {0} is incorrect. Please check and try again!',
cronSpecRule2: 'Execution period format is incorrect, please check and try again!', cronSpecRule2: 'Execution period format is incorrect, please check and try again!',
perMonthHelper: 'Execute on the {0} day of every month at {1}:{2}',
perWeekHelper: 'Execute every week on {0} at {1}:{2}',
perDayHelper: 'Execute every day at {0}:{1}',
perHourHelper: 'Execute every hour at {0} minutes',
perNDayHelper: 'Execute every {0} days at {1}:{2}',
perNHourHelper: 'Execute every {0} hours at {1}',
perNMinuteHelper: 'Execute every {0} minutes',
perNSecondHelper: 'Execute every {0} seconds',
perMonth: 'Every monthly', perMonth: 'Every monthly',
perWeek: 'Every week', perWeek: 'Every week',
perHour: 'Every hour', perHour: 'Every hour',
@ -957,8 +967,6 @@ const message = {
perNHour: 'Every N hours', perNHour: 'Every N hours',
perNMinute: 'Every N minutes', perNMinute: 'Every N minutes',
perNSecond: 'Every N seconds', perNSecond: 'Every N seconds',
per: 'Every ',
handle: 'Handle',
day: 'Day', day: 'Day',
monday: 'Monday', monday: 'Monday',
tuesday: 'Tuesday', tuesday: 'Tuesday',

View File

@ -200,6 +200,7 @@ const message = {
simpleName: '支持非底線開頭英文數字_,長度3-30', simpleName: '支持非底線開頭英文數字_,長度3-30',
simplePassword: '支持非底線開頭英文數字_,長度1-30', simplePassword: '支持非底線開頭英文數字_,長度1-30',
dbName: '支持非特殊字符開頭英文中文數字.-_長度1-64', dbName: '支持非特殊字符開頭英文中文數字.-_長度1-64',
composeName: '支持非特殊字符開頭小寫英文數字-和_長度1-256',
imageName: '支持英文數字:@/.-_,長度1-256', imageName: '支持英文數字:@/.-_,長度1-256',
volumeName: '支持英文數字.-和_,長度2-30', volumeName: '支持英文數字.-和_,長度2-30',
supervisorName: '支援非特殊字元開頭,英文數字-和_,長度1-128', supervisorName: '支援非特殊字元開頭,英文數字-和_,長度1-128',
@ -902,6 +903,14 @@ const message = {
retainCopiesUnit: ' (查看)', retainCopiesUnit: ' (查看)',
cronSpecRule: ' {0} 行中執行週期格式錯誤請檢查後重試', cronSpecRule: ' {0} 行中執行週期格式錯誤請檢查後重試',
cronSpecRule2: '執行週期格式錯誤請檢查後重試', cronSpecRule2: '執行週期格式錯誤請檢查後重試',
perMonthHelper: '每月 {0} {1}:{2} 執行',
perWeekHelper: '每週 {0} {1}:{2} 執行',
perDayHelper: '每日 {0}:{1} 執行',
perHourHelper: '每小時 {0} 執行',
perNDayHelper: ' {0} {1}:{2} 執行',
perNHourHelper: ' {0}小時 {1} 執行',
perNMinuteHelper: ' {0} 執行',
perNSecondHelper: ' {0} 執行',
perMonth: '每月', perMonth: '每月',
perWeek: '每周', perWeek: '每周',
perHour: '每小時', perHour: '每小時',
@ -910,8 +919,6 @@ const message = {
perNHour: ' N ', perNHour: ' N ',
perNMinute: ' N 分鐘', perNMinute: ' N 分鐘',
perNSecond: ' N ', perNSecond: ' N ',
per: '每',
handle: '執行',
day: '日', day: '日',
monday: '周一', monday: '周一',
tuesday: '周二', tuesday: '周二',

View File

@ -200,6 +200,7 @@ const message = {
simpleName: '支持非下划线开头英文数字_,长度3-30', simpleName: '支持非下划线开头英文数字_,长度3-30',
simplePassword: '支持非下划线开头英文数字_,长度1-30', simplePassword: '支持非下划线开头英文数字_,长度1-30',
dbName: '支持非特殊字符开头英文中文数字.-_,长度1-64', dbName: '支持非特殊字符开头英文中文数字.-_,长度1-64',
composeName: '支持非特殊字符开头小写英文数字-和_,长度1-256',
imageName: '支持英文数字:@/.-_,长度1-256', imageName: '支持英文数字:@/.-_,长度1-256',
volumeName: '支持英文数字.-和_,长度2-30', volumeName: '支持英文数字.-和_,长度2-30',
supervisorName: '支持非特殊字符开头,英文数字-和_,长度1-128', supervisorName: '支持非特殊字符开头,英文数字-和_,长度1-128',
@ -902,6 +903,14 @@ const message = {
retainCopiesUnit: ' (查看)', retainCopiesUnit: ' (查看)',
cronSpecRule: ' {0} 行中执行周期格式错误请检查后重试', cronSpecRule: ' {0} 行中执行周期格式错误请检查后重试',
cronSpecRule2: '执行周期格式错误请检查后重试', cronSpecRule2: '执行周期格式错误请检查后重试',
perMonthHelper: '每月 {0} {1}:{2} 执行',
perWeekHelper: '每周 {0} {1}:{2} 执行',
perDayHelper: '每日 {0}:{1} 执行',
perHourHelper: '每小时 {0} 执行',
perNDayHelper: ' {0} {1}:{2} 执行',
perNHourHelper: ' {0}小时 {1} 执行',
perNMinuteHelper: ' {0} 执行',
perNSecondHelper: ' {0} 执行',
perMonth: '每月', perMonth: '每月',
perWeek: '每周', perWeek: '每周',
perHour: '每小时', perHour: '每小时',
@ -910,8 +919,6 @@ const message = {
perNHour: ' N ', perNHour: ' N ',
perNMinute: ' N 分钟', perNMinute: ' N 分钟',
perNSecond: ' N ', perNSecond: ' N ',
per: '每',
handle: '执行',
day: '日', day: '日',
monday: '周一', monday: '周一',
tuesday: '周二', tuesday: '周二',

View File

@ -98,7 +98,7 @@ const form = reactive({
envFileContent: `env_file:\n - 1panel.env`, envFileContent: `env_file:\n - 1panel.env`,
}); });
const rules = reactive({ const rules = reactive({
name: [Rules.requiredInput, Rules.imageName], name: [Rules.requiredInput, Rules.composeName],
path: [Rules.requiredInput], path: [Rules.requiredInput],
template: [Rules.requiredSelect], template: [Rules.requiredSelect],
}); });

View File

@ -373,7 +373,7 @@
/> />
<fu-table-operations <fu-table-operations
fix fix
width="180px" width="200px"
:ellipsis="2" :ellipsis="2"
:buttons="buttons" :buttons="buttons"
:label="$t('commons.table.operate')" :label="$t('commons.table.operate')"

View File

@ -517,18 +517,17 @@ const search = async () => {
form.cmd = res.data.cmd || []; form.cmd = res.data.cmd || [];
for (const item of form.cmd) { for (const item of form.cmd) {
if (item.indexOf(' ') !== -1) { if (item.indexOf(' ') !== -1) {
itemCmd += `"${item.replaceAll('"', '\\"')}" `; itemCmd += `"${escapeQuotes(item)}" `;
} else { } else {
itemCmd += item + ' '; itemCmd += item + ' ';
} }
} }
form.cmdStr = itemCmd.trimEnd(); form.cmdStr = itemCmd.trimEnd();
let itemEntrypoint = ''; let itemEntrypoint = '';
form.entrypoint = res.data.entrypoint || []; form.entrypoint = res.data.entrypoint || [];
for (const item of form.entrypoint) { for (const item of form.entrypoint) {
if (item.indexOf(' ') !== -1) { if (item.indexOf(' ') !== -1) {
itemEntrypoint += `"${item.replaceAll('"', '\\"')}" `; itemEntrypoint += `"${escapeQuotes(item)}" `;
} else { } else {
itemEntrypoint += item + ' '; itemEntrypoint += item + ' ';
} }
@ -656,14 +655,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
const submit = async () => { const submit = async () => {
form.cmd = []; form.cmd = [];
if (form.cmdStr) { if (form.cmdStr) {
let itemCmd = splitWithQuotes(form.cmdStr); let itemCmd = splitStringIgnoringQuotes(form.cmdStr);
for (const item of itemCmd) { for (const item of itemCmd) {
form.cmd.push(item.replace(/(?<!\\)"/g, '').replaceAll('\\"', '"')); form.cmd.push(item.replace(/(?<!\\)"/g, '').replaceAll('\\"', '"'));
} }
} }
form.entrypoint = []; form.entrypoint = [];
if (form.entrypointStr) { if (form.entrypointStr) {
let itemEntrypoint = splitWithQuotes(form.entrypointStr); let itemEntrypoint = splitStringIgnoringQuotes(form.entrypointStr);
for (const item of itemEntrypoint) { for (const item of itemEntrypoint) {
form.entrypoint.push(item.replace(/(?<!\\)"/g, '').replaceAll('\\"', '"')); form.entrypoint.push(item.replace(/(?<!\\)"/g, '').replaceAll('\\"', '"'));
} }
@ -778,15 +777,25 @@ const isFromApp = (rowData: Container.ContainerHelper) => {
return false; return false;
}; };
const splitWithQuotes = (str) => { const escapeQuotes = (input) => {
str = str.replace(/\\"/g, '<quota>'); return input.replace(/(?<!\\)"/g, '\\"');
const regex = /(?=(?:[^'"]|['"][^'"]*['"])*$)\s+/g; };
let parts = str.split(regex).filter(Boolean);
let returnList = []; const splitStringIgnoringQuotes = (input) => {
for (const item of parts) { input = input.replace(/\\"/g, '<quota>');
returnList.push(item.replaceAll('<quota>', '\\"')); const regex = /"([^"]*)"|(\S+)/g;
const result = [];
let match;
while ((match = regex.exec(input)) !== null) {
if (match[1]) {
result.push(match[1].replaceAll('<quota>', '\\"'));
} else if (match[2]) {
result.push(match[2].replaceAll('<quota>', '\\"'));
} }
return returnList; }
return result;
}; };
onMounted(() => { onMounted(() => {

View File

@ -67,7 +67,6 @@ const loading = ref();
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
cacheSizeKey: 'backup-cronjob-page-size',
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,

View File

@ -166,50 +166,26 @@ export function transSpecToObj(spec: string) {
export function transSpecToStr(spec: string): string { export function transSpecToStr(spec: string): string {
const specObj = transSpecToObj(spec); const specObj = transSpecToObj(spec);
let str = '';
if (specObj.specType.indexOf('N') === -1 || specObj.specType === 'perWeek') {
str += i18n.global.t('cronjob.' + specObj.specType) + ' ';
} else {
str += i18n.global.t('cronjob.per') + ' ';
}
switch (specObj.specType) { switch (specObj.specType) {
case 'perMonth': case 'perMonth':
str += return i18n.global.t('cronjob.perMonthHelper', [specObj.day, specObj.hour, loadZero(specObj.minute)]);
specObj.day +
i18n.global.t('cronjob.day') +
' ' +
loadZero(specObj.hour) +
':' +
loadZero(specObj.minute);
break;
case 'perWeek': case 'perWeek':
str += loadWeek(specObj.week) + ' ' + loadZero(specObj.hour) + ':' + loadZero(specObj.minute); return i18n.global.t('cronjob.perWeekHelper', [
break; loadWeek(specObj.week),
specObj.hour,
loadZero(specObj.minute),
]);
case 'perDay': case 'perDay':
str += loadZero(specObj.hour) + ':' + loadZero(specObj.minute); return i18n.global.t('cronjob.perDayHelper', [specObj.hour, loadZero(specObj.minute)]);
break;
case 'perNDay':
str +=
specObj.day +
i18n.global.t('commons.units.day') +
', ' +
loadZero(specObj.hour) +
':' +
loadZero(specObj.minute);
break;
case 'perNHour':
str += specObj.hour + i18n.global.t('commons.units.hour') + ', ' + loadZero(specObj.minute);
break;
case 'perHour': case 'perHour':
str += loadZero(specObj.minute); return i18n.global.t('cronjob.perHourHelper', [loadZero(specObj.minute)]);
break; case 'perNDay':
return i18n.global.t('cronjob.perNDayHelper', [specObj.day, specObj.hour, loadZero(specObj.minute)]);
case 'perNHour':
return i18n.global.t('cronjob.perNHourHelper', [specObj.hour, loadZero(specObj.minute)]);
case 'perNMinute': case 'perNMinute':
str += loadZero(specObj.minute) + i18n.global.t('commons.units.minute'); return i18n.global.t('cronjob.perNMinuteHelper', [loadZero(specObj.minute)]);
break;
case 'perNSecond': case 'perNSecond':
str += loadZero(specObj.second) + i18n.global.t('commons.units.second'); return i18n.global.t('cronjob.perNSecondHelper', [loadZero(specObj.second)]);
break;
} }
return str + ' ' + i18n.global.t('cronjob.handle');
} }

View File

@ -102,7 +102,7 @@ const bindRef = ref();
const data = ref(); const data = ref();
const paginationConfig = reactive({ const paginationConfig = reactive({
cacheSizeKey: 'backup-page-size', cacheSizeKey: 'license-page-size',
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,