mirror of https://github.com/shunfei/cronsun
新建/编辑任务添加安全选项检查,添加消息组件
parent
a2c31890ef
commit
fe3dfd4429
|
@ -3,4 +3,6 @@ conf/files/*.json
|
||||||
.tags_sorted_by_file
|
.tags_sorted_by_file
|
||||||
bin/*/*server
|
bin/*/*server
|
||||||
.DS_Store
|
.DS_Store
|
||||||
web/ui/node_modules
|
web/ui/node_modules
|
||||||
|
.vscode
|
||||||
|
*npm-debug.log
|
||||||
|
|
|
@ -14,5 +14,6 @@ var (
|
||||||
ErrEmptyNodeGroupName = errors.New("Name of node group is empty.")
|
ErrEmptyNodeGroupName = errors.New("Name of node group is empty.")
|
||||||
ErrIllegalNodeGroupId = errors.New("Invalid node group id that includes illegal characters such as '/'.")
|
ErrIllegalNodeGroupId = errors.New("Invalid node group id that includes illegal characters such as '/'.")
|
||||||
|
|
||||||
InvalidJobErr = errors.New("invalid job")
|
ErrSecurityInvalidCmd = errors.New("Security error: the suffix of script file is not on the whitelist.")
|
||||||
|
ErrSecurityInvalidUser = errors.New("Security error: the user is not on the whitelist.")
|
||||||
)
|
)
|
||||||
|
|
|
@ -121,8 +121,8 @@ func GetJobs() (jobs map[string]*Job, err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !job.Valid() {
|
if err := job.Valid(); err != nil {
|
||||||
log.Warnf("job[%s] is invalid", string(j.Key))
|
log.Warnf("job[%s] is invalid: %s", string(j.Key), err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +142,7 @@ func GetJobFromKv(kv *mvccpb.KeyValue) (job *Job, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !job.Valid() {
|
err = job.Valid()
|
||||||
err = InvalidJobErr
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +269,7 @@ func (j *Job) Check() error {
|
||||||
return ErrEmptyJobCommand
|
return ErrEmptyJobCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return j.Valid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行结果写入 mongoDB
|
// 执行结果写入 mongoDB
|
||||||
|
@ -309,17 +307,25 @@ func (j *Job) Cmds(nid string, gs map[string]*Group) (cmds map[string]*Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安全选项验证
|
// 安全选项验证
|
||||||
func (j *Job) Valid() bool {
|
func (j *Job) Valid() error {
|
||||||
if len(j.cmd) == 0 {
|
if len(j.cmd) == 0 {
|
||||||
j.splitCmd()
|
j.splitCmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
security := conf.Config.Security
|
security := conf.Config.Security
|
||||||
if !security.Open {
|
if !security.Open {
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return j.validUser() && j.validCmd()
|
if !j.validUser() {
|
||||||
|
return ErrSecurityInvalidUser
|
||||||
|
}
|
||||||
|
|
||||||
|
if !j.validCmd() {
|
||||||
|
return ErrSecurityInvalidCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *Job) validUser() bool {
|
func (j *Job) validUser() bool {
|
||||||
|
@ -339,7 +345,6 @@ func (j *Job) validCmd() bool {
|
||||||
if len(conf.Config.Security.Ext) == 0 {
|
if len(conf.Config.Security.Ext) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ext := range conf.Config.Security.Ext {
|
for _, ext := range conf.Config.Security.Ext {
|
||||||
if strings.HasSuffix(j.cmd[0], ext) {
|
if strings.HasSuffix(j.cmd[0], ext) {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (b BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
buf.Write(debug.Stack())
|
buf.Write(debug.Stack())
|
||||||
stack = buf.String()
|
stack = buf.String()
|
||||||
|
|
||||||
outJSONError(w, http.StatusInternalServerError, "Internal Server Error")
|
outJSONWithCode(w, http.StatusInternalServerError, "Internal Server Error")
|
||||||
|
|
||||||
log.Errorf("%v\n\n%s\n", err_, stack)
|
log.Errorf("%v\n\n%s\n", err_, stack)
|
||||||
return
|
return
|
||||||
|
@ -53,10 +53,3 @@ func outJSONWithCode(w http.ResponseWriter, httpCode int, data interface{}) {
|
||||||
func outJSON(w http.ResponseWriter, data interface{}) {
|
func outJSON(w http.ResponseWriter, data interface{}) {
|
||||||
outJSONWithCode(w, http.StatusOK, data)
|
outJSONWithCode(w, http.StatusOK, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func outJSONError(w http.ResponseWriter, httpCode int, msg string) {
|
|
||||||
r := map[string]string{
|
|
||||||
"error": msg,
|
|
||||||
}
|
|
||||||
outJSONWithCode(w, httpCode, r)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"sunteng/cronsun/conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Configuration struct {
|
||||||
|
Security *securityCnf `json:"security"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityCnf struct {
|
||||||
|
Enable bool `json:"enable"`
|
||||||
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
||||||
|
AllowSuffixs []string `json:"allowSuffixs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfiguration() *Configuration {
|
||||||
|
cnf := &Configuration{
|
||||||
|
Security: &securityCnf{
|
||||||
|
Enable: conf.Config.Security.Open,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Config.Security.Open {
|
||||||
|
cnf.Security.AllowUsers = conf.Config.Security.Users
|
||||||
|
cnf.Security.AllowSuffixs = conf.Config.Security.Ext
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cnf *Configuration) Configuratios(w http.ResponseWriter, r *http.Request) {
|
||||||
|
outJSON(w, cnf)
|
||||||
|
}
|
26
web/job.go
26
web/job.go
|
@ -26,7 +26,7 @@ func (j *Job) GetJob(w http.ResponseWriter, r *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
statusCode = http.StatusInternalServerError
|
statusCode = http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
outJSONError(w, statusCode, err.Error())
|
outJSONWithCode(w, statusCode, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ func (j *Job) DeleteJob(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
_, err := models.DeleteJob(vars["group"], vars["id"])
|
_, err := models.DeleteJob(vars["group"], vars["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ func (j *Job) ChangeJobStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&job)
|
err := decoder.Decode(&job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
|
@ -57,20 +57,20 @@ func (j *Job) ChangeJobStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
originJob, rev, err := models.GetJobAndRev(vars["group"], vars["id"])
|
originJob, rev, err := models.GetJobAndRev(vars["group"], vars["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
originJob.Pause = job.Pause
|
originJob.Pause = job.Pause
|
||||||
b, err := json.Marshal(originJob)
|
b, err := json.Marshal(originJob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.DefalutClient.PutWithModRev(originJob.Key(), string(b), rev)
|
_, err = models.DefalutClient.PutWithModRev(originJob.Key(), string(b), rev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,13 +86,13 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&job)
|
err := decoder.Decode(&job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
|
|
||||||
if err = job.Check(); err != nil {
|
if err = job.Check(); err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +110,13 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
b, err := json.Marshal(job)
|
b, err := json.Marshal(job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = models.DefalutClient.Put(job.Key(), string(b))
|
_, err = models.DefalutClient.Put(job.Key(), string(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
|
||||||
func (j *Job) GetGroups(w http.ResponseWriter, r *http.Request) {
|
func (j *Job) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
resp, err := models.DefalutClient.Get(conf.Config.Cmd, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
resp, err := models.DefalutClient.Get(conf.Config.Cmd, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ func (j *Job) GetList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
resp, err := models.DefalutClient.Get(prefix, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
resp, err := models.DefalutClient.Get(prefix, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ func (j *Job) GetList(w http.ResponseWriter, r *http.Request) {
|
||||||
job := models.Job{}
|
job := models.Job{}
|
||||||
err = json.Unmarshal(resp.Kvs[i].Value, &job)
|
err = json.Unmarshal(resp.Kvs[i].Value, &job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jobList = append(jobList, &jobStatus{Job: &job})
|
jobList = append(jobList, &jobStatus{Job: &job})
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (jl *JobLog) GetList(w http.ResponseWriter, r *http.Request) {
|
||||||
pager.List, pager.Total, err = models.GetJobLogList(query, page, pageSize, sort)
|
pager.List, pager.Total, err = models.GetJobLogList(query, page, pageSize, sort)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
web/node.go
20
web/node.go
|
@ -23,7 +23,7 @@ func (n *Node) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
de := json.NewDecoder(r.Body)
|
de := json.NewDecoder(r.Body)
|
||||||
var err error
|
var err error
|
||||||
if err = de.Decode(&g); err != nil {
|
if err = de.Decode(&g); err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
@ -36,14 +36,14 @@ func (n *Node) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = g.Check(); err != nil {
|
if err = g.Check(); err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO modRev
|
// @TODO modRev
|
||||||
var modRev int64 = 0
|
var modRev int64 = 0
|
||||||
if _, err = g.Put(modRev); err != nil {
|
if _, err = g.Put(modRev); err != nil {
|
||||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ func (n *Node) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
func (n *Node) GetGroups(w http.ResponseWriter, r *http.Request) {
|
func (n *Node) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
resp, err := models.DefalutClient.Get(conf.Config.Group, v3.WithPrefix(), v3.WithSort(v3.SortByKey, v3.SortAscend))
|
resp, err := models.DefalutClient.Get(conf.Config.Group, v3.WithPrefix(), v3.WithSort(v3.SortByKey, v3.SortAscend))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func (n *Node) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
err = json.Unmarshal(resp.Kvs[i].Value, &g)
|
err = json.Unmarshal(resp.Kvs[i].Value, &g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("node.GetGroups(key: %s) error: %s", string(resp.Kvs[i].Key), err.Error())
|
log.Errorf("node.GetGroups(key: %s) error: %s", string(resp.Kvs[i].Key), err.Error())
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
list = append(list, &g)
|
list = append(list, &g)
|
||||||
|
@ -76,7 +76,7 @@ func (n *Node) GetGroupByGroupId(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
g, err := models.GetGroupById(vars["id"])
|
g, err := models.GetGroupById(vars["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,13 +91,13 @@ func (n *Node) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
groupId := strings.TrimSpace(vars["id"])
|
groupId := strings.TrimSpace(vars["id"])
|
||||||
if len(groupId) == 0 {
|
if len(groupId) == 0 {
|
||||||
outJSONError(w, http.StatusBadRequest, "empty node ground id.")
|
outJSONWithCode(w, http.StatusBadRequest, "empty node ground id.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := models.DeleteGroupById(groupId)
|
_, err := models.DeleteGroupById(groupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ func (n *Node) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errstr := fmt.Sprintf("failed to fetch jobs from etcd after deleted node group[%s]: %s", groupId, err.Error())
|
errstr := fmt.Sprintf("failed to fetch jobs from etcd after deleted node group[%s]: %s", groupId, err.Error())
|
||||||
log.Error(errstr)
|
log.Error(errstr)
|
||||||
outJSONError(w, http.StatusInternalServerError, errstr)
|
outJSONWithCode(w, http.StatusInternalServerError, errstr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ func (n *Node) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
func (n *Node) GetNodes(w http.ResponseWriter, r *http.Request) {
|
func (n *Node) GetNodes(w http.ResponseWriter, r *http.Request) {
|
||||||
nodes, err := models.GetNodes()
|
nodes, err := models.GetNodes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ func InitRouters() (s *http.Server, err error) {
|
||||||
nodeHandler := &Node{}
|
nodeHandler := &Node{}
|
||||||
jobLogHandler := &JobLog{}
|
jobLogHandler := &JobLog{}
|
||||||
infoHandler := &Info{}
|
infoHandler := &Info{}
|
||||||
|
configHandler := NewConfiguration()
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
subrouter := r.PathPrefix("/v1").Subrouter()
|
subrouter := r.PathPrefix("/v1").Subrouter()
|
||||||
|
@ -62,6 +63,9 @@ func InitRouters() (s *http.Server, err error) {
|
||||||
h = BaseHandler{Handle: infoHandler.Overview}
|
h = BaseHandler{Handle: infoHandler.Overview}
|
||||||
subrouter.Handle("/info/overview", h).Methods("GET")
|
subrouter.Handle("/info/overview", h).Methods("GET")
|
||||||
|
|
||||||
|
h = BaseHandler{Handle: configHandler.Configuratios}
|
||||||
|
subrouter.Handle("/configurations", h).Methods("GET")
|
||||||
|
|
||||||
uidir := conf.Config.Web.UIDir
|
uidir := conf.Config.Web.UIDir
|
||||||
if len(uidir) == 0 {
|
if len(uidir) == 0 {
|
||||||
uidir = path.Join("web", "ui", "dist")
|
uidir = path.Join("web", "ui", "dist")
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,16 +4,20 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Cronsun Managerment</title>
|
<title>Cronsun Managerment</title>
|
||||||
<style>
|
<style>
|
||||||
|
.initial.error {
|
||||||
|
margin: 170px 80px;
|
||||||
|
color: red;
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
.loader, .loader:after {border-radius: 50%; width: 10em; height: 10em;}
|
.loader, .loader:after {border-radius: 50%; width: 10em; height: 10em;}
|
||||||
.loader {
|
.loader {
|
||||||
margin: 180px auto;
|
margin: 180px auto;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-indent: -9999em;
|
text-indent: -9999em;
|
||||||
border-top: 1.1em solid rgba(128,0,128, 0.2);
|
border: 1.1em solid rgba(9, 47, 181, 0.2);
|
||||||
border-right: 1.1em solid rgba(128,0,128, 0.2);
|
border-left: 1.1em solid #2185d0;
|
||||||
border-bottom: 1.1em solid rgba(128,0,128, 0.2);
|
|
||||||
border-left: 1.1em solid #800080;
|
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
-ms-transform: translateZ(0);
|
-ms-transform: translateZ(0);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
|
@ -4,16 +4,20 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Cronsun Managerment</title>
|
<title>Cronsun Managerment</title>
|
||||||
<style>
|
<style>
|
||||||
|
.initial.error {
|
||||||
|
margin: 170px 80px;
|
||||||
|
color: red;
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
.loader, .loader:after {border-radius: 50%; width: 10em; height: 10em;}
|
.loader, .loader:after {border-radius: 50%; width: 10em; height: 10em;}
|
||||||
.loader {
|
.loader {
|
||||||
margin: 180px auto;
|
margin: 180px auto;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-indent: -9999em;
|
text-indent: -9999em;
|
||||||
border-top: 1.1em solid rgba(128,0,128, 0.2);
|
border: 1.1em solid rgba(9, 47, 181, 0.2);
|
||||||
border-right: 1.1em solid rgba(128,0,128, 0.2);
|
border-left: 1.1em solid #2185d0;
|
||||||
border-bottom: 1.1em solid rgba(128,0,128, 0.2);
|
|
||||||
border-left: 1.1em solid #800080;
|
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
-ms-transform: translateZ(0);
|
-ms-transform: translateZ(0);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
|
@ -11,11 +11,17 @@
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
|
<Messager/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Messager from './components/Messager.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app'
|
name: 'app',
|
||||||
|
components: {
|
||||||
|
Messager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -95,11 +95,6 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(){
|
mounted(){
|
||||||
// var formatNumber = function(i, len){
|
|
||||||
// var n = i == 0 ? 1 : Math.ceil(Math.log10(i+1));
|
|
||||||
// if (n >= len) return i.toString();
|
|
||||||
// return '0'.repeat(len-n) + i.toString();
|
|
||||||
// }
|
|
||||||
var d = new Date()
|
var d = new Date()
|
||||||
this.today = d.getFullYear().toString() + '-' + formatNumber(d.getMonth()+1, 2) + '-' + d.getDate();
|
this.today = d.getFullYear().toString() + '-' + formatNumber(d.getMonth()+1, 2) + '-' + d.getDate();
|
||||||
|
|
||||||
|
|
|
@ -17,17 +17,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>任务分组</label>
|
<label>任务分组</label>
|
||||||
<Dropdown title="选择分组" v-bind:items="groups" v-bind:selected="job.group" v-on:change="changeGroup"/>
|
<Dropdown title="选择分组" v-bind:items="groups" v-bind:selected="job.group" v-on:change="changeGroup"></Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="twelve wide field">
|
<div class="twelve wide field">
|
||||||
<label>任务脚本</label>
|
<label>任务脚本 {{allowSuffixsTip}}</label>
|
||||||
<input type="text" v-model="job.cmd" placeholder="任务脚本">
|
<input type="text" v-model="job.cmd" placeholder="任务脚本">
|
||||||
</div>
|
</div>
|
||||||
<div class="four wide field">
|
<div class="four wide field">
|
||||||
<label>用户(可选)</label>
|
<label>用户({{$appConfig.security.enable ? '必选' : '可选'}})</label>
|
||||||
<input type="text" v-model="job.user" placeholder="指定执行脚本的用户">
|
<Dropdown v-if="$appConfig.security.enable" title="指定执行用户" v-bind:items="$appConfig.security.allowUsers" v-bind:selected="job.user" v-on:change="changeUser"></Dropdown>
|
||||||
|
<input v-else type="text" v-model="job.user" placeholder="指定执行用户">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -56,6 +57,7 @@ export default {
|
||||||
action: 'CREATE',
|
action: 'CREATE',
|
||||||
groups: [],
|
groups: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
allowSuffixsTip: '',
|
||||||
job: {
|
job: {
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -88,6 +90,10 @@ export default {
|
||||||
this.job.group = val;
|
this.job.group = val;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
changeUser: function(val, text){
|
||||||
|
this.job.user = val;
|
||||||
|
},
|
||||||
|
|
||||||
removeRule: function(index){
|
removeRule: function(index){
|
||||||
this.job.rules.splice(index, 1);
|
this.job.rules.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
@ -102,7 +108,7 @@ export default {
|
||||||
var vm = this;
|
var vm = this;
|
||||||
this.$rest.PUT('job', this.job)
|
this.$rest.PUT('job', this.job)
|
||||||
.onsucceed(exceptCode, ()=>{vm.$router.push('/job')})
|
.onsucceed(exceptCode, ()=>{vm.$router.push('/job')})
|
||||||
.onfailed((resp)=>{console.log(resp)})
|
.onfailed((resp)=>{vm.$bus.$emit('error', resp)})
|
||||||
.onend(()=>{vm.loading=false})
|
.onend(()=>{vm.loading=false})
|
||||||
.do();
|
.do();
|
||||||
},
|
},
|
||||||
|
@ -114,6 +120,12 @@ export default {
|
||||||
|
|
||||||
mounted: function(){
|
mounted: function(){
|
||||||
var vm = this;
|
var vm = this;
|
||||||
|
var secCnf = this.$appConfig.security;
|
||||||
|
if (secCnf.enable) {
|
||||||
|
if (secCnf.allowSuffixs && secCnf.allowSuffixs.length > 0) {
|
||||||
|
this.allowSuffixsTip = '(当前限制只允许添加此类后缀脚本:' + secCnf.allowSuffixs.join(' ') + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$route.path.indexOf('/job/create') === 0) {
|
if (this.$route.path.indexOf('/job/create') === 0) {
|
||||||
this.action = 'CREATE';
|
this.action = 'CREATE';
|
||||||
|
@ -145,13 +157,6 @@ export default {
|
||||||
vm.job.pause = !vm.job.pause;
|
vm.job.pause = !vm.job.pause;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(this.$el).find('.dropdown').dropdown({
|
|
||||||
allowAdditions: true,
|
|
||||||
onChange: function(value, text, $choice){
|
|
||||||
vm.job.group = value;
|
|
||||||
}
|
|
||||||
}).dropdown('set exactly', this.job.group);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<style scope>
|
||||||
|
.show {}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<div class="ui sticky fixed" style="top: 80px; right: 20px; width: 400px;">
|
||||||
|
<div v-for="(m, index) in queue" :key="m.id" class="ui floating message transition animate fly left" :class="[m.type, m.animation, m.visiable]">
|
||||||
|
<i class="close icon" v-on:click="closeMessage(m.id)"></i>
|
||||||
|
<div class="header">{{m.content}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'message',
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
queue: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
showMessage(type, content){
|
||||||
|
var id = Math.random().toString();
|
||||||
|
this.queue.push({
|
||||||
|
id: id,
|
||||||
|
content: content,
|
||||||
|
type: type,
|
||||||
|
animation: 'in',
|
||||||
|
visiable: 'visiable'
|
||||||
|
});
|
||||||
|
|
||||||
|
var vm = this;
|
||||||
|
setTimeout(()=>{
|
||||||
|
vm.closeMessage(id);
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
|
||||||
|
closeMessage(id){
|
||||||
|
var vm = this;
|
||||||
|
for (var i in vm.queue) {
|
||||||
|
if (vm.queue[i].id === id) {
|
||||||
|
vm.queue[i].animation = 'out';
|
||||||
|
setTimeout(()=>{
|
||||||
|
for (var i in vm.queue) {
|
||||||
|
if (vm.queue[i].id === id) {
|
||||||
|
vm.queue.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted(){
|
||||||
|
var vm = this;
|
||||||
|
this.$bus.$on('error', (content)=>{
|
||||||
|
vm.showMessage('error', content);
|
||||||
|
});
|
||||||
|
this.$bus.$on('success', (content)=>{
|
||||||
|
vm.showMessage('success', content);
|
||||||
|
});
|
||||||
|
this.$bus.$on('warning', (content)=>{
|
||||||
|
vm.showMessage('warning', content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -7,8 +7,9 @@ Vue.config.debug = true;
|
||||||
|
|
||||||
// global restful client
|
// global restful client
|
||||||
import Rest from './libraries/rest-client.js';
|
import Rest from './libraries/rest-client.js';
|
||||||
const RestApi =(Vue, options)=>{
|
var restApi = new Rest('/v1/');
|
||||||
Vue.prototype.$rest = new Rest('/v1/');
|
const RestApi = (Vue, options)=>{
|
||||||
|
Vue.prototype.$rest = restApi;
|
||||||
};
|
};
|
||||||
Vue.use(RestApi);
|
Vue.use(RestApi);
|
||||||
|
|
||||||
|
@ -47,8 +48,27 @@ var router = new VueRouter({
|
||||||
routes: routes
|
routes: routes
|
||||||
});
|
});
|
||||||
|
|
||||||
var app = new Vue({
|
|
||||||
el: '#app',
|
restApi.GET('configurations').onsucceed(200, (resp)=>{
|
||||||
render: h => h(App),
|
const Config = (Vue, options)=>{
|
||||||
router: router
|
Vue.prototype.$appConfig = resp;
|
||||||
});
|
}
|
||||||
|
Vue.use(Config);
|
||||||
|
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
render: h => h(App),
|
||||||
|
router: router
|
||||||
|
});
|
||||||
|
}).onfailed((data, xhr)=>{
|
||||||
|
var msg = data ? data : xhr.status+' '+xhr.statusText;
|
||||||
|
showInitialError('Failed to get global configurations('+xhr.responseURL+'): '+msg);
|
||||||
|
}).onexception((msg)=>{
|
||||||
|
showInitialError('Failed to get global configurations('+xhr.responseURL+'): '+msg);
|
||||||
|
}).do();
|
||||||
|
|
||||||
|
function showInitialError(msg) {
|
||||||
|
var d = document.getElementById('app');
|
||||||
|
d.innerHTML = msg;
|
||||||
|
d.className = 'initial error';
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue