From 86bf7ea73a35485e26d654d2b1d1e555437b0e0e Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:05:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=94=AF=E6=8C=81=E9=80=89=E6=8B=A9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=8F=8A=E8=A7=A3=E9=87=8A=E5=99=A8=20(#6721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #3933 #2738 --- agent/app/api/v2/device.go | 15 + agent/app/dto/cronjob.go | 30 +- agent/app/model/cronjob.go | 11 +- agent/app/service/cronjob.go | 11 +- agent/app/service/cronjob_helper.go | 45 +- agent/app/service/device.go | 16 + agent/app/task/task.go | 2 + agent/i18n/lang/en.yaml | 1 + agent/i18n/lang/zh-Hant.yaml | 1 + agent/i18n/lang/zh.yaml | 1 + agent/init/migration/migrate.go | 1 + agent/init/migration/migrations/init.go | 7 + agent/router/ro_toolbox.go | 1 + agent/utils/cmd/cmd.go | 5 +- frontend/src/api/interface/cronjob.ts | 4 + frontend/src/api/modules/toolbox.ts | 3 + .../src/components/codemirror-pro/index.vue | 4 + frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + frontend/src/views/cronjob/operate/index.vue | 411 ++++++++++++------ 21 files changed, 392 insertions(+), 180 deletions(-) diff --git a/agent/app/api/v2/device.go b/agent/app/api/v2/device.go index 455e3b20a..fb8cc8222 100644 --- a/agent/app/api/v2/device.go +++ b/agent/app/api/v2/device.go @@ -86,6 +86,21 @@ func (b *BaseApi) UpdateDeviceByFile(c *gin.Context) { helper.SuccessWithData(c, nil) } +// @Tags Device +// @Summary Load user list +// @Description 获取服务器用户列表 +// @Success 200 +// @Security ApiKeyAuth +// @Router /toolbox/device/users [get] +func (b *BaseApi) LoadUsers(c *gin.Context) { + users, err := deviceService.LoadUsers() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, users) +} + // @Tags Device // @Summary Update device // @Description 修改系统参数 diff --git a/agent/app/dto/cronjob.go b/agent/app/dto/cronjob.go index f17307ca0..cdb859fae 100644 --- a/agent/app/dto/cronjob.go +++ b/agent/app/dto/cronjob.go @@ -21,9 +21,13 @@ type CronjobCreate struct { SpecCustom bool `json:"specCustom"` Spec string `json:"spec" validate:"required"` - Script string `json:"script"` - Command string `json:"command"` - ContainerName string `json:"containerName"` + Executor string `json:"executor"` + ScriptMode string `json:"scriptMode"` + Script string `json:"script"` + Command string `json:"command"` + ContainerName string `json:"containerName"` + User string `json:"user"` + AppID string `json:"appID"` Website string `json:"website"` ExclusionRules string `json:"exclusionRules"` @@ -44,9 +48,13 @@ type CronjobUpdate struct { SpecCustom bool `json:"specCustom"` Spec string `json:"spec" validate:"required"` - Script string `json:"script"` - Command string `json:"command"` - ContainerName string `json:"containerName"` + Executor string `json:"executor"` + ScriptMode string `json:"scriptMode"` + Script string `json:"script"` + Command string `json:"command"` + ContainerName string `json:"containerName"` + User string `json:"user"` + AppID string `json:"appID"` Website string `json:"website"` ExclusionRules string `json:"exclusionRules"` @@ -89,9 +97,13 @@ type CronjobInfo struct { SpecCustom bool `json:"specCustom"` Spec string `json:"spec"` - Script string `json:"script"` - Command string `json:"command"` - ContainerName string `json:"containerName"` + Executor string `json:"executor"` + ScriptMode string `json:"scriptMode"` + Script string `json:"script"` + Command string `json:"command"` + ContainerName string `json:"containerName"` + User string `json:"user"` + AppID string `json:"appID"` Website string `json:"website"` ExclusionRules string `json:"exclusionRules"` diff --git a/agent/app/model/cronjob.go b/agent/app/model/cronjob.go index 5709ee082..fc9945df3 100644 --- a/agent/app/model/cronjob.go +++ b/agent/app/model/cronjob.go @@ -14,9 +14,13 @@ type Cronjob struct { SpecCustom bool `json:"specCustom"` Spec string `gorm:"not null" json:"spec"` - Command string `json:"command"` - ContainerName string `json:"containerName"` - Script string `json:"script"` + Executor string `json:"executor"` + Command string `json:"command"` + ContainerName string `json:"containerName"` + ScriptMode string `json:"scriptMode"` + Script string `json:"script"` + User string `json:"user"` + Website string `json:"website"` AppID string `json:"appID"` DBType string `json:"dbType"` @@ -39,6 +43,7 @@ type JobRecords struct { BaseModel CronjobID uint `json:"cronjobID"` + TaskID string `json:"taskID"` StartTime time.Time `json:"startTime"` Interval float64 `json:"interval"` Records string `json:"records"` diff --git a/agent/app/service/cronjob.go b/agent/app/service/cronjob.go index 8ba9d54c1..9603fea32 100644 --- a/agent/app/service/cronjob.go +++ b/agent/app/service/cronjob.go @@ -182,13 +182,13 @@ func (u *CronjobService) HandleOnce(id uint) error { return nil } -func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error { - cronjob, _ := cronjobRepo.Get(commonRepo.WithByName(cronjobDto.Name)) +func (u *CronjobService) Create(req dto.CronjobCreate) error { + cronjob, _ := cronjobRepo.Get(commonRepo.WithByName(req.Name)) if cronjob.ID != 0 { return constant.ErrRecordExist } - cronjob.Secret = cronjobDto.Secret - if err := copier.Copy(&cronjob, &cronjobDto); err != nil { + cronjob.Secret = req.Secret + if err := copier.Copy(&cronjob, &req); err != nil { return errors.WithMessage(constant.ErrStructTransform, err.Error()) } cronjob.Status = constant.StatusEnable @@ -282,8 +282,11 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error { upMap["spec_custom"] = req.SpecCustom upMap["spec"] = spec upMap["script"] = req.Script + upMap["script_mode"] = req.ScriptMode upMap["command"] = req.Command upMap["container_name"] = req.ContainerName + upMap["executor"] = req.Executor + upMap["user"] = req.User upMap["app_id"] = req.AppID upMap["website"] = req.Website upMap["exclusion_rules"] = req.ExclusionRules diff --git a/agent/app/service/cronjob_helper.go b/agent/app/service/cronjob_helper.go index 4768db605..81dcd0668 100644 --- a/agent/app/service/cronjob_helper.go +++ b/agent/app/service/cronjob_helper.go @@ -8,13 +8,12 @@ import ( "strings" "time" - "github.com/1Panel-dev/1Panel/agent/buserr" - "github.com/1Panel-dev/1Panel/agent/i18n" - "github.com/1Panel-dev/1Panel/agent/app/model" "github.com/1Panel-dev/1Panel/agent/app/repo" + "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" + "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/1Panel-dev/1Panel/agent/utils/ntp" @@ -35,15 +34,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) { } record.Records = u.generateLogsPath(*cronjob, record.StartTime) _ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records}) - script := cronjob.Script - if len(cronjob.ContainerName) != 0 { - command := "sh" - if len(cronjob.Command) != 0 { - command = cronjob.Command - } - script = fmt.Sprintf("docker exec %s %s -c \"%s\"", cronjob.ContainerName, command, strings.ReplaceAll(cronjob.Script, "\"", "\\\"")) - } - err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records) + err = u.handleShell(*cronjob, record.Records) u.removeExpiredLog(*cronjob) case "curl": if len(cronjob.URL) == 0 { @@ -51,7 +42,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) { } record.Records = u.generateLogsPath(*cronjob, record.StartTime) _ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records}) - err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("curl '%s'", cronjob.URL), record.Records) + err = cmd.ExecShell(record.Records, 24*time.Hour, "bash", "-c", "curl", cronjob.URL) u.removeExpiredLog(*cronjob) case "ntp": err = u.handleNtpSync() @@ -100,17 +91,29 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) { }() } -func (u *CronjobService) handleShell(cronType, cornName, script, logPath string) error { - handleDir := fmt.Sprintf("%s/task/%s/%s", constant.DataDir, cronType, cornName) - if _, err := os.Stat(handleDir); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(handleDir, os.ModePerm); err != nil { +func (u *CronjobService) handleShell(cronjob model.Cronjob, logPath string) error { + if len(cronjob.ContainerName) != 0 { + command := "sh" + if len(cronjob.Command) != 0 { + command = cronjob.Command + } + scriptFile, _ := os.ReadFile(cronjob.Script) + return cmd.ExecShell(logPath, 24*time.Hour, "docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(string(scriptFile), "\"", "\\\"")) + } + if cronjob.ScriptMode == "input" { + fileItem := path.Join(global.CONF.System.BaseDir, "1panel", "task", "shell", cronjob.Name, cronjob.Name+".sh") + _ = os.MkdirAll(path.Dir(fileItem), os.ModePerm) + shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { return err } + defer shellFile.Close() + if _, err := shellFile.WriteString(cronjob.Script); err != nil { + return err + } + return cmd.ExecShell(logPath, 24*time.Hour, "sudo", "-u", cronjob.User, cronjob.Executor, fileItem) } - if err := cmd.ExecCronjobWithTimeOut(script, handleDir, logPath, 24*time.Hour); err != nil { - return err - } - return nil + return cmd.ExecShell(logPath, 24*time.Hour, "sudo", "-u", cronjob.User, cronjob.Executor, cronjob.Script) } func (u *CronjobService) handleNtpSync() error { diff --git a/agent/app/service/device.go b/agent/app/service/device.go index eca21dd7a..6a2dccaab 100644 --- a/agent/app/service/device.go +++ b/agent/app/service/device.go @@ -37,6 +37,7 @@ type IDeviceService interface { LoadTimeZone() ([]string, error) CheckDNS(key, value string) (bool, error) LoadConf(name string) (string, error) + LoadUsers() ([]string, error) Scan() dto.CleanData Clean(req []dto.Clean) @@ -168,6 +169,21 @@ func (u *DeviceService) Update(key, value string) error { return nil } +func (u *DeviceService) LoadUsers() ([]string, error) { + file, err := os.ReadFile("/etc/passwd") + if err != nil { + return nil, err + } + var users []string + lines := strings.Split(string(file), "\n") + for _, line := range lines { + if strings.Contains(line, ":") { + users = append(users, strings.Split(line, ":")[0]) + } + } + return users, nil +} + func (u *DeviceService) UpdateHosts(req []dto.HostHelper) error { conf, err := os.ReadFile(defaultHostPath) if err != nil { diff --git a/agent/app/task/task.go b/agent/app/task/task.go index dcbabe0cb..2a89031c7 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -58,6 +58,7 @@ const ( TaskBuild = "TaskBuild" TaskPull = "TaskPull" TaskPush = "TaskPush" + TaskHandle = "TaskHandle" ) const ( @@ -65,6 +66,7 @@ const ( TaskScopeApp = "App" TaskScopeRuntime = "Runtime" TaskScopeDatabase = "Database" + TaskScopeCronjob = "Cronjob" TaskScopeAppStore = "AppStore" TaskScopeSnapshot = "Snapshot" TaskScopeContainer = "Container" diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index fffec4c77..83522769b 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -227,6 +227,7 @@ TaskRollback: "Rollback" TaskPull: "Pull" TaskBuild: "Build" TaskPush: "Push" +TaskHandle: "Execute" Website: "Website" App: "App" Runtime: "Runtime" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 256ca4b2f..07dcefbda 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -229,6 +229,7 @@ TaskRollback: "回滚" TaskPull: "拉取" TaskBuild: "建構" TaskPush: "推送" +TaskHandle: "執行" Website: "網站" App: "應用" Runtime: "運行環境" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 2fb15aae5..412fb19be 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -231,6 +231,7 @@ TaskRollback: "回滚" TaskPull: "拉取" TaskBuild: "构建" TaskPush: "推送" +TaskHandle: "执行" Website: "网站" App: "应用" Runtime: "运行环境" diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index cf5b14619..328fc7812 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -22,6 +22,7 @@ func Init() { migrations.AddTaskDB, migrations.UpdateAppInstall, migrations.UpdateSnapshot, + migrations.UpdateCronjob, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 0d68399f7..b2078a5ec 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -266,3 +266,10 @@ var UpdateSnapshot = &gormigrate.Migration{ return tx.AutoMigrate(&model.Snapshot{}) }, } + +var UpdateCronjob = &gormigrate.Migration{ + ID: "20241011-update-cronjob", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate(&model.Cronjob{}, &model.JobRecords{}) + }, +} diff --git a/agent/router/ro_toolbox.go b/agent/router/ro_toolbox.go index a72fe546c..a65f02293 100644 --- a/agent/router/ro_toolbox.go +++ b/agent/router/ro_toolbox.go @@ -12,6 +12,7 @@ func (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) { baseApi := v2.ApiGroupApp.BaseApi { toolboxRouter.POST("/device/base", baseApi.LoadDeviceBaseInfo) + toolboxRouter.GET("/device/users", baseApi.LoadUsers) toolboxRouter.GET("/device/zone/options", baseApi.LoadTimeOption) toolboxRouter.POST("/device/update/conf", baseApi.UpdateDeviceConf) toolboxRouter.POST("/device/update/host", baseApi.UpdateDeviceHost) diff --git a/agent/utils/cmd/cmd.go b/agent/utils/cmd/cmd.go index a331e85a3..8b31105ce 100644 --- a/agent/utils/cmd/cmd.go +++ b/agent/utils/cmd/cmd.go @@ -107,15 +107,14 @@ func ExecContainerScript(containerName, cmdStr string, timeout time.Duration) er return nil } -func ExecCronjobWithTimeOut(cmdStr, workdir, outPath string, timeout time.Duration) error { +func ExecShell(outPath string, timeout time.Duration, name string, arg ...string) error { file, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { return err } defer file.Close() - cmd := exec.Command("bash", "-c", cmdStr) - cmd.Dir = workdir + cmd := exec.Command(name, arg...) cmd.Stdout = file cmd.Stderr = file if err := cmd.Start(); err != nil { diff --git a/frontend/src/api/interface/cronjob.ts b/frontend/src/api/interface/cronjob.ts index 89d385285..37a6b1cef 100644 --- a/frontend/src/api/interface/cronjob.ts +++ b/frontend/src/api/interface/cronjob.ts @@ -10,11 +10,15 @@ export namespace Cronjob { specs: Array; specObjs: Array; + executor: string; + isExecutorCustom: boolean; script: string; + scriptMode: string; isCustom: boolean; command: string; inContainer: boolean; containerName: string; + user: string; appID: string; website: string; exclusionRules: string; diff --git a/frontend/src/api/modules/toolbox.ts b/frontend/src/api/modules/toolbox.ts index 0002b37a0..26e4137bf 100644 --- a/frontend/src/api/modules/toolbox.ts +++ b/frontend/src/api/modules/toolbox.ts @@ -12,6 +12,9 @@ export const getDeviceBase = () => { export const loadTimeZoneOptions = () => { return http.get>(`/toolbox/device/zone/options`); }; +export const loadUsers = () => { + return http.get>(`/toolbox/device/users`); +}; export const updateDevice = (key: string, value: string) => { return http.post(`/toolbox/device/update/conf`, { key: key, value: value }, TimeoutEnum.T_60S); }; diff --git a/frontend/src/components/codemirror-pro/index.vue b/frontend/src/components/codemirror-pro/index.vue index 27649a84f..e39ec9d67 100644 --- a/frontend/src/components/codemirror-pro/index.vue +++ b/frontend/src/components/codemirror-pro/index.vue @@ -13,6 +13,7 @@ import { oneDark } from '@codemirror/theme-one-dark'; import { StreamLanguage } from '@codemirror/language'; import { nginx } from './nginx'; import { yaml } from '@codemirror/legacy-modes/mode/yaml'; +import { shell } from '@codemirror/legacy-modes/mode/shell'; import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile'; import { placeholder } from '@codemirror/view'; import { json } from '@codemirror/lang-json'; @@ -93,6 +94,9 @@ const initCodeMirror = () => { case 'json': extensions.push(json()); break; + case 'shell': + extensions.push(StreamLanguage.define(shell)); + break; } let startState = EditorState.create({ doc: content.value, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 64bb6019f..4a8dce4d4 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -923,6 +923,7 @@ const message = { saturday: 'Saturday', sunday: 'Sunday', shellContent: 'Script', + executor: 'Executor', errRecord: 'Incorrect logging', errHandle: 'Cronjob execution failure', noRecord: 'The execution did not generate any logs', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 0030044cc..6aba0c741 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -878,6 +878,7 @@ const message = { saturday: '周六', sunday: '周日', shellContent: '腳本內容', + executor: '解釋器', errRecord: '錯誤的日誌記錄', errHandle: '任務執行失敗', noRecord: '當前計劃任務暫未產生記錄', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 04aca3b54..190b64f49 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -879,6 +879,7 @@ const message = { saturday: '周六', sunday: '周日', shellContent: '脚本内容', + executor: '解释器', errRecord: '错误的日志记录', errHandle: '任务执行失败', noRecord: '当前计划任务暂未产生记录', diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue index 6beced29b..54db995d3 100644 --- a/frontend/src/views/cronjob/operate/index.vue +++ b/frontend/src/views/cronjob/operate/index.vue @@ -67,149 +67,251 @@ - - - - -
- -
- - - - - - - - - - - - - - - - - - - -
- {{ time }} -
- -
- - {{ $t('commons.button.delete') }} - - -
+ + + - - {{ $t('commons.button.add') }} - -
+
+ +
+ + + + + + + + + + + + + + + + + + + +
+ {{ time }} +
+ +
+ + {{ $t('commons.button.delete') }} + + +
+
+ + {{ $t('commons.button.add') }} + +
-
- -
- - -
- {{ time }} -
- -
- - {{ $t('commons.button.delete') }} - - -
+
+ +
+ + +
+ {{ time }} +
+ +
+ + {{ $t('commons.button.delete') }} + + +
+
+ + {{ $t('commons.button.add') }} + +
+ + +
+ + + {{ $t('cronjob.containerCheckBox') }} + + + + + + + + + + + + + + + {{ $t('container.custom') }} + + + + + + + + + + + +
+ + + + + +
+ +
+
+
+
+ + + + {{ $t('container.custom') }} + + + + + + + + + +
+
+ + + + {{ $t('commons.button.edit') }} + {{ $t('container.pathSelect') }} + + + + + + +
+ + - - {{ $t('commons.button.add') }} -
- - - {{ $t('cronjob.containerCheckBox') }} - - - - - - - - - - {{ $t('container.custom') }} - - - - - - - - - - - - { dialogData.value.rowData.specs = dialogData.value.rowData.specs || []; if (dialogData.value.title === 'create') { changeType(); + dialogData.value.rowData.scriptMode = 'input'; dialogData.value.rowData.dbType = 'mysql'; dialogData.value.rowData.downloadAccountID = 1; } @@ -442,7 +547,14 @@ const acceptParams = (params: DialogProps): void => { dialogData.value.rowData!.isCustom = dialogData.value.rowData!.command !== 'sh' && dialogData.value.rowData!.command !== 'bash' && - dialogData.value.rowData!.command === 'ash'; + dialogData.value.rowData!.command !== 'ash'; + + dialogData.value.rowData!.executor = dialogData.value.rowData!.executor || 'bash'; + dialogData.value.rowData!.isCustom = + dialogData.value.rowData!.executor !== 'sh' && + dialogData.value.rowData!.executor !== 'bash' && + dialogData.value.rowData!.executor !== 'python' && + dialogData.value.rowData!.executor !== 'python3'; title.value = i18n.global.t('cronjob.' + dialogData.value.title); if (dialogData.value?.rowData?.exclusionRules) { @@ -454,6 +566,7 @@ const acceptParams = (params: DialogProps): void => { drawerVisible.value = true; loadBackups(); loadAppInstalls(); + loadShellUsers(); loadWebsites(); loadContainers(); if (dialogData.value.rowData?.dbType) { @@ -477,6 +590,7 @@ const websiteOptions = ref([]); const backupOptions = ref([]); const accountOptions = ref([]); const appOptions = ref([]); +const userOptions = ref([]); const dbInfo = reactive({ isExist: false, @@ -485,6 +599,14 @@ const dbInfo = reactive({ dbs: [] as Array, }); +const verifyScript = (rule: any, value: any, callback: any) => { + if (!dialogData.value.rowData!.script || dialogData.value.rowData!.script.length === 0) { + callback(new Error(i18n.global.t('commons.rule.requiredInput'))); + return; + } + callback(); +}; + const verifySpec = (rule: any, value: any, callback: any) => { if (dialogData.value.rowData!.specObjs.length === 0) { callback(new Error(i18n.global.t('commons.rule.requiredInput'))); @@ -583,7 +705,7 @@ const rules = reactive({ { validator: verifySpec, trigger: 'change', required: true }, ], - script: [Rules.requiredInput], + script: [{ validator: verifyScript, trigger: 'blur', required: true }], website: [Rules.requiredSelect], dbName: [Rules.requiredSelect], url: [Rules.requiredInput], @@ -600,6 +722,10 @@ const loadDir = async (path: string) => { dialogData.value.rowData!.sourceDir = path; }; +const loadScriptDir = async (path: string) => { + dialogData.value.rowData!.script = path; +}; + const hasDay = (item: any) => { return item.specType === 'perMonth' || item.specType === 'perNDay'; }; @@ -724,6 +850,11 @@ const changeAccount = async () => { } }; +const loadShellUsers = async () => { + const res = await loadUsers(); + userOptions.value = res.data || []; +}; + const loadAppInstalls = async () => { const res = await ListAppInstalled(); appOptions.value = res.data || [];