mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
parent
f3fd02fcb3
commit
86bf7ea73a
@ -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 修改系统参数
|
||||
|
@ -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"`
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -227,6 +227,7 @@ TaskRollback: "Rollback"
|
||||
TaskPull: "Pull"
|
||||
TaskBuild: "Build"
|
||||
TaskPush: "Push"
|
||||
TaskHandle: "Execute"
|
||||
Website: "Website"
|
||||
App: "App"
|
||||
Runtime: "Runtime"
|
||||
|
@ -229,6 +229,7 @@ TaskRollback: "回滚"
|
||||
TaskPull: "拉取"
|
||||
TaskBuild: "建構"
|
||||
TaskPush: "推送"
|
||||
TaskHandle: "執行"
|
||||
Website: "網站"
|
||||
App: "應用"
|
||||
Runtime: "運行環境"
|
||||
|
@ -231,6 +231,7 @@ TaskRollback: "回滚"
|
||||
TaskPull: "拉取"
|
||||
TaskBuild: "构建"
|
||||
TaskPush: "推送"
|
||||
TaskHandle: "执行"
|
||||
Website: "网站"
|
||||
App: "应用"
|
||||
Runtime: "运行环境"
|
||||
|
@ -22,6 +22,7 @@ func Init() {
|
||||
migrations.AddTaskDB,
|
||||
migrations.UpdateAppInstall,
|
||||
migrations.UpdateSnapshot,
|
||||
migrations.UpdateCronjob,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -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{})
|
||||
},
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -10,11 +10,15 @@ export namespace Cronjob {
|
||||
specs: Array<string>;
|
||||
specObjs: Array<SpecObj>;
|
||||
|
||||
executor: string;
|
||||
isExecutorCustom: boolean;
|
||||
script: string;
|
||||
scriptMode: string;
|
||||
isCustom: boolean;
|
||||
command: string;
|
||||
inContainer: boolean;
|
||||
containerName: string;
|
||||
user: string;
|
||||
appID: string;
|
||||
website: string;
|
||||
exclusionRules: string;
|
||||
|
@ -12,6 +12,9 @@ export const getDeviceBase = () => {
|
||||
export const loadTimeZoneOptions = () => {
|
||||
return http.get<Array<string>>(`/toolbox/device/zone/options`);
|
||||
};
|
||||
export const loadUsers = () => {
|
||||
return http.get<Array<string>>(`/toolbox/device/users`);
|
||||
};
|
||||
export const updateDevice = (key: string, value: string) => {
|
||||
return http.post(`/toolbox/device/update/conf`, { key: key, value: value }, TimeoutEnum.T_60S);
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -878,6 +878,7 @@ const message = {
|
||||
saturday: '周六',
|
||||
sunday: '周日',
|
||||
shellContent: '腳本內容',
|
||||
executor: '解釋器',
|
||||
errRecord: '錯誤的日誌記錄',
|
||||
errHandle: '任務執行失敗',
|
||||
noRecord: '當前計劃任務暫未產生記錄',
|
||||
|
@ -879,6 +879,7 @@ const message = {
|
||||
saturday: '周六',
|
||||
sunday: '周日',
|
||||
shellContent: '脚本内容',
|
||||
executor: '解释器',
|
||||
errRecord: '错误的日志记录',
|
||||
errHandle: '任务执行失败',
|
||||
noRecord: '当前计划任务暂未产生记录',
|
||||
|
@ -67,149 +67,251 @@
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
||||
<el-input :disabled="dialogData.title === 'edit'" clearable v-model.trim="dialogData.rowData!.name" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('cronjob.cronSpec')" prop="specCustom">
|
||||
<el-checkbox :label="$t('container.custom')" v-model="dialogData.rowData!.specCustom" />
|
||||
</el-form-item>
|
||||
<div v-if="!dialogData.rowData!.specCustom">
|
||||
<el-form-item prop="spec">
|
||||
<div v-for="(specObj, index) of dialogData.rowData.specObjs" :key="index" style="width: 100%">
|
||||
<el-select class="specTypeClass" v-model="specObj.specType" @change="changeSpecType(index)">
|
||||
<el-option
|
||||
v-for="item in specOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select v-if="specObj.specType === 'perWeek'" class="specClass" v-model="specObj.week">
|
||||
<el-option
|
||||
v-for="item in weekOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-if="hasDay(specObj)" class="specClass" v-model.number="specObj.day">
|
||||
<template #append>
|
||||
<div class="append">{{ $t('cronjob.day') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input v-if="hasHour(specObj)" class="specClass" v-model.number="specObj.hour">
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.hour') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="specObj.specType !== 'perNSecond'"
|
||||
class="specClass"
|
||||
v-model.number="specObj.minute"
|
||||
>
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.minute') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="specObj.specType === 'perNSecond'"
|
||||
class="specClass"
|
||||
v-model.number="specObj.second"
|
||||
>
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.second') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-popover placement="top-start" :title="$t('cronjob.nextTime')" width="200" trigger="click">
|
||||
<div v-for="(time, index_t) of nextTimes" :key="index_t">
|
||||
<el-tag class="mt-2">{{ time }}</el-tag>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="ml-2.5" @click="loadNext(specObj)" link type="primary">
|
||||
{{ $t('commons.button.preview') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
class="ml-2.5"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSpecDelete(index)"
|
||||
v-if="dialogData.rowData.specObjs.length > 1"
|
||||
>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
<el-divider v-if="dialogData.rowData.specObjs.length > 1" class="divider" />
|
||||
</div>
|
||||
<el-card>
|
||||
<el-form-item :label="$t('cronjob.cronSpec')" prop="specCustom">
|
||||
<el-checkbox :label="$t('container.custom')" v-model="dialogData.rowData!.specCustom" />
|
||||
</el-form-item>
|
||||
<el-button class="mb-3" @click="handleSpecAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="!dialogData.rowData!.specCustom">
|
||||
<el-form-item prop="spec">
|
||||
<div v-for="(specObj, index) of dialogData.rowData.specObjs" :key="index" style="width: 100%">
|
||||
<el-select class="specTypeClass" v-model="specObj.specType" @change="changeSpecType(index)">
|
||||
<el-option
|
||||
v-for="item in specOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select v-if="specObj.specType === 'perWeek'" class="specClass" v-model="specObj.week">
|
||||
<el-option
|
||||
v-for="item in weekOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-if="hasDay(specObj)" class="specClass" v-model.number="specObj.day">
|
||||
<template #append>
|
||||
<div class="append">{{ $t('cronjob.day') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input v-if="hasHour(specObj)" class="specClass" v-model.number="specObj.hour">
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.hour') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="specObj.specType !== 'perNSecond'"
|
||||
class="specClass"
|
||||
v-model.number="specObj.minute"
|
||||
>
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.minute') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="specObj.specType === 'perNSecond'"
|
||||
class="specClass"
|
||||
v-model.number="specObj.second"
|
||||
>
|
||||
<template #append>
|
||||
<div class="append">{{ $t('commons.units.second') }}</div>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
:title="$t('cronjob.nextTime')"
|
||||
width="200"
|
||||
trigger="click"
|
||||
>
|
||||
<div v-for="(time, index_t) of nextTimes" :key="index_t">
|
||||
<el-tag class="mt-2">{{ time }}</el-tag>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="ml-2.5" @click="loadNext(specObj)" link type="primary">
|
||||
{{ $t('commons.button.preview') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
class="ml-2.5"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSpecDelete(index)"
|
||||
v-if="dialogData.rowData.specObjs.length > 1"
|
||||
>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
<el-divider v-if="dialogData.rowData.specObjs.length > 1" class="divider" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-button class="mb-3" @click="handleSpecAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="dialogData.rowData!.specCustom">
|
||||
<el-form-item prop="spec">
|
||||
<div v-for="(spec, index) of dialogData.rowData.specs" :key="index" style="width: 100%">
|
||||
<el-input style="width: 80%" v-model="dialogData.rowData.specs[index]" />
|
||||
<el-popover placement="top-start" :title="$t('cronjob.nextTime')" width="200" trigger="click">
|
||||
<div v-for="(time, index_t) of nextTimes" :key="index_t">
|
||||
<el-tag class="mt-2">{{ time }}</el-tag>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="ml-2.5" @click="loadNext(spec)" link type="primary">
|
||||
{{ $t('commons.button.preview') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
class="ml-2.5"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSpecCustomDelete(index)"
|
||||
v-if="dialogData.rowData.specs.length > 1"
|
||||
>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
<el-divider v-if="dialogData.rowData.specs.length > 1" class="divider" />
|
||||
</div>
|
||||
<div v-if="dialogData.rowData!.specCustom">
|
||||
<el-form-item prop="spec">
|
||||
<div v-for="(spec, index) of dialogData.rowData.specs" :key="index">
|
||||
<el-input style="width: 80%" v-model="dialogData.rowData.specs[index]" />
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
:title="$t('cronjob.nextTime')"
|
||||
width="200"
|
||||
trigger="click"
|
||||
>
|
||||
<div v-for="(time, index_t) of nextTimes" :key="index_t">
|
||||
<el-tag class="mt-2">{{ time }}</el-tag>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="ml-2.5" @click="loadNext(spec)" link type="primary">
|
||||
{{ $t('commons.button.preview') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
class="ml-2.5"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleSpecCustomDelete(index)"
|
||||
v-if="dialogData.rowData.specs.length > 1"
|
||||
>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
<el-divider v-if="dialogData.rowData.specs.length > 1" class="divider" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-button class="mb-3" @click="handleSpecCustomAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div v-if="hasScript()">
|
||||
<el-form-item class="mt-5">
|
||||
<el-checkbox v-model="dialogData.rowData!.inContainer">
|
||||
{{ $t('cronjob.containerCheckBox') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-card v-if="dialogData.rowData!.inContainer">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('cronjob.containerName')" prop="containerName">
|
||||
<el-select class="selectClass" v-model="dialogData.rowData!.containerName">
|
||||
<el-option
|
||||
v-for="item in containerOptions"
|
||||
:key="item"
|
||||
:value="item"
|
||||
:label="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('container.command')" prop="command" :rules="Rules.requiredInput">
|
||||
<el-checkbox border v-model="dialogData.rowData!.isCustom">
|
||||
{{ $t('container.custom') }}
|
||||
</el-checkbox>
|
||||
<el-select
|
||||
v-if="!dialogData.rowData!.isCustom"
|
||||
style="width: calc(100% - 100px)"
|
||||
filterable
|
||||
clearable
|
||||
v-model="dialogData.rowData!.command"
|
||||
>
|
||||
<el-option value="ash" label="/bin/ash" />
|
||||
<el-option value="bash" label="/bin/bash" />
|
||||
<el-option value="sh" label="/bin/sh" />
|
||||
</el-select>
|
||||
<el-input
|
||||
clearable
|
||||
v-else
|
||||
style="width: calc(100% - 100px)"
|
||||
v-model="dialogData.rowData!.command"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<div v-if="!dialogData.rowData!.inContainer">
|
||||
<el-card>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
:label="$t('commons.table.user')"
|
||||
prop="user"
|
||||
:rules="Rules.requiredSelect"
|
||||
>
|
||||
<el-select filterable v-model="dialogData.rowData!.user">
|
||||
<div v-for="item in userOptions" :key="item">
|
||||
<el-option :value="item" :label="item" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
:label="$t('cronjob.executor')"
|
||||
prop="executor"
|
||||
:rules="Rules.requiredSelect"
|
||||
>
|
||||
<el-checkbox border v-model="dialogData.rowData!.isExecutorCustom">
|
||||
{{ $t('container.custom') }}
|
||||
</el-checkbox>
|
||||
<el-select
|
||||
v-if="!dialogData.rowData!.isExecutorCustom"
|
||||
style="width: calc(100% - 100px)"
|
||||
v-model="dialogData.rowData!.executor"
|
||||
>
|
||||
<el-option value="bash" label="bash" />
|
||||
<el-option value="python" label="python" />
|
||||
<el-option value="sh" label="sh" />
|
||||
</el-select>
|
||||
<el-input
|
||||
clearable
|
||||
v-else
|
||||
style="width: calc(100% - 100px)"
|
||||
v-model="dialogData.rowData!.executor"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-form-item :label="$t('cronjob.shellContent')" prop="script" class="mt-5">
|
||||
<el-radio-group v-model="dialogData.rowData!.scriptMode">
|
||||
<el-radio value="input">{{ $t('commons.button.edit') }}</el-radio>
|
||||
<el-radio value="select">{{ $t('container.pathSelect') }}</el-radio>
|
||||
</el-radio-group>
|
||||
<CodemirrorPro
|
||||
v-if="dialogData.rowData!.scriptMode=== 'input'"
|
||||
v-model="dialogData.rowData!.script"
|
||||
placeholder="#Define or paste the content of your shell file here"
|
||||
mode="javascript"
|
||||
:heightDiff="400"
|
||||
/>
|
||||
<el-input
|
||||
v-if="dialogData.rowData!.scriptMode=== 'select'"
|
||||
:placeholder="$t('commons.example') + '/tmp/test.sh'"
|
||||
v-model="dialogData.rowData!.script"
|
||||
>
|
||||
<template #prepend>
|
||||
<FileList @choose="loadScriptDir" :dir="false"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item :label="$t('cronjob.shellContent')" v-else prop="script" class="mt-5">
|
||||
<CodemirrorPro
|
||||
v-if="dialogData.rowData!.scriptMode=== 'input'"
|
||||
v-model="dialogData.rowData!.script"
|
||||
placeholder="#Define or paste the content of your shell file here"
|
||||
mode="javascript"
|
||||
:heightDiff="400"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button class="mb-3" @click="handleSpecCustomAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-form-item v-if="hasScript()">
|
||||
<el-checkbox v-model="dialogData.rowData!.inContainer">
|
||||
{{ $t('cronjob.containerCheckBox') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="hasScript() && dialogData.rowData!.inContainer"
|
||||
:label="$t('cronjob.containerName')"
|
||||
prop="containerName"
|
||||
>
|
||||
<el-select class="selectClass" v-model="dialogData.rowData!.containerName">
|
||||
<el-option v-for="item in containerOptions" :key="item" :value="item" :label="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="hasScript() && dialogData.rowData!.inContainer"
|
||||
:label="$t('container.command')"
|
||||
prop="command"
|
||||
:rules="Rules.requiredInput"
|
||||
>
|
||||
<el-checkbox class="w-full" border v-model="dialogData.rowData!.isCustom">
|
||||
{{ $t('container.custom') }}
|
||||
</el-checkbox>
|
||||
<el-select style="width: calc(100% - 100px)" filterable clearable v-model="dialogData.rowData!.command">
|
||||
<el-option value="ash" label="/bin/ash" />
|
||||
<el-option value="bash" label="/bin/bash" />
|
||||
<el-option value="sh" label="/bin/sh" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
|
||||
<el-input clearable type="textarea" :rows="5" v-model="dialogData.rowData!.script" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'cutWebsiteLog'"
|
||||
:label="dialogData.rowData!.type === 'website' ? $t('cronjob.website'):$t('website.website')"
|
||||
@ -383,6 +485,7 @@ import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { addCronjob, editCronjob, loadNextHandle } from '@/api/modules/cronjob';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import { listDbItems } from '@/api/modules/database';
|
||||
import { GetWebsiteOptions } from '@/api/modules/website';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
@ -398,6 +501,7 @@ import {
|
||||
transSpecToObj,
|
||||
weekOptions,
|
||||
} from './../helper';
|
||||
import { loadUsers } from '@/api/modules/toolbox';
|
||||
const router = useRouter();
|
||||
|
||||
interface DialogProps {
|
||||
@ -428,6 +532,7 @@ const acceptParams = (params: DialogProps): void => {
|
||||
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<Database.DbItem>,
|
||||
});
|
||||
|
||||
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 || [];
|
||||
|
Loading…
x
Reference in New Issue
Block a user