Skip to content

Commit a253a5f

Browse files
author
Jiangshui Lee
committed
worker dev
1 parent b503012 commit a253a5f

File tree

18 files changed

+588
-17
lines changed

18 files changed

+588
-17
lines changed

common/constants.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package common
22

33
const (
4-
SaveTaskKeyPrefix string = "/cron/tasks/"
5-
KillTaskKeyPrefix string = "/cron/killers/"
4+
SAVE_TASK_PATH string = "/cron/tasks/"
5+
KILL_TASK_PATH string = "/cron/killers/"
66
)

common/errors.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package common
2+
3+
import "errors"
4+
5+
var (
6+
ErrorTaskFieldIsNil = errors.New("task field all be required")
7+
)

common/model/task.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package model
22

33
type Task struct {
4-
Name string `json:"name"`
5-
Command string `json:"command"`
6-
CronExpr string `json:"cron_expr"`
4+
Name string `json:"name" valid:"required"`
5+
Command string `json:"command" valid:"required"`
6+
CronExpr string `json:"cron_expr" valid:"required"`
77
}

common/util.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package common
2+
3+
import (
4+
"crontab/common/model"
5+
"encoding/json"
6+
"strings"
7+
)
8+
9+
func UnpackJsonToTask(value []byte) (task *model.Task, err error) {
10+
task = &model.Task{}
11+
if err = json.Unmarshal(value, &task); err != nil {
12+
return
13+
}
14+
15+
return
16+
}
17+
18+
func ExtractNameFromPath(path string) (name string) {
19+
return strings.TrimPrefix(path, SAVE_TASK_PATH)
20+
}

master/api.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ func saveTask(c *gin.Context) {
1818
goto ERR
1919
}
2020

21+
if len(task.Name) <= 0 || len(task.Command) <= 0 || len(task.CronExpr) <= 0 {
22+
err = common.ErrorTaskFieldIsNil
23+
goto ERR
24+
}
25+
2126
if oldTask, err = GlobalTaskMgr.SaveTask(task); err != nil {
2227
goto ERR
2328
}
@@ -71,15 +76,15 @@ ERR:
7176

7277
func killTask(c *gin.Context) {
7378
var (
74-
err error
75-
task *model.Task
79+
err error
80+
taskName string
7681
)
7782

78-
if err = c.BindJSON(&task); err != nil {
83+
if err = c.BindJSON(&taskName); err != nil {
7984
goto ERR
8085
}
8186

82-
if err = GlobalTaskMgr.KillTask(task.Name); err != nil {
87+
if err = GlobalTaskMgr.KillTask(taskName); err != nil {
8388
goto ERR
8489
}
8590

master/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Config struct {
1111
type Base struct {
1212
LogConfigPath string
1313
Mode string
14+
WebRoot string
1415
}
1516

1617
type ApiConf struct {

master/controller.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func InitController(ports []string) (err error) {
5656
}
5757

5858
func initHandler() http.Handler {
59-
gin.SetMode(gin.DebugMode)
59+
gin.SetMode(Conf.Base.Mode)
6060
handler := gin.New()
6161
handler.Use(cors.Middleware(cors.Config{
6262
Origins: "*",
@@ -72,7 +72,14 @@ func initHandler() http.Handler {
7272
handler.GET("/", func(ctx *gin.Context) {
7373
ctx.JSON(200, "test")
7474
})
75-
Route(handler.Group(""))
75+
76+
// static file mapping
77+
if len(Conf.Base.WebRoot) > 0 {
78+
handler.Static("/web", Conf.Base.WebRoot)
79+
}
80+
81+
// api group
82+
Route(handler.Group("api"))
7683

7784
return handler
7885
}

master/main/config.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
LogConfigPath=./logconfig_dev.json
55
# App Run Mode: debug/release
66
Mode=debug
7+
# Web root
8+
WebRoot=/Users/jayson/go/src/crontab/master/main/web
79

810
# Api Request Configuration
911
[ApiConf]

master/main/master.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func Init() {
5353
goto ERR
5454
}
5555

56+
println("master.Init() run complete!")
57+
5658
c = make(chan os.Signal, 1)
5759
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
5860
for {

master/main/web/index.html

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Golang分布式Crontab</title>
8+
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
9+
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
10+
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.min.js"></script>
11+
</head>
12+
<body>
13+
<div class="container-fluid">
14+
<!-- 页头 -->
15+
<div class="row">
16+
<div class="col-md-12">
17+
<div class="page-header">
18+
<h1>管理后台</h1><small>Golang分布式Crontab</small>
19+
</div>
20+
</div>
21+
</div>
22+
23+
<!-- 功能按钮 -->
24+
<div class="row">
25+
<div class="col-md-12">
26+
<button type="button" class="btn btn-primary" id="btn-create">新建任务</button>
27+
</div>
28+
</div>
29+
30+
<!-- 任务列表 -->
31+
<div class="row">
32+
<div class="col-md-12">
33+
<div class="panel panel-default" style="margin-top:20px;">
34+
<div class="panel-body">
35+
<table id="task-list" class="table table-striped table-hover">
36+
<thead>
37+
<tr>
38+
<th>任务名称</th>
39+
<th>Shell命令</th>
40+
<th>Crontab表达式</th>
41+
<th>任务操作</th>
42+
</tr>
43+
</thead>
44+
<tbody>
45+
<!-- <tr>
46+
<td class="task-name">tt1</td>
47+
<td class="task-command">echo hello2</td>
48+
<td class="task-cronExpr">* * * * * </td>
49+
<td>
50+
<div class="btn-toolbar">
51+
<button class="btn btn-info edit-task">编辑</button>
52+
<button class="btn btn-danger delete-task" style="margin-left:10px;">删除</button>
53+
<button class="btn btn-warning kill-task" style="margin-left:10px;">强杀</button>
54+
<button class="btn btn-success log-task" style="margin-left:10px;">日志</button>
55+
</div>
56+
</td>
57+
</tr> -->
58+
</tbody>
59+
</table>
60+
</div>
61+
</div>
62+
</div>
63+
</div>
64+
</div>
65+
66+
<!-- 编辑和新增的模态框 -->
67+
<div id="task-model" class="modal fade" role="dialog" aria-hidden="true" tabindex="-1">
68+
<div class="modal-dialog">
69+
<div class="modal-content">
70+
<div class="modal-header">
71+
<h5 class="modal-title">编辑任务</h5>
72+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" aria-hidden="true"></button>
73+
</div>
74+
<div class="modal-body">
75+
<form>
76+
<div class="form-group">
77+
<label for="edit-name"><h6>任务名称</h6></label>
78+
<input type="text" class="form-control" id="edit-name" placeholder="task name">
79+
</div>
80+
<div class="form-group">
81+
<label for="edit-command"><h6>Shell命令</h6></label>
82+
<input type="text" class="form-control" id="edit-command" placeholder="shell command">
83+
</div>
84+
<div class="form-group">
85+
<label for="edit-cronExpr"><h6>Cron表达式</h6></label>
86+
<input type="text" class="form-control" id="edit-cronExpr" placeholder="crontab expression">
87+
</div>
88+
</form>
89+
</div>
90+
<div class="modal-footer">
91+
<button type="button" class="btn btn-default" data-bs-dismiss="modal">取消</button>
92+
<button type="button" class="btn btn-info" id="btn-save">保存</button>
93+
</div>
94+
</div>
95+
</div>
96+
</div>
97+
98+
<!-- script脚本 -->
99+
<script>
100+
$(document).ready(function(){
101+
// edit task
102+
$('#task-list').on('click', '.edit-task', function(event){
103+
var taskName = $(this).parents('tr').children('.task-name').text()
104+
var taskCommand = $(this).parents('tr').children('.task-command').text()
105+
var taskCronExpr = $(this).parents('tr').children('.task-cronExpr').text()
106+
107+
$('#edit-name').val(taskName)
108+
$('#edit-command').val(taskCommand)
109+
$('#edit-cronExpr').val(taskCronExpr)
110+
$('#task-model').modal('show')
111+
reloadTaskList()
112+
})
113+
114+
$('#btn-create').on('click',function(event){
115+
$('#edit-name').val('')
116+
$('#edit-command').val('')
117+
$('#edit-cronExpr').val('')
118+
119+
$('#task-model').modal('show')
120+
})
121+
122+
// save task
123+
$('#btn-save').on('click',function(event){
124+
var taskInfo = {name: $('#edit-name').val(), command: $('#edit-command').val(), cron_expr:$('#edit-cronExpr').val()}
125+
$.ajax({
126+
url: '/api/cron/tasks',
127+
dataType: 'json',
128+
method:'POST',
129+
contentType : 'application/json; charset=utf-8',
130+
data: JSON.stringify(taskInfo),
131+
complete: function(){
132+
reloadTaskList()
133+
}
134+
})
135+
136+
$('#task-model').modal('toggle')
137+
reloadTaskList()
138+
})
139+
140+
// delete task
141+
$('#task-list').on('click', '.delete-task', function(event){
142+
var taskName = $(this).parents('tr').children('.task-name').text()
143+
$.ajax({
144+
url: '/api/cron/tasks/' + taskName,
145+
dataType: 'json',
146+
method:'DELETE',
147+
complete: function(){
148+
reloadTaskList()
149+
}
150+
})
151+
})
152+
153+
// kill task
154+
$('#task-list').on('click', '.kill-task', function(event){
155+
var taskName = $(this).parents('tr').children('.task-name').text()
156+
$.ajax({
157+
url: '/api/cron/task/kill',
158+
dataType: 'json',
159+
method:'POST',
160+
contentType : 'application/json; charset=utf-8',
161+
data: JSON.stringify({name: taskName}),
162+
complete: function(){
163+
reloadTaskList()
164+
}
165+
})
166+
})
167+
168+
// view task log
169+
$('#task-list').on('click', '.log-task', function(event){
170+
171+
})
172+
173+
// refresh the task list
174+
function reloadTaskList() {
175+
$.ajax({
176+
url: '/api/cron/tasks',
177+
dataType: 'json',
178+
method: 'GET',
179+
success: function(resp){
180+
if (resp.code != 0) {
181+
return
182+
}
183+
184+
$('#task-list tbody').empty()
185+
186+
var taskList = resp.data
187+
for(var i = 0;i < taskList.length;i++) {
188+
var task = taskList[i];
189+
var tr = $('<tr>')
190+
.append($('<td class="task-name">').html(task.name))
191+
.append($('<td class="task-command">').html(task.command))
192+
.append($('<td class="task-cronExpr">').html(task.cron_expr));
193+
var toolbar = $('<div class="btn-toolbar">')
194+
.append($('<button class="btn btn-info edit-task">').html('编辑'))
195+
.append($('<button class="btn btn-danger delete-task">').html('删除'))
196+
.append($('<button class="btn btn-warning kill-task">').html('强杀'))
197+
.append($('<button class="btn btn-success log-task">').html('日志'));
198+
tr.append(toolbar)
199+
200+
$('#task-list tbody').append(tr)
201+
}
202+
}
203+
})
204+
}
205+
206+
reloadTaskList()
207+
})
208+
</script>
209+
210+
<style>
211+
.edit-task, .delete-task, .kill-task, .log-task {
212+
margin-left: 10px;
213+
}
214+
215+
.form-control {
216+
margin-top: 10px;
217+
}
218+
219+
.form-group {
220+
margin-top: 20px;
221+
}
222+
</style>
223+
</body>
224+
</html>

master/router.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ func Route(r *gin.RouterGroup) {
99
adminGroup.GET("/tasks", listTask)
1010
adminGroup.POST("/tasks", saveTask)
1111
adminGroup.DELETE("/tasks/:name", removeTask)
12-
adminGroup.POST("/tasks/kill", killTask)
12+
adminGroup.POST("/task/kill", killTask)
1313
}
1414
}

0 commit comments

Comments
 (0)