diff --git a/etc/init.shy b/etc/init.shy index 67890560..77bddea2 100644 --- a/etc/init.shy +++ b/etc/init.shy @@ -9,4 +9,3 @@ source etc/local.shy alias import nfs alias send send alias dial dial -config debug on diff --git a/src/contexts/ctx.go b/src/contexts/ctx.go index fee70e76..78782834 100644 --- a/src/contexts/ctx.go +++ b/src/contexts/ctx.go @@ -1162,17 +1162,17 @@ func (m *Message) Sort(key string, arg ...string) *Message { // {{{ result = true } case "time": - ti, e := time.ParseInLocation(m.Conf("time_layout"), table[i][key], time.Local) + ti, e := time.ParseInLocation(m.Confx("time_layout"), table[i][key], time.Local) m.Assert(e) - tj, e := time.ParseInLocation(m.Conf("time_layout"), table[j][key], time.Local) + tj, e := time.ParseInLocation(m.Confx("time_layout"), table[j][key], time.Local) m.Assert(e) if tj.Before(ti) { result = true } case "time_r": - ti, e := time.ParseInLocation(m.Conf("time_layout"), table[i][key], time.Local) + ti, e := time.ParseInLocation(m.Confx("time_layout"), table[i][key], time.Local) m.Assert(e) - tj, e := time.ParseInLocation(m.Conf("time_layout"), table[j][key], time.Local) + tj, e := time.ParseInLocation(m.Confx("time_layout"), table[j][key], time.Local) m.Assert(e) if ti.Before(tj) { result = true @@ -1598,13 +1598,13 @@ func (m *Message) Confv(key string, args ...interface{}) interface{} { // {{{ hand = x.Hand } - switch value := x.Value.(type) { + switch x.Value.(type) { case string: - x.Value = fmt.Sprintf("%v", value) + x.Value = fmt.Sprintf("%v", args[0]) case bool: - x.Value = Right(fmt.Sprintf("%v", value)) + x.Value = Right(fmt.Sprintf("%v", args[0])) case int: - i, e := strconv.Atoi(fmt.Sprintf("%v", value)) + i, e := strconv.Atoi(fmt.Sprintf("%v", args[0])) m.Assert(e) x.Value = i case nil: @@ -1940,7 +1940,7 @@ var CGI = template.FuncMap{ } if m, ok := arg[0].(*Message); ok { if len(arg) == 1 { - return fmt.Sprintf("%v", m) + return fmt.Sprintf("%v", m.Format()) } switch which := arg[1].(type) { @@ -2005,32 +2005,27 @@ var CGI = template.FuncMap{ } return "" }, // }}} - "conf": func(arg ...interface{}) string { // {{{ + "conf": func(arg ...interface{}) interface{} { // {{{ if len(arg) == 0 { return "" } if m, ok := arg[0].(*Message); ok { + if len(arg) == 1 { - return fmt.Sprintf("%v", m) + list := []string{} + for k, _ := range m.target.Configs { + list = append(list, k) + } + return list } switch which := arg[1].(type) { case string: if len(arg) == 2 { - return m.Conf(which) - } - - switch value := arg[2].(type) { - case string: - return m.Conf(which, value) - case int: - return fmt.Sprintf("%d", m.Confi(which, value)) - case bool: - return fmt.Sprintf("%t", m.Confs(which, value)) - default: - return m.Conf(which, fmt.Sprintf("%v", arg[2])) + return m.Confv(which) } + return m.Confv(which, arg[2:]...) } } return "" @@ -2051,69 +2046,155 @@ var CGI = template.FuncMap{ return "" }, // }}} - "detail": func(arg ...interface{}) string { // {{{ + "detail": func(arg ...interface{}) interface{} { // {{{ if len(arg) == 0 { return "" } - if m, ok := arg[0].(*Message); ok { + switch m := arg[0].(type) { + case *Message: if len(arg) == 1 { - return strings.Join(m.Meta["detail"], "") + return m.Meta["detail"] } - return m.Detail(arg[1:]...) + + index := 0 + switch value := arg[1].(type) { + case int: + index = value + case string: + i, e := strconv.Atoi(value) + m.Assert(e) + index = i + } + if len(arg) == 2 { + return m.Detail(index) + } + return m.Detail(index, arg[2]) + case map[string][]string: + return strings.Join(m["detail"], "") + case []string: + return strings.Join(m, "") + default: + return fmt.Sprintf("%v", arg[0]) } return "" }, // }}} - "option": func(arg ...interface{}) string { // {{{ + "option": func(arg ...interface{}) interface{} { // {{{ if len(arg) == 0 { return "" } - if m, ok := arg[0].(*Message); ok { + switch m := arg[0].(type) { + case *Message: if len(arg) == 1 { - return fmt.Sprintf("%v", m) + return m.Meta["option"] } - switch which := arg[1].(type) { + + switch value := arg[1].(type) { + case int: + if 0 <= value && value < len(m.Meta["option"]) { + return m.Meta["option"][value] + } case string: if len(arg) == 2 { - return m.Option(which) + return m.Meta[value] } - return m.Option(which, arg[2:]...) + switch val := arg[2].(type) { + case int: + if 0 <= val && val < len(m.Meta[value]) { + return m.Meta[value][val] + } + } } + case map[string][]string: + if len(arg) == 1 { + return strings.Join(m["option"], "") + } + switch value := arg[1].(type) { + case string: + return strings.Join(m[value], "") + } + case []string: + return strings.Join(m, "") + default: + return fmt.Sprintf("%v", arg[0]) } return "" }, // }}} - "result": func(arg ...interface{}) string { // {{{ + "result": func(arg ...interface{}) interface{} { // {{{ if len(arg) == 0 { return "" } - if m, ok := arg[0].(*Message); ok { + switch m := arg[0].(type) { + case *Message: if len(arg) == 1 { - return strings.Join(m.Meta["result"], "") + return m.Meta["result"] } - return m.Result(arg[1:]...) + + index := 0 + switch value := arg[1].(type) { + case int: + index = value + case string: + i, e := strconv.Atoi(value) + m.Assert(e) + index = i + } + if len(arg) == 2 { + return m.Result(index) + } + return m.Result(index, arg[2]) + case map[string][]string: + return strings.Join(m["result"], "") + case []string: + return strings.Join(m, "") + default: + return fmt.Sprintf("%v", arg[0]) } return "" }, // }}} - "append": func(arg ...interface{}) string { // {{{ + "append": func(arg ...interface{}) interface{} { // {{{ if len(arg) == 0 { return "" } - if m, ok := arg[0].(*Message); ok { + switch m := arg[0].(type) { + case *Message: if len(arg) == 1 { - return fmt.Sprintf("%v", m) + return m.Meta["append"] } - switch which := arg[1].(type) { + + switch value := arg[1].(type) { + case int: + if 0 <= value && value < len(m.Meta["append"]) { + return m.Meta["append"][value] + } case string: if len(arg) == 2 { - return m.Append(which) + return m.Meta[value] } - return m.Append(which, arg[2:]...) + switch val := arg[2].(type) { + case int: + if 0 <= val && val < len(m.Meta[value]) { + return m.Meta[value][val] + } + } } + case map[string][]string: + if len(arg) == 1 { + return strings.Join(m["append"], "") + } + switch value := arg[1].(type) { + case string: + return strings.Join(m[value], "") + } + case []string: + return strings.Join(m, "") + default: + return fmt.Sprintf("%v", arg[0]) } return "" }, // }}} diff --git a/src/contexts/nfs/nfs.go b/src/contexts/nfs/nfs.go index 42f5b291..ace94a3e 100644 --- a/src/contexts/nfs/nfs.go +++ b/src/contexts/nfs/nfs.go @@ -66,11 +66,26 @@ func (nfs *NFS) open(name string) (*os.File, error) { // {{{ } // }}} -func dir(m *ctx.Message, name string, level int) { // {{{ +func dir(m *ctx.Message, name string, level int, deep bool, fields []string) { // {{{ back, e := os.Getwd() m.Assert(e) os.Chdir(name) defer os.Chdir(back) + s, e := os.Stat(".") + for _, k := range fields { + switch k { + case "filename": + m.Add("append", "filename", "..") + case "dir": + m.Add("append", "dir", "true") + case "size": + m.Add("append", "size", 0) + case "line": + m.Add("append", "line", 0) + case "time": + m.Add("append", "time", s.ModTime().Format("2006-01-02 15:04:05")) + } + } if fs, e := ioutil.ReadDir("."); m.Assert(e) { for _, f := range fs { @@ -119,15 +134,23 @@ func dir(m *ctx.Message, name string, level int) { // {{{ if !(m.Confx("dir_type") == "file" && f.IsDir() || m.Confx("dir_type") == "dir" && !f.IsDir()) { - m.Add("append", "filename", filename) - m.Add("append", "dir", f.IsDir()) - m.Add("append", "size", f.Size()) - m.Add("append", "line", line) - m.Add("append", "time", f.ModTime().Format("2006-01-02 15:04:05")) + for _, k := range fields { + switch k { + case "filename": + m.Add("append", "filename", filename) + case "dir": + m.Add("append", "dir", f.IsDir()) + case "size": + m.Add("append", "size", f.Size()) + case "line": + m.Add("append", "line", line) + case "time": + m.Add("append", "time", f.ModTime().Format("2006-01-02 15:04:05")) + } + } } - - if f.IsDir() { - dir(m, f.Name(), level+1) + if f.IsDir() && deep { + dir(m, f.Name(), level+1, deep, fields) } } } @@ -815,7 +838,9 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", "dir_name": &ctx.Config{Name: "dir_name(name/tree/path/full)", Value: "name", Help: "dir命令输出文件名的类型, name: 文件名, tree: 带缩进的文件名, path: 相对路径, full: 绝对路径"}, "dir_info": &ctx.Config{Name: "dir_info(sizes/lines/files/dirs)", Value: "sizes lines files dirs", Help: "dir命令输出目录的统计信息, info: 输出统计信息, 否则输出"}, + "dir_deep": &ctx.Config{Name: "dir_deep(yes/no)", Value: "yes", Help: "dir命令输出目录的统计信息, info: 输出统计信息, 否则输出"}, "dir_type": &ctx.Config{Name: "dir_type(file/dir)", Value: "file", Help: "dir命令输出的文件类型, file: 只输出普通文件, dir: 只输出目录文件, 否则输出所有文件"}, + "dir_field": &ctx.Config{Name: "dir_field", Value: "filename line size time", Help: "表格排序字段"}, "sort_field": &ctx.Config{Name: "sort_field", Value: "line", Help: "表格排序字段"}, "sort_order": &ctx.Config{Name: "sort_order(int/int_r/string/string_r/time/time_r)", Value: "int", Help: "表格排序类型"}, @@ -1093,7 +1118,11 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", // }}} }}, "pwd": &ctx.Command{Name: "pwd", Help: "查看当前路径", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - if len(arg) > 0 { // {{{ + if m.Options("dir") { // {{{ + m.Echo(m.Option("dir")) + return + } + if len(arg) > 0 { os.Chdir(arg[0]) } wd, e := os.Getwd() @@ -1101,14 +1130,19 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", m.Echo(wd) // }}} }}, "dir": &ctx.Command{ - Name: "dir dir [dir_info info] [dir_name name|tree|path|full] [dir_type file|dir] [sort_field name] [sort_order type]", + Name: "dir dir [dir_deep yes|no] [dir_info info] [dir_name name|tree|path|full] [dir_type file|dir] [sort_field name] [sort_order type]", Help: "查看目录, dir: 目录名, dir_info: 显示统计信息, dir_name: 文件名类型, dir_type: 文件类型, sort_field: 排序字段, sort_order: 排序类型", - Form: map[string]int{"dir_info": 1, "dir_name": 1, "dir_type": 1, "sort_field": 1, "sort_order": 1}, + Form: map[string]int{"dir_field": 1, "dir_deep": 1, "dir_info": 1, "dir_name": 1, "dir_type": 1, "sort_field": 1, "sort_order": 1}, Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { - d := "." // {{{ + d := "./" + m.Option("dir") // {{{ if len(arg) > 0 { d = arg[0] } + if s, e := os.Stat(d); m.Assert(e) && !s.IsDir() { + d = path.Dir(d) + } + fields := strings.Split(m.Confx("dir_field"), " ") + trip := 0 if m.Confx("dir_name") == "path" { wd, e := os.Getwd() @@ -1121,7 +1155,8 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", m.Option(v, 0) } - dir(m, d, 0) + m.Option("time_layout", "2006-01-02 15:04:05") + dir(m, d, 0, ctx.Right(m.Confx("dir_deep")), fields) m.Sort(m.Confx("sort_field"), m.Confx("sort_order")) m.Table(func(maps map[string]string, list []string, line int) bool { for i, v := range list { @@ -1145,9 +1180,9 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", // }}} }}, "git": &ctx.Command{ - Name: "git branch|status|diff|log|info arg... [git_path path]...", - Help: "版本控制, branch: 分支管理, status: 查看状态, info: 查看分支与状态, git_path: 指定路径", - Form: map[string]int{"git_path": 1, "git_info": 1, "git_log": 1, "git_log_form": 1}, + Name: "git branch|status|diff|log|info arg... [dir path]...", + Help: "版本控制, branch: 分支管理, status: 查看状态, info: 查看分支与状态, dir: 指定路径", + Form: map[string]int{"dir": 1, "git_info": 1, "git_log": 1, "git_log_form": 1}, Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { if len(arg) == 0 { // {{{ arg = []string{"info"} @@ -1166,10 +1201,10 @@ var Index = &ctx.Context{Name: "nfs", Help: "存储中心", } wd, e := os.Getwd() m.Assert(e) - if !m.Has("git_path") { - m.Option("git_path", m.Conf("git_path")) + if !m.Has("dir") { + m.Option("dir", m.Confx("dir")) } - for _, p := range m.Meta["git_path"] { + for _, p := range m.Meta["dir"] { if !path.IsAbs(p) { p = path.Join(wd, p) } diff --git a/src/contexts/web/web.go b/src/contexts/web/web.go index 53d6397a..bd71f664 100644 --- a/src/contexts/web/web.go +++ b/src/contexts/web/web.go @@ -15,6 +15,7 @@ import ( // {{{ "path" "bytes" + "mime" "mime/multipart" "path/filepath" @@ -111,6 +112,7 @@ func (web *WEB) Trans(m *ctx.Message, key string, hand func(*ctx.Message, *ctx.C http.Redirect(w, r, msg.Append("redirect"), http.StatusFound) return } + if msg.Has("template") { msg.Spawn().Cmd("/render", msg.Meta["template"]) return @@ -145,6 +147,12 @@ func (web *WEB) ServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{ } } + r.Form.Add("path", r.URL.Path) + if strings.HasPrefix(r.URL.Path, "/index") { + r.Form.Add("dir", strings.TrimPrefix(r.URL.Path, "/index")) + r.URL.Path = "/index" + } + web.ServeMux.ServeHTTP(w, r) if web.Message != nil && web.Confs("logheaders") { @@ -296,6 +304,37 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心", "argument": []interface{}{"tmux", "show-buffer"}, "template": "result", "title": "buffer", }, + map[string]interface{}{ + "module": "nfs", "command": "git", + "argument": []interface{}{}, + "template": "result", "title": "git", + }, + map[string]interface{}{ + "module": "nfs", "command": "dir", + "argument": []interface{}{"dir_type", "all", "dir_deep", "false", "dir_field", "time size line filename", "sort_field", "time", "sort_order", "time_r"}, + "template": "append", "title": "", + }, + map[string]interface{}{ + "template": "upload", "title": "upload", + }, + map[string]interface{}{ + "template": "create", "title": "create", + }, + map[string]interface{}{ + "module": "nfs", "detail": []interface{}{"pwd"}, + "template": "detail", "title": "detail", + }, + }, + "xujianing": []interface{}{ + map[string]interface{}{ + "module": "nfs", "detail": []interface{}{"pwd"}, + "template": "detail", "title": "detail", + }, + map[string]interface{}{ + "module": "nfs", "command": "dir", + "argument": []interface{}{"dir_type", "all", "dir_deep", "false", "dir_field", "time size line filename", "sort_field", "time", "sort_order", "time_r"}, + "template": "append", "title": "", + }, }, }, Help: "资源列表"}, }, @@ -564,9 +603,48 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心", // }}} }}, "/index": &ctx.Command{Name: "/index", Help: "网页门户", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { + r := m.Optionv("request").(*http.Request) w := m.Optionv("response").(http.ResponseWriter) - w.Header().Add("Content-Type", "text/html") + login := m.Spawn().Cmd("/login") + if login.Has("template") { + m.Copy(login, "append") + return + } + + aaa := login.Appendv("aaa").(*ctx.Message) + list := m.Confv("index", aaa.Cap("username")) + if list == nil { + m.Echo("no right, please contact manager") + m.Append("template", "result") + return + } + if m.Options("details") { + if !ctx.Right(m.Find(m.Option("module")).Cmd("right", aaa.Cap("username"), "check", "command", m.Option("details")).Result(0)) { + m.Echo("no right, please contact manager") + m.Append("template", "result") + return + } + msg := m.Find(m.Option("module")).Cmd(m.Option("details")) + m.Copy(msg, "result").Copy(msg, "append") + return + } + + dir := path.Join(m.Cap("directory"), m.Option("dir")) + if s, e := os.Stat(dir); e == nil && m.Option("dir") != "" && !s.IsDir() { + w.Header().Set("Content-type", mime.TypeByExtension(dir)) + http.ServeFile(w, r, dir) + return + } + + if !ctx.Right(m.Spawn(c).Cmd("right", aaa.Cap("username"), "check", "command", "/index", "dir", dir).Result(0)) { + m.Echo("no right, please contact manager") + m.Append("template", "result") + return + } + + m.Option("dir", dir) + w.Header().Add("Content-Type", "text/html") tpl := template.New("render").Funcs(ctx.CGI) tpl = template.Must(tpl.ParseGlob(path.Join(m.Conf("template_dir"), m.Conf("common_tmpl")))) tpl = template.Must(tpl.ParseGlob(path.Join(m.Conf("template_dir"), m.Conf("upload_tmpl")))) @@ -577,19 +655,37 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心", []byte{27, 91, 109}, []byte(""), } - list := m.Confv("index", "shy") for _, v := range list.([]interface{}) { val := v.(map[string]interface{}) - msg := m.Find(val["module"].(string)).Cmd(val["command"], val["argument"]) - for i, v := range msg.Meta["result"] { - b := []byte(v) - for i := 0; i < len(replace)-1; i += 2 { - b = bytes.Replace(b, replace[i], replace[i+1], -1) - } - msg.Meta["result"][i] = string(b) + if _, ok := val["detail"]; ok { + detail := val["detail"].([]interface{}) + msg := m.Spawn().Add("detail", detail[0].(string), detail[1:]) + msg.Option("title", val["title"]) + msg.Option("module", val["module"]) + m.Assert(tpl.ExecuteTemplate(w, val["template"].(string), msg)) + continue + } + if _, ok := val["module"]; ok { + if _, ok := val["command"]; ok { + msg := m.Find(val["module"].(string)).Cmd(val["command"], val["argument"]) + for i, v := range msg.Meta["result"] { + b := []byte(v) + for i := 0; i < len(replace)-1; i += 2 { + b = bytes.Replace(b, replace[i], replace[i+1], -1) + } + msg.Meta["result"][i] = string(b) + } + if msg.Option("title", val["title"]) == "" { + msg.Option("title", m.Option("dir")) + } + m.Assert(tpl.ExecuteTemplate(w, val["template"].(string), msg)) + continue + } + } + + if _, ok := val["template"]; ok { + m.Assert(tpl.ExecuteTemplate(w, val["template"].(string), m)) } - msg.Option("title", val["title"]) - m.Assert(tpl.ExecuteTemplate(w, val["template"].(string), msg.Meta)) } }}, "/travel": &ctx.Command{Name: "/travel", Help: "文件上传", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { @@ -710,13 +806,9 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心", // 共享列表 share := m.Sess("share", m.Target()) index := share.Target().Index - m.Log("fuck", "%v", share.Target().Index) - m.Log("fuck", "%v", aaa.Format()) if index != nil && index[aaa.Cap("username")] != nil { for k, v := range index[aaa.Cap("username")].Index { - m.Log("fuck", "%v", v.Commands) for _, j := range v.Commands { - m.Log("fuck", "%v", j.Shares) for _, n := range j.Shares { for _, nn := range n { if match, e := regexp.MatchString(nn, m.Option("dir")); m.Assert(e) && match { @@ -925,19 +1017,15 @@ var Index = &ctx.Context{Name: "web", Help: "应用中心", }}, "/render": &ctx.Command{Name: "/render [main [tmpl]]", Help: "模板响应, main: 模板入口, tmpl: 附加模板", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { w := m.Optionv("response").(http.ResponseWriter) // {{{ + w.Header().Add("Content-Type", "text/html") - tpl := template.Must(template.New("render").Funcs(ctx.CGI).ParseGlob(path.Join(m.Conf("template_dir"), m.Conf("common_tmpl")))) + tpl := template.New("render").Funcs(ctx.CGI) + tpl = template.Must(tpl.ParseGlob(path.Join(m.Conf("template_dir"), m.Conf("common_tmpl")))) if len(arg) > 1 { tpl = template.Must(tpl.ParseGlob(path.Join(m.Conf("template_dir"), arg[1]))) } - main := m.Conf("common_main") - if len(arg) > 0 { - main = arg[0] - } - - w.Header().Add("Content-Type", "text/html") - m.Assert(tpl.ExecuteTemplate(w, main, m.Message())) + m.Assert(tpl.ExecuteTemplate(w, m.Confx("common_main", arg, 0), m.Message())) // }}} }}, "/json": &ctx.Command{Name: "/json", Help: "json响应", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) { diff --git a/usr/template/common/base.html b/usr/template/common/base.html index de292706..52f37a8a 100644 --- a/usr/template/common/base.html +++ b/usr/template/common/base.html @@ -22,14 +22,95 @@ {{end}} -{{define "message"}} -
message -
{{meta .message}}
+{{define "notice"}} +
notice +
{{result .Meta}}
+{{end}} + +{{define "detail"}} +
{{append . "title"}} + {{detail .Meta}} +
+ +
+ +{{end}} + +{{define "result"}} +
{{append . "title"}} +
{{result .Meta|unscaped}}
+
+{{end}} + +{{define "append_link"}} +{{.}} + +{{end}} + +{{define "append"}} +
{{append . "title"}} + + {{$msg := .}} + {{$ncol := append . |len}} + {{$nrow := append . 0|append .|len}} + {{range append .}}{{end}} + {{range $row, $val := append . 0|append .}} + + {{range append $msg}} + {{$value := append $msg . $row}} + {{if eq . "filename"}} + {{template "append_link" $value}} + {{else}} + + {{end}} + {{end}} + + {{end}} +
{{.}}
{{$value}}
+
+ {{end}} diff --git a/usr/template/common/login.html b/usr/template/common/login.html index 2d9e5ed0..a8a570da 100644 --- a/usr/template/common/login.html +++ b/usr/template/common/login.html @@ -84,8 +84,8 @@ {{$meta := .Meta}} {{template "head" $meta}} -{{if meta $meta.message}} - {{template "message" $meta}} +{{if meta $meta.notice}} + {{template "notice" $meta}} {{end}} {{template "login" $meta}} {{template "tail" $meta}} diff --git a/usr/template/upload.html b/usr/template/upload.html index 63a5b734..0fdb68fe 100644 --- a/usr/template/upload.html +++ b/usr/template/upload.html @@ -112,12 +112,6 @@ {{end}} {{end}} -{{define "result"}} -
{{meta . "title"}} -
{{meta . "result"|unscaped}}
-
-{{end}} - {{define "upload"}}
upload