mirror of https://github.com/shunfei/cronsun
新建/编辑任务添加安全选项检查,添加消息组件
parent
a2c31890ef
commit
fe3dfd4429
|
@ -3,4 +3,6 @@ conf/files/*.json
|
|||
.tags_sorted_by_file
|
||||
bin/*/*server
|
||||
.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.")
|
||||
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
|
||||
}
|
||||
|
||||
if !job.Valid() {
|
||||
log.Warnf("job[%s] is invalid", string(j.Key))
|
||||
if err := job.Valid(); err != nil {
|
||||
log.Warnf("job[%s] is invalid: %s", string(j.Key), err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -142,9 +142,7 @@ func GetJobFromKv(kv *mvccpb.KeyValue) (job *Job, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
if !job.Valid() {
|
||||
err = InvalidJobErr
|
||||
}
|
||||
err = job.Valid()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -271,7 +269,7 @@ func (j *Job) Check() error {
|
|||
return ErrEmptyJobCommand
|
||||
}
|
||||
|
||||
return nil
|
||||
return j.Valid()
|
||||
}
|
||||
|
||||
// 执行结果写入 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 {
|
||||
j.splitCmd()
|
||||
}
|
||||
|
||||
security := conf.Config.Security
|
||||
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 {
|
||||
|
@ -339,7 +345,6 @@ func (j *Job) validCmd() bool {
|
|||
if len(conf.Config.Security.Ext) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, ext := range conf.Config.Security.Ext {
|
||||
if strings.HasSuffix(j.cmd[0], ext) {
|
||||
return true
|
||||
|
|
|
@ -27,7 +27,7 @@ func (b BaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
buf.Write(debug.Stack())
|
||||
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)
|
||||
return
|
||||
|
@ -53,10 +53,3 @@ func outJSONWithCode(w http.ResponseWriter, httpCode int, data interface{}) {
|
|||
func outJSON(w http.ResponseWriter, data interface{}) {
|
||||
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 {
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
outJSONError(w, statusCode, err.Error())
|
||||
outJSONWithCode(w, statusCode, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ func (j *Job) DeleteJob(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
_, err := models.DeleteJob(vars["group"], vars["id"])
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ func (j *Job) ChangeJobStatus(w http.ResponseWriter, r *http.Request) {
|
|||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&job)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
|
@ -57,20 +57,20 @@ func (j *Job) ChangeJobStatus(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
originJob, rev, err := models.GetJobAndRev(vars["group"], vars["id"])
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
originJob.Pause = job.Pause
|
||||
b, err := json.Marshal(originJob)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = models.DefalutClient.PutWithModRev(originJob.Key(), string(b), rev)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -86,13 +86,13 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
|
|||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&job)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
|
||||
if err = job.Check(); err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -110,13 +110,13 @@ func (j *Job) UpdateJob(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
b, err := json.Marshal(job)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = models.DefalutClient.Put(job.Key(), string(b))
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
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) {
|
||||
resp, err := models.DefalutClient.Get(conf.Config.Cmd, clientv3.WithPrefix(), clientv3.WithKeysOnly())
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
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))
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ func (j *Job) GetList(w http.ResponseWriter, r *http.Request) {
|
|||
job := models.Job{}
|
||||
err = json.Unmarshal(resp.Kvs[i].Value, &job)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
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)
|
||||
var err error
|
||||
if err = de.Decode(&g); err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
@ -36,14 +36,14 @@ func (n *Node) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if err = g.Check(); err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// @TODO modRev
|
||||
var modRev int64 = 0
|
||||
if _, err = g.Put(modRev); err != nil {
|
||||
outJSONError(w, http.StatusBadRequest, err.Error())
|
||||
outJSONWithCode(w, http.StatusBadRequest, err.Error())
|
||||
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) {
|
||||
resp, err := models.DefalutClient.Get(conf.Config.Group, v3.WithPrefix(), v3.WithSort(v3.SortByKey, v3.SortAscend))
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ func (n *Node) GetGroups(w http.ResponseWriter, r *http.Request) {
|
|||
err = json.Unmarshal(resp.Kvs[i].Value, &g)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
list = append(list, &g)
|
||||
|
@ -76,7 +76,7 @@ func (n *Node) GetGroupByGroupId(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
g, err := models.GetGroupById(vars["id"])
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -91,13 +91,13 @@ func (n *Node) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
groupId := strings.TrimSpace(vars["id"])
|
||||
if len(groupId) == 0 {
|
||||
outJSONError(w, http.StatusBadRequest, "empty node ground id.")
|
||||
outJSONWithCode(w, http.StatusBadRequest, "empty node ground id.")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := models.DeleteGroupById(groupId)
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (n *Node) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
errstr := fmt.Sprintf("failed to fetch jobs from etcd after deleted node group[%s]: %s", groupId, err.Error())
|
||||
log.Error(errstr)
|
||||
outJSONError(w, http.StatusInternalServerError, errstr)
|
||||
outJSONWithCode(w, http.StatusInternalServerError, errstr)
|
||||
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) {
|
||||
nodes, err := models.GetNodes()
|
||||
if err != nil {
|
||||
outJSONError(w, http.StatusInternalServerError, err.Error())
|
||||
outJSONWithCode(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ func InitRouters() (s *http.Server, err error) {
|
|||
nodeHandler := &Node{}
|
||||
jobLogHandler := &JobLog{}
|
||||
infoHandler := &Info{}
|
||||
configHandler := NewConfiguration()
|
||||
|
||||
r := mux.NewRouter()
|
||||
subrouter := r.PathPrefix("/v1").Subrouter()
|
||||
|
@ -62,6 +63,9 @@ func InitRouters() (s *http.Server, err error) {
|
|||
h = BaseHandler{Handle: infoHandler.Overview}
|
||||
subrouter.Handle("/info/overview", h).Methods("GET")
|
||||
|
||||
h = BaseHandler{Handle: configHandler.Configuratios}
|
||||
subrouter.Handle("/configurations", h).Methods("GET")
|
||||
|
||||
uidir := conf.Config.Web.UIDir
|
||||
if len(uidir) == 0 {
|
||||
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">
|
||||
<title>Cronsun Managerment</title>
|
||||
<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 {
|
||||
margin: 180px auto;
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
border-top: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-right: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-bottom: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-left: 1.1em solid #800080;
|
||||
border: 1.1em solid rgba(9, 47, 181, 0.2);
|
||||
border-left: 1.1em solid #2185d0;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
|
|
|
@ -4,16 +4,20 @@
|
|||
<meta charset="utf-8">
|
||||
<title>Cronsun Managerment</title>
|
||||
<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 {
|
||||
margin: 180px auto;
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
border-top: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-right: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-bottom: 1.1em solid rgba(128,0,128, 0.2);
|
||||
border-left: 1.1em solid #800080;
|
||||
border: 1.1em solid rgba(9, 47, 181, 0.2);
|
||||
border-left: 1.1em solid #2185d0;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
|
|
|
@ -11,11 +11,17 @@
|
|||
<div class="ui container">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
<Messager/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Messager from './components/Messager.vue';
|
||||
|
||||
export default {
|
||||
name: 'app'
|
||||
name: 'app',
|
||||
components: {
|
||||
Messager
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -95,11 +95,6 @@ export default {
|
|||
},
|
||||
|
||||
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()
|
||||
this.today = d.getFullYear().toString() + '-' + formatNumber(d.getMonth()+1, 2) + '-' + d.getDate();
|
||||
|
||||
|
|
|
@ -17,17 +17,18 @@
|
|||
</div>
|
||||
<div class="field">
|
||||
<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 class="fields">
|
||||
<div class="twelve wide field">
|
||||
<label>任务脚本</label>
|
||||
<label>任务脚本 {{allowSuffixsTip}}</label>
|
||||
<input type="text" v-model="job.cmd" placeholder="任务脚本">
|
||||
</div>
|
||||
<div class="four wide field">
|
||||
<label>用户(可选)</label>
|
||||
<input type="text" v-model="job.user" placeholder="指定执行脚本的用户">
|
||||
<label>用户({{$appConfig.security.enable ? '必选' : '可选'}})</label>
|
||||
<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 class="field">
|
||||
|
@ -56,6 +57,7 @@ export default {
|
|||
action: 'CREATE',
|
||||
groups: [],
|
||||
loading: false,
|
||||
allowSuffixsTip: '',
|
||||
job: {
|
||||
id: '',
|
||||
name: '',
|
||||
|
@ -88,6 +90,10 @@ export default {
|
|||
this.job.group = val;
|
||||
},
|
||||
|
||||
changeUser: function(val, text){
|
||||
this.job.user = val;
|
||||
},
|
||||
|
||||
removeRule: function(index){
|
||||
this.job.rules.splice(index, 1);
|
||||
},
|
||||
|
@ -102,7 +108,7 @@ export default {
|
|||
var vm = this;
|
||||
this.$rest.PUT('job', this.job)
|
||||
.onsucceed(exceptCode, ()=>{vm.$router.push('/job')})
|
||||
.onfailed((resp)=>{console.log(resp)})
|
||||
.onfailed((resp)=>{vm.$bus.$emit('error', resp)})
|
||||
.onend(()=>{vm.loading=false})
|
||||
.do();
|
||||
},
|
||||
|
@ -114,6 +120,12 @@ export default {
|
|||
|
||||
mounted: function(){
|
||||
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) {
|
||||
this.action = 'CREATE';
|
||||
|
@ -145,13 +157,6 @@ export default {
|
|||
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: {
|
||||
|
|
|
@ -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
|
||||
import Rest from './libraries/rest-client.js';
|
||||
const RestApi =(Vue, options)=>{
|
||||
Vue.prototype.$rest = new Rest('/v1/');
|
||||
var restApi = new Rest('/v1/');
|
||||
const RestApi = (Vue, options)=>{
|
||||
Vue.prototype.$rest = restApi;
|
||||
};
|
||||
Vue.use(RestApi);
|
||||
|
||||
|
@ -47,8 +48,27 @@ var router = new VueRouter({
|
|||
routes: routes
|
||||
});
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
render: h => h(App),
|
||||
router: router
|
||||
});
|
||||
|
||||
restApi.GET('configurations').onsucceed(200, (resp)=>{
|
||||
const Config = (Vue, options)=>{
|
||||
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