mirror of https://github.com/ouqiang/gocron
主机、任务日志列表增加搜索功能
parent
9339137bca
commit
09c6927884
|
@ -1,6 +1,9 @@
|
|||
package models
|
||||
|
||||
import "github.com/ouqiang/gocron/modules/ssh"
|
||||
import (
|
||||
"github.com/ouqiang/gocron/modules/ssh"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
@ -60,10 +63,12 @@ func (host *Host) NameExists(name string, id int16) (bool, error) {
|
|||
return count > 0, err
|
||||
}
|
||||
|
||||
func (host *Host) List() ([]Host, error) {
|
||||
func (host *Host) List(params CommonMap) ([]Host, error) {
|
||||
host.parsePageAndPageSize()
|
||||
list := make([]Host, 0)
|
||||
err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||
session := Db.Desc("id")
|
||||
host.parseWhere(session, params)
|
||||
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
@ -80,6 +85,21 @@ func (host *Host) Total() (int64, error) {
|
|||
return Db.Count(host)
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("id = ?", id)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("name = ?", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (host *Host) parsePageAndPageSize() {
|
||||
if host.Page <= 0 {
|
||||
host.Page = Page
|
||||
|
|
|
@ -130,7 +130,7 @@ func (task *Task) List(params CommonMap) ([]TaskHost, error) {
|
|||
list := make([]TaskHost, 0)
|
||||
fields := "t.*, host.alias"
|
||||
session := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id")
|
||||
parseWhere(session, params)
|
||||
task.parseWhere(session, params)
|
||||
err := session.Cols(fields).Desc("t.id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
||||
|
||||
return list, err
|
||||
|
@ -141,17 +141,21 @@ func (task *Task) Total() (int64, error) {
|
|||
}
|
||||
|
||||
// 解析where
|
||||
func parseWhere(session *xorm.Session, params CommonMap) {
|
||||
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("t.id = ?", id)
|
||||
}
|
||||
hostId, ok := params["HostId"]
|
||||
if ok && hostId.(int) > 0 {
|
||||
session.And("host_id = ? ", hostId)
|
||||
session.And("host_id = ?", hostId)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("name = ?", name)
|
||||
session.And("t.name LIKE ?", "%" + name.(string) + "%")
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package models
|
|||
|
||||
import (
|
||||
"time"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
type TaskType int8
|
||||
|
@ -20,7 +21,7 @@ type TaskLog struct {
|
|||
Hostname string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名,逗号分隔
|
||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
||||
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败
|
||||
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕
|
||||
Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果
|
||||
Page int `xorm:"-"`
|
||||
PageSize int `xorm:"-"`
|
||||
|
@ -47,10 +48,12 @@ func (taskLog *TaskLog) setStatus(id int64, status Status) (int64, error) {
|
|||
return taskLog.Update(id, CommonMap{"status": status})
|
||||
}
|
||||
|
||||
func (taskLog *TaskLog) List() ([]TaskLog, error) {
|
||||
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
||||
taskLog.parsePageAndPageSize()
|
||||
list := make([]TaskLog, 0)
|
||||
err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||
session := Db.Desc("id")
|
||||
taskLog.parseWhere(session, params)
|
||||
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||
if len(list) > 0 {
|
||||
for i, item := range list {
|
||||
endTime := item.EndTime
|
||||
|
@ -85,4 +88,23 @@ func (taskLog *TaskLog) parsePageAndPageSize() {
|
|||
|
||||
func (taskLog *TaskLog) pageLimitOffset() int {
|
||||
return (taskLog.Page - 1) * taskLog.PageSize
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
taskId, ok := params["TaskId"]
|
||||
if ok && taskId.(int) > 0 {
|
||||
session.And("task_id = ?", taskId)
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
session.And("protocol = ?", protocol)
|
||||
}
|
||||
status, ok := params["Status"]
|
||||
if ok && status.(int) > -1 {
|
||||
session.And("status = ?", status)
|
||||
}
|
||||
}
|
|
@ -24,8 +24,6 @@ function Util() {
|
|||
swal(FAILURE_MESSAGE, '未知错误', 'error');
|
||||
};
|
||||
util.get = function(url, callback) {
|
||||
var SUCCESS = 0; // 操作成功
|
||||
var FAILURE_MESSAGE = '操作失败';
|
||||
$.get(
|
||||
url,
|
||||
function(response) {
|
||||
|
@ -76,4 +74,25 @@ function Util() {
|
|||
return util;
|
||||
}
|
||||
|
||||
function registerSelectFormValidation(type, $form, $select, selectName) {
|
||||
$.fn.form.settings.rules[type] = function(value) {
|
||||
var success = true;
|
||||
var selectedIndex = $($form).form("get value", selectName);
|
||||
$($select).find("option").each(function() {
|
||||
var value = $(this).val();
|
||||
var match = $(this).data("match");
|
||||
var validateType = $(this).data("validate-type");
|
||||
if (selectedIndex == value && validateType == type && match) {
|
||||
var matchValue = $($form).form("get value", match);
|
||||
if (!$.trim(matchValue)) {
|
||||
success = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return success;
|
||||
};
|
||||
}
|
||||
|
||||
var util = new Util();
|
|
@ -12,12 +12,14 @@ import (
|
|||
|
||||
func Index(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hosts, err := hostModel.List()
|
||||
queryParams := parseQueryParams(ctx)
|
||||
hosts, err := hostModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Title"] = "主机列表"
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "host/index")
|
||||
}
|
||||
|
||||
|
@ -142,4 +144,15 @@ func Remove(ctx *macaron.Context) string {
|
|||
}
|
||||
|
||||
return json.Success("操作成功", nil)
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
params["Page"] = ctx.QueryInt("page")
|
||||
params["PageSize"] = ctx.QueryInt("page_size")
|
||||
|
||||
return params
|
||||
}
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/csrf"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/go-macaron/gzip"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
)
|
||||
|
@ -35,6 +34,7 @@ func Register(m *macaron.Macaron) {
|
|||
})
|
||||
// 50x错误
|
||||
m.InternalServerError(func(ctx *macaron.Context) {
|
||||
logger.Debug("500错误")
|
||||
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
|
||||
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
|
||||
ctx.HTML(500, "error/500")
|
||||
|
@ -85,7 +85,6 @@ func Register(m *macaron.Macaron) {
|
|||
func RegisterMiddleware(m *macaron.Macaron) {
|
||||
m.Use(macaron.Logger())
|
||||
m.Use(macaron.Recovery())
|
||||
m.Use(gzip.Gziper())
|
||||
m.Use(macaron.Static(StaticDir))
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: "templates",
|
||||
|
@ -106,7 +105,7 @@ func RegisterMiddleware(m *macaron.Macaron) {
|
|||
// 系统未安装,重定向到安装页面
|
||||
m.Use(func(ctx *macaron.Context) {
|
||||
installUrl := "/install"
|
||||
if strings.HasPrefix(ctx.Req.RequestURI, installUrl) {
|
||||
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
|
||||
return
|
||||
}
|
||||
if !app.Installed {
|
||||
|
@ -116,6 +115,16 @@ func RegisterMiddleware(m *macaron.Macaron) {
|
|||
// 设置模板共享变量
|
||||
m.Use(func(ctx *macaron.Context) {
|
||||
ctx.Data["URI"] = ctx.Req.URL.Path
|
||||
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
|
||||
paths := strings.Split(urlPath, "/")
|
||||
ctx.Data["Controller"] = ""
|
||||
ctx.Data["Action"] = ""
|
||||
if len(paths) > 0 {
|
||||
ctx.Data["Controller"] = paths[0]
|
||||
}
|
||||
if len(paths) > 1 {
|
||||
ctx.Data["Action"] = paths[1]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func Create(ctx *macaron.Context) {
|
|||
func Edit(ctx *macaron.Context) {
|
||||
id := ctx.ParamsInt(":id")
|
||||
hostModel := new(models.Host)
|
||||
hosts, err := hostModel.List()
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil || len(hosts) == 0 {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
@ -87,11 +87,16 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
|||
return json.CommonFailure("请选择主机名")
|
||||
}
|
||||
|
||||
if form.Protocol != models.TaskHTTP {
|
||||
taskModel.HostId = form.HostId
|
||||
} else {
|
||||
taskModel.HostId = 0
|
||||
}
|
||||
|
||||
taskModel.Name = form.Name
|
||||
taskModel.Protocol = form.Protocol
|
||||
taskModel.Command = form.Command
|
||||
taskModel.Timeout = form.Timeout
|
||||
taskModel.HostId = form.HostId
|
||||
taskModel.Remark = form.Remark
|
||||
taskModel.Status = form.Status
|
||||
taskModel.RetryTimes = form.RetryTimes
|
||||
|
@ -195,21 +200,20 @@ func addTaskToTimer(id int) {
|
|||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["HostId"] = ctx.QueryInt("host_id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
params["Status"] = ctx.QueryInt("status")
|
||||
params["Status"] = ctx.QueryInt("status") - 1
|
||||
params["Page"] = ctx.QueryInt("page")
|
||||
params["PageSize"] = ctx.QueryInt("page_size")
|
||||
|
||||
logger.Debug("%+v", params)
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
func setHostsToTemplate(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hosts, err := hostModel.List()
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil || len(hosts) == 0 {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
|
|
@ -12,12 +12,14 @@ import (
|
|||
|
||||
func Index(ctx *macaron.Context) {
|
||||
logModel := new(models.TaskLog)
|
||||
logs, err := logModel.List()
|
||||
queryParams := parseQueryParams(ctx)
|
||||
logs, err := logModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Title"] = "任务日志"
|
||||
ctx.Data["Logs"] = logs
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "task/log")
|
||||
}
|
||||
|
||||
|
@ -31,4 +33,16 @@ func Clear(ctx *macaron.Context) string {
|
|||
}
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["TaskId"] = ctx.QueryInt("task_id")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
params["Status"] = ctx.QueryInt("status") - 1
|
||||
params["Page"] = ctx.QueryInt("page")
|
||||
params["PageSize"] = ctx.QueryInt("page_size")
|
||||
|
||||
return params
|
||||
}
|
|
@ -57,6 +57,7 @@ func (task *Task) Add(taskModel models.TaskHost) {
|
|||
}
|
||||
|
||||
cronName := strconv.Itoa(taskModel.Id)
|
||||
// Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题
|
||||
Cron.RemoveJob(cronName)
|
||||
err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
||||
if err != nil {
|
||||
|
@ -123,20 +124,17 @@ func (h *HTTPHandler) Run(taskModel models.TaskHost) (result string, err error)
|
|||
req.Header.Set("User-Agent", "golang/gocron")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
logger.Error("任务处理HTTP请求错误-", err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logger.Error("任务处理#读取HTTP请求返回值失败-", err.Error())
|
||||
return
|
||||
}
|
||||
// 返回状态码非200,均为失败
|
||||
if resp.StatusCode != 200 {
|
||||
return string(body), errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
|
||||
}
|
||||
|
@ -248,10 +246,10 @@ func execJob(handler Handler, taskModel models.TaskHost) TaskResult {
|
|||
return TaskResult{Result: output, Err: err, RetryTimes: i}
|
||||
}
|
||||
i++
|
||||
// 重试规则,每次递增1分钟
|
||||
time.Sleep( time.Duration(i) * 60 * time.Second)
|
||||
if i < execTimes {
|
||||
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
|
||||
// 重试间隔时间,每次递增1分钟
|
||||
time.Sleep( time.Duration(i) * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="bigcontainer">
|
||||
<div class="fl">
|
||||
<p>© 2017 gocron
|
||||
<i class="Github Alternate icon"></i><a href="https://github.com/ouqiang/cron-scheduler" target="_blank">
|
||||
<i class="Github Alternate icon"></i><a href="https://github.com/ouqiang/gocron" target="_blank">
|
||||
GitHub
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
select {
|
||||
height: 40px;
|
||||
}
|
||||
.fields.search > .field {
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -53,11 +59,11 @@
|
|||
<div class="ui teal inverted menu">
|
||||
<div class="bigcontainer">
|
||||
<div class="right menu">
|
||||
<a class="item {{{if eq .URI "/"}}}active{{{end}}}" href="/"><i class="home icon"></i>首页</a>
|
||||
<a class="item {{{if eq .URI "/task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
|
||||
<a class="item {{{if eq .URI "/host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a>
|
||||
<a class="item {{{if eq .URI "/user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a>
|
||||
<a class="item {{{if eq .URI "/admin"}}}active{{{end}}}" href="/admin"><i class="settings icon"></i>管理</a>
|
||||
<a class="item {{{if eq .Controller ""}}}active{{{end}}}" href="/"><i class="home icon"></i>首页</a>
|
||||
<a class="item {{{if eq .Controller "task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
|
||||
<a class="item {{{if eq .Controller "host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a>
|
||||
<a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a>
|
||||
<a class="item {{{if eq .Controller "admin"}}}active{{{end}}}" href="/admin"><i class="settings icon"></i>管理</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,7 +1,6 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
<!--the vertical menu-->
|
||||
{{{ template "host/menu" . }}}
|
||||
|
||||
<div class="twelve wide column">
|
||||
|
@ -48,19 +47,10 @@
|
|||
<div class="four fields">
|
||||
<div class="field">
|
||||
<label>认证方式</label>
|
||||
<div class="ui dropdown selection">
|
||||
{{{ if .Host }}}
|
||||
<input type="hidden" name="auth_type" value="{{{if eq .Host.AuthType 1 }}}1{{{else}}}2{{{end}}}">
|
||||
{{{else}}}
|
||||
<input type="hidden" name="auth_type" value="2">
|
||||
{{{end}}}
|
||||
<div class="default text">公钥</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="2">公钥</div>
|
||||
<div class="item" data-value="1">密码</div>
|
||||
</div>
|
||||
</div>
|
||||
<select name="auth_type" id="authType">
|
||||
<option value="2" {{{if .Host}}} {{{if eq .Host.AuthType 2}}}selected {{{end}}} {{{end}}} data-validate-type="selectPrivateKey" data-match="private_key">公钥</option>
|
||||
<option value="1" {{{if .Host}}} {{{if eq .Host.AuthType 1}}}selected {{{end}}} {{{end}}} data-validate-type="selectPassword" data-match="password">密码</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
|
@ -91,12 +81,14 @@
|
|||
<div class="ui primary submit button">保存</div>
|
||||
</form>
|
||||
</div>
|
||||
<!--the newDevice form-->
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$('.ui.form').form(
|
||||
var $uiForm = $('.ui.form');
|
||||
registerSelectFormValidation("selectPrivateKey", $uiForm, $('#authType'), 'auth_type');
|
||||
registerSelectFormValidation("selectPassword", $uiForm, $('#authType'), 'auth_type');
|
||||
$($uiForm).form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/host/store', fields, function(code, message) {
|
||||
|
@ -137,8 +129,26 @@
|
|||
identifier : 'port',
|
||||
rules: [
|
||||
{
|
||||
type : 'integer',
|
||||
prompt : '请输入SSH端口'
|
||||
type : 'integer[1..65535]',
|
||||
prompt : '请输入有效的端口号'
|
||||
}
|
||||
]
|
||||
},
|
||||
PrivateKey: {
|
||||
identifier : 'private_key',
|
||||
rules: [
|
||||
{
|
||||
type : 'selectPrivateKey',
|
||||
prompt : '请输入私钥'
|
||||
}
|
||||
]
|
||||
},
|
||||
Password: {
|
||||
identifier : 'password',
|
||||
rules: [
|
||||
{
|
||||
type : 'selectPassword',
|
||||
prompt : '请输入密码'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -14,9 +14,23 @@
|
|||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<form class="ui form">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<input type="text" placeholder="ID" name="id" value="{{{if gt .Params.Id 0}}}{{{.Params.Id}}}{{{end}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="text" placeholder="主机名" name="name" value="{{{.Params.Name}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui linkedin submit button">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="ui striped table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>主机名</th>
|
||||
<th>别名</th>
|
||||
<th>用户名</th>
|
||||
|
@ -28,6 +42,7 @@
|
|||
<tbody>
|
||||
{{{range $i, $v := .Hosts}}}
|
||||
<tr>
|
||||
<td>{{{.Id}}}</td>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Alias}}}</td>
|
||||
<td>{{{.Username}}}</td>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
{{{template "task/menu" .}}}
|
||||
<div class="twelve wide column">
|
||||
|
@ -14,107 +13,74 @@
|
|||
</div>
|
||||
</div>
|
||||
<form class="ui form">
|
||||
<div class="five fields">
|
||||
<div class="field">
|
||||
<input type="text" placeholder="任务名称" name="name">
|
||||
<div class="six fields search">
|
||||
<div class="one wide field">
|
||||
<input type="text" placeholder="任务ID" name="id" value="{{{if gt .Params.Id 0}}}{{{.Params.Id}}}{{{end}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>主机</label>
|
||||
<div class="ui dropdown selection">
|
||||
<input type="hidden" name="protocol" value="{{{if gt .Params.Protocol 0}}}{{{.Params.Protocol}}}{{{else}}}3{{{end}}}">
|
||||
<div class="default text">本地命令</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="3">本地命令</div>
|
||||
<div class="item" data-value="2">SSH</div>
|
||||
<div class="item" data-value="1">HTTP</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline fields">
|
||||
<input type="text" placeholder="任务名称" name="name" value="{{{.Params.Name}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<select name="host_id" id="hostId">
|
||||
<option value="">选择主机</option>
|
||||
{{{range $i, $v := .Hosts}}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}"
|
||||
{{{if eq $.Params.HostId .Id }}} checked {{{end}}}>
|
||||
</div>
|
||||
</div>
|
||||
<option value="{{{.Id}}}" {{{if eq $.Params.HostId .Id }}} selected {{{end}}} >{{{.Alias}}}-{{{.Name}}}</option>
|
||||
{{{end}}}
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui dropdown selection">
|
||||
<input type="hidden" name="protocol" value="{{{if gt .Params.Protocol 0}}}{{{.Params.Protocol}}}{{{else}}}3{{{end}}}">
|
||||
<div class="default text">本地命令</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="-1">协议</div>
|
||||
<div class="item" data-value="3">本地命令</div>
|
||||
<div class="item" data-value="2">SSH</div>
|
||||
<div class="item" data-value="1">HTTP</div>
|
||||
</div>
|
||||
</div>
|
||||
<select name="protocol" id="protocol">
|
||||
<option value="0">选择协议</option>
|
||||
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>本地命令</option>
|
||||
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
||||
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui dropdown selection">
|
||||
<input type="hidden" name="status" value="{{{if eq .Params.Status 1}}}1{{{else}}}2{{{end}}}">
|
||||
<div class="default text">暂停</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="-1">状态</div>
|
||||
<div class="item" data-value="2">暂停</div>
|
||||
<div class="item" data-value="1">激活</div>
|
||||
</div>
|
||||
</div>
|
||||
<select name="status">
|
||||
<option value="0">状态</option>
|
||||
<option value="1" {{{if eq .Params.Status 0}}}selected{{{end}}} >暂停</option>
|
||||
<option value="2" {{{if eq .Params.Status 1}}}selected{{{end}}}>激活</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui linkedin submit button">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="ui violet table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务名称</th>
|
||||
<th>cron表达式</th>
|
||||
<th>协议</th>
|
||||
<th width="5%">命令</th>
|
||||
<th>超时时间(秒)</th>
|
||||
<th>重试次数</th>
|
||||
<th>主机</th>
|
||||
<th>备注</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{range $i, $v := .Tasks}}}
|
||||
<tr>
|
||||
<td>{{{.Task.Name}}}</td>
|
||||
<td>{{{.Spec}}}</td>
|
||||
<td>{{{if eq .Protocol 1}}} HTTP {{{else}}} SSH {{{end}}}</td>
|
||||
<td>{{{.Command}}}</td>
|
||||
<td>{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
|
||||
<td>{{{.RetryTimes}}}</td>
|
||||
<td>{{{.Alias}}}</td>
|
||||
<td>{{{.Remark}}}</td>
|
||||
<td>{{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}}</td>
|
||||
<td>
|
||||
<a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a>
|
||||
{{{if eq .Status 1}}}
|
||||
<button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button>
|
||||
{{{else}}}
|
||||
<button class="ui blue button" @click="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
|
||||
{{{end}}}
|
||||
<button class="ui positive button" @click="remove({{{.Id}}})">删除</button> <br>
|
||||
<div style="margin-top:10px;">
|
||||
<button class="ui twitter button" @click="run({{{.Id}}})">手动运行</button>
|
||||
<a class="ui instagram button" href="/task/log?task_id={{{.Id}}}">查看日志</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{{end}}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{{range $i, $v := .Tasks}}}
|
||||
<div class="ui device two column middle aligned vertical grid list segment">
|
||||
<div class="column verborder">
|
||||
<div class="ui info segment">
|
||||
<h5 class="ui header">{{{.Task.Name}}} {{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}}
|
||||
</h5>
|
||||
<p>任务ID: <span class="stress">{{{.Id}}}</span></p>
|
||||
<p>cron表达式: {{{.Spec}}}</p>
|
||||
<p>协议: {{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else if eq .Protocol 3}}}本地命令{{{end}}}</p>
|
||||
<p class="sensorStatus">命令:{{{.Command}}}</p>
|
||||
<p class="sensorStatus">超时时间:{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</p>
|
||||
<p>重试次数: {{{.RetryTimes}}}</p>
|
||||
{{{if eq .Protocol 2}}}
|
||||
<p>主机: {{{.Alias}}}</p>
|
||||
{{{end}}}
|
||||
<p>备注: {{{.Remark}}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center aligned column">
|
||||
<div class="ui buttons">
|
||||
<a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a>
|
||||
{{{if eq .Status 1}}}
|
||||
<button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button>
|
||||
{{{else}}}
|
||||
<button class="ui blue button" @click="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
|
||||
{{{end}}}
|
||||
<button class="ui positive button" @click="remove({{{.Id}}})">删除</button> <br>
|
||||
<button class="ui twitter button" @click="run({{{.Id}}})">手动运行</button>
|
||||
<a class="ui instagram button" href="/task/log?task_id={{{.Id}}}">查看日志</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{{end}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -124,7 +90,7 @@
|
|||
|
||||
var vue = new Vue(
|
||||
{
|
||||
el: '.ui.violet.table',
|
||||
el: '.ui.list',
|
||||
methods: {
|
||||
changeStatus: function (id ,status) {
|
||||
var url = '';
|
||||
|
@ -155,4 +121,4 @@
|
|||
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
||||
{{{ template "common/footer" . }}}
|
||||
|
|
|
@ -22,9 +22,36 @@
|
|||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<form class="ui form">
|
||||
<div class="six fields search">
|
||||
<div class="field">
|
||||
<input type="text" placeholder="任务ID" name="task_id" value="{{{if gt .Params.TaskId 0}}}{{{.Params.TaskId}}}{{{end}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<select name="protocol" id="protocol">
|
||||
<option value="0">协议</option>
|
||||
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>本地命令</option>
|
||||
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
||||
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<select name="status">
|
||||
<option value="0">状态</option>
|
||||
<option value="1" {{{if eq .Params.Status 0}}}selected{{{end}}} >失败</option>
|
||||
<option value="2" {{{if eq .Params.Status 1}}}selected{{{end}}}>执行中</option>
|
||||
<option value="3" {{{if eq .Params.Status 2}}}selected{{{end}}}>成功</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui linkedin submit button">搜索</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<table class="ui pink table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务ID</th>
|
||||
<th>任务名称</th>
|
||||
<th>cron表达式</th>
|
||||
<th>协议</th>
|
||||
|
@ -39,6 +66,7 @@
|
|||
<tbody>
|
||||
{{{range $i, $v := .Logs}}}
|
||||
<tr>
|
||||
<td><a href="/task?id={{{.TaskId}}}">{{{.TaskId}}}</a></td>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Spec}}}</td>
|
||||
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else}}} 本地命令 {{{end}}}</td>
|
||||
|
@ -128,55 +156,5 @@
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/host/store', fields, function(code, message) {
|
||||
location.reload();
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
identifier : 'name',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机名'
|
||||
}
|
||||
]
|
||||
},
|
||||
alias: {
|
||||
identifier : 'alias',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入主机别名'
|
||||
}
|
||||
]
|
||||
},
|
||||
username: {
|
||||
identifier : 'username',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入SSH用户名'
|
||||
}
|
||||
]
|
||||
},
|
||||
port: {
|
||||
identifier : 'port',
|
||||
rules: [
|
||||
{
|
||||
type : 'integer',
|
||||
prompt : '请输入SSH端口'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
{{{ template "common/footer" . }}}
|
|
@ -1,5 +1,4 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
{{{template "task/menu" .}}}
|
||||
<div class="twelve wide column">
|
||||
|
@ -25,47 +24,31 @@
|
|||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>crontab表达式 (每行一个表达式)</label>
|
||||
<textarea rows="5" name="spec">{{{.Task.Spec}}}</textarea>
|
||||
<label>crontab表达式</label>
|
||||
<div class="ui small left icon input">
|
||||
<textarea rows="5" name="spec">{{{.Task.Spec}}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>协议</label>
|
||||
<div class="ui dropdown selection">
|
||||
{{{if .Task}}}
|
||||
<input type="hidden" name="protocol" value="{{{if (gt .Task.Protocol 0)}}}{{{.Task.Protocol}}}{{{else}}}3{{{end}}}">
|
||||
{{{else}}}
|
||||
<input type="hidden" name="protocol" value="3">
|
||||
{{{end}}}
|
||||
<div class="default text">本地命令</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="3">本地命令</div>
|
||||
<div class="item" data-value="2">SSH</div>
|
||||
<div class="item" data-value="1">HTTP</div>
|
||||
</div>
|
||||
</div>
|
||||
<select name="protocol" id="protocol">
|
||||
<option value="3" {{{if .Task}}} {{{if eq .Task.Protocol 3}}}selected{{{end}}} {{{end}}}>本地命令</option>
|
||||
<option value="2" {{{if .Task}}} {{{if eq .Task.Protocol 2}}}selected{{{end}}} {{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
|
||||
<option value="1" {{{if .Task}}} {{{if eq .Task.Protocol 1}}}selected{{{end}}} {{{end}}}>HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>主机</label>
|
||||
<div class="inline fields">
|
||||
<select name="host_id" id="hostId">
|
||||
<option value="">选择主机</option>
|
||||
{{{range $i, $v := .Hosts}}}
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
{{{if $.Task}}}
|
||||
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}"
|
||||
{{{if and (eq $.Task.Protocol 2) (eq $.Task.HostId .Id) }}} checked {{{end}}}
|
||||
>
|
||||
{{{else}}}
|
||||
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}">
|
||||
{{{end}}}
|
||||
<label>{{{.Alias}}}-{{{.Name}}}</label>
|
||||
</div>
|
||||
<option value="{{{.Id}}}" {{{if $.Task}}}{{{if eq $.Task.HostId .Id }}} selected {{{end}}} {{{end}}}>{{{.Alias}}}-{{{.Name}}}</option>
|
||||
</div>
|
||||
{{{end}}}
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
|
@ -73,32 +56,23 @@
|
|||
<textarea rows="5" name="command">{{{.Task.Command}}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>任务超时时间 (单位秒, 默认0,不限制超时)</label>
|
||||
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>任务执行事变 (单位秒,0不限制,不能超过24小时)</label>
|
||||
<label>任务重试次数 (默认0, 取值范围1-10)</label>
|
||||
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>任务状态 (任务添加成功后,是否立即调度)</label>
|
||||
<div class="ui dropdown selection">
|
||||
{{{if .Task}}}
|
||||
<input type="hidden" name="status" value="{{{if and .Task.Status (eq .Task.Status 1)}}}1{{{else}}}2{{{end}}}">
|
||||
{{{else}}}
|
||||
<input type="hidden" name="status" value="2">
|
||||
{{{end}}}
|
||||
<div class="default text">暂停</div>
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<div class="item" data-value="2">暂停</div>
|
||||
<div class="item" data-value="1">激活</div>
|
||||
</div>
|
||||
</div>
|
||||
<select name="status">
|
||||
<option value="2"{{{if .Task}}} {{{if eq .Task.Status 2}}}selected{{{end}}} {{{end}}}>暂停</option>
|
||||
<option value="1" {{{if .Task}}} {{{if eq .Task.Status 1}}}selected{{{end}}} {{{end}}}>激活</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
|
@ -117,7 +91,9 @@
|
|||
$('.ui.checkbox')
|
||||
.checkbox()
|
||||
;
|
||||
$('.ui.form').form(
|
||||
var $uiForm = $('.ui.form');
|
||||
registerSelectFormValidation("selectProtocol", $uiForm, $('#protocol'), 'protocol');
|
||||
$($uiForm).form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/task/store', fields, function(code, message) {
|
||||
|
@ -153,11 +129,12 @@
|
|||
prompt : '请输入任务命令'
|
||||
}
|
||||
]
|
||||
}, hosts: {
|
||||
identifier : 'hosts',
|
||||
},
|
||||
hosts: {
|
||||
identifier : 'host_id',
|
||||
rules: [
|
||||
{
|
||||
type : 'checked',
|
||||
type : 'selectProtocol',
|
||||
prompt : '请选择主机'
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue