From ab6bfe93d48a1d46a221b861f808af4d00e0980a Mon Sep 17 00:00:00 2001 From: shaoying Date: Mon, 16 Dec 2019 01:38:35 +0800 Subject: [PATCH] add web.wiki --- Makefile | 2 - core.go => base.go | 32 +++-- base/aaa/aaa.go | 96 +++++++++++++ {core => base}/cli/cli.go | 1 + base/ctx/ctx.go | 85 ++++++++++++ base/gdb/gdb.go | 97 ++++++++++++++ {core => base}/lex/lex.go | 0 {core => base}/log/log.go | 0 {core => base}/mdb/mdb.go | 0 base/nfs/nfs.go | 140 +++++++++++++++++++ base/shy.go | 11 ++ {core => base}/ssh/ssh.go | 0 {core => base}/tcp/tcp.go | 0 {core => base}/web/web.go | 134 +++++++++++++++--- {core => base}/yac/yac.go | 0 core/aaa/aaa.go | 17 --- core/chat/chat.go | 166 +++++++++++++++++++++++ core/ctx/ctx.go | 17 --- core/gdb/gdb.go | 17 --- core/nfs/nfs.go | 17 --- core/wiki/chart.go | 276 ++++++++++++++++++++++++++++++++++++++ core/wiki/wiki.go | 92 +++++++++++++ demo/Makefile | 2 +- demo/build.sh | 53 ++++++++ demo/go.sum | 11 +- demo/main.go | 3 +- demo/miss.md | 17 +++ go.mod | 1 + go.sum | 3 + misc/chat/chat.go | 65 --------- misc/shy.go | 16 --- type.go | 224 ++++++++++++++++++++++++++++--- 32 files changed, 1390 insertions(+), 205 deletions(-) delete mode 100644 Makefile rename core.go => base.go (76%) create mode 100644 base/aaa/aaa.go rename {core => base}/cli/cli.go (96%) create mode 100644 base/ctx/ctx.go create mode 100644 base/gdb/gdb.go rename {core => base}/lex/lex.go (100%) rename {core => base}/log/log.go (100%) rename {core => base}/mdb/mdb.go (100%) create mode 100644 base/nfs/nfs.go create mode 100644 base/shy.go rename {core => base}/ssh/ssh.go (100%) rename {core => base}/tcp/tcp.go (100%) rename {core => base}/web/web.go (67%) rename {core => base}/yac/yac.go (100%) delete mode 100644 core/aaa/aaa.go create mode 100644 core/chat/chat.go delete mode 100644 core/ctx/ctx.go delete mode 100644 core/gdb/gdb.go delete mode 100644 core/nfs/nfs.go create mode 100644 core/wiki/chart.go create mode 100644 core/wiki/wiki.go create mode 100755 demo/build.sh create mode 100644 demo/miss.md delete mode 100644 misc/chat/chat.go delete mode 100644 misc/shy.go diff --git a/Makefile b/Makefile deleted file mode 100644 index e1351bba..00000000 --- a/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - cd demo && go build main.go diff --git a/core.go b/base.go similarity index 76% rename from core.go rename to base.go index a7a337ae..ffba6765 100644 --- a/core.go +++ b/base.go @@ -1,6 +1,7 @@ package ice import ( + "github.com/shylinux/toolkits" "os" "time" ) @@ -34,7 +35,10 @@ func (f *Frame) Start(m *Message, arg ...string) bool { // 启动服务 Index.begin.Cmd(arg) - + return true +} +func (f *Frame) Close(m *Message, arg ...string) bool { + // 保存配置 m.Travel(func(p *Context, s *Context) { if cmd, ok := s.Commands["_exit"]; ok { msg := m.Spawns(s) @@ -42,10 +46,7 @@ func (f *Frame) Start(m *Message, arg ...string) bool { cmd.Hand(msg, s, "_exit", arg...) } }) - // 保存配置 - return true -} -func (f *Frame) Close(m *Message, arg ...string) bool { + list := map[*Context]*Message{m.target: m} m.Travel(func(p *Context, s *Context) { if msg, ok := list[p]; ok && msg != nil { @@ -58,14 +59,22 @@ func (f *Frame) Close(m *Message, arg ...string) bool { } var Index = &Context{Name: "ice", Help: "冰山模块", - Caches: map[string]*Cache{}, - Configs: map[string]*Config{}, + Caches: map[string]*Cache{}, + Configs: map[string]*Config{ + "cache": {Name: "数据缓存", Value: map[string]interface{}{ + "store": "var/data", + "limit": "30", + "least": "10", + }}, + }, Commands: map[string]*Command{ "_init": {Name: "_init", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) }}, - "hi": {Name: "hi", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) + "exit": {Name: "exit", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { + c.Close(m.Spawn(c), arg...) + os.Exit(kit.Int(kit.Select("0", arg, 0))) + }}, + "_exit": {Name: "_init", Help: "hello", Hand: func(m *Message, c *Context, cmd string, arg ...string) { }}, }, } @@ -89,6 +98,9 @@ func Run(arg ...string) string { if len(arg) == 0 { arg = os.Args[1:] } + if len(arg) == 0 { + arg = append(arg, os.Getenv("ice_serve")) + } if Index.Begin(Pulse.Spawns(), arg...).Start(Index.begin.Spawns(), arg...) { Index.Close(Index.start.Spawns(), arg...) diff --git a/base/aaa/aaa.go b/base/aaa/aaa.go new file mode 100644 index 00000000..4ba5b964 --- /dev/null +++ b/base/aaa/aaa.go @@ -0,0 +1,96 @@ +package aaa + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" +) + +var Index = &ice.Context{Name: "aaa", Help: "认证模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "role": {Name: "role", Help: "角色", Value: map[string]interface{}{ + "meta": map[string]interface{}{}, + "hash": map[string]interface{}{ + "root": map[string]interface{}{}, + "tech": map[string]interface{}{}, + "void": map[string]interface{}{}, + }, + "list": map[string]interface{}{}, + }}, + "user": {Name: "user", Help: "用户", Value: map[string]interface{}{ + "meta": map[string]interface{}{}, + "hash": map[string]interface{}{}, + "list": map[string]interface{}{}, + }}, + "sess": {Name: "sess", Help: "会话", Value: map[string]interface{}{ + "meta": map[string]interface{}{"expire": "720h"}, + "hash": map[string]interface{}{}, + "list": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "_init": {Name: "_init", Help: "加载", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "_exit": {Name: "_exit", Help: "加载", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "role": {Name: "role", Help: "角色", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + }}, + "user": {Name: "user", Help: "用户", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "login": + user := m.Confm("user", "hash."+arg[1]) + if user == nil { + m.Confv("user", "hash."+arg[1], map[string]interface{}{ + "create_time": m.Time(), + "password": arg[2], + "userrole": kit.Select("void", "root", arg[1] == m.Conf("cli.runtime", "boot.username")), + }) + m.Log("info", "create user %s %s", arg[1], m.Conf("user", "hash."+arg[1])) + } else if kit.Format(user["password"]) != arg[2] { + m.Log("warn", "login fail %s", arg[1]) + // 登录失败 + break + } + + sessid := kit.Format(user["sessid"]) + if sessid == "" { + sessid = m.Cmdx("aaa.sess", "login", arg[1]) + } + + m.Grow("user", nil, map[string]interface{}{ + "create_time": m.Time(), + "remote_ip": m.Option("remote_ip"), + "username": arg[1], + "sessid": sessid, + }) + // 登录成功 + m.Echo(sessid) + } + }}, + "sess": {Name: "sess check|login", Help: "会话", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "check": + + user := m.Conf("sess", "hash."+arg[1]+".username") + if user != "" { + m.Confm("user", "hash."+user, func(value map[string]interface{}) { + m.Push("username", user) + m.Push("userrole", value["userrole"]) + }) + } + + m.Echo(user) + case "login": + sessid := kit.Hashs("uniq") + m.Conf("sess", "hash."+sessid, map[string]interface{}{ + "create_time": m.Time(), + "username": arg[1], + }) + m.Log("info", "create sess %s %s", arg[1], sessid) + m.Echo(sessid) + } + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/core/cli/cli.go b/base/cli/cli.go similarity index 96% rename from core/cli/cli.go rename to base/cli/cli.go index 147822e6..090a1287 100644 --- a/core/cli/cli.go +++ b/base/cli/cli.go @@ -39,6 +39,7 @@ var Index = &ice.Context{Name: "cli", Help: "命令模块", } m.Conf("runtime", "node.type", "worker") + m.Conf("runtime", "node.name", m.Conf("runtime", "boot.pathname")) m.Log("info", "runtime %v", kit.Formats(m.Confv("runtime"))) }}, "runtime": {Name: "runtime", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { diff --git a/base/ctx/ctx.go b/base/ctx/ctx.go new file mode 100644 index 00000000..9a54bef2 --- /dev/null +++ b/base/ctx/ctx.go @@ -0,0 +1,85 @@ +package ctx + +import ( + "encoding/json" + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "os" +) + +var Index = &ice.Context{Name: "ctx", Help: "元始模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "command": {Name: "command", Help: "命令", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + ice.Pulse.Travel(func(p *ice.Context, s *ice.Context) { + for k, v := range s.Commands { + if k[0] == '/' || k[0] == '_' { + continue + } + if p != nil && p != ice.Index { + m.Push("key", p.Name+"."+s.Name) + } else { + m.Push("key", s.Name) + } + m.Push("index", k) + m.Push("name", v.Name) + m.Push("help", v.Help) + } + }) + return + } + + m.Search(arg[0]+"."+arg[1], func(p *ice.Context, s *ice.Context, key string) { + if i, ok := s.Commands[key]; ok { + if len(arg) == 2 { + m.Push("name", i.Name) + m.Push("help", i.Help) + m.Push("meta", kit.Format(i.Meta)) + m.Push("list", kit.Format(i.List)) + } else { + switch arg[2] { + case "run": + m.Log("info", "run %s %s %v", s.Name, key, arg[3:]) + msg := m.Spawn(s) + i.Hand(msg, s, key, arg[3:]...) + m.Copy(msg) + } + } + } + }) + }}, + "config": {Name: "config", Help: "配置", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "save": + if f, p, e := kit.Create(arg[1]); m.Assert(e) { + data := map[string]interface{}{} + for _, k := range arg[2:] { + data[k] = m.Confv(k) + } + if s, e := json.MarshalIndent(data, "", " "); m.Assert(e) { + if n, e := f.Write(s); m.Assert(e) { + m.Log("info", "save %d %s", n, p) + } + } + m.Echo(p) + } + case "load": + if f, e := os.Open(arg[1]); e == nil { + data := map[string]interface{}{} + json.NewDecoder(f).Decode(&data) + + for k, v := range data { + m.Search(k, func(p *ice.Context, s *ice.Context, key string) { + m.Log("info", "load %s.%s %v", s.Name, key, kit.Format(v)) + s.Configs[key].Value = v + }) + } + } + } + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/base/gdb/gdb.go b/base/gdb/gdb.go new file mode 100644 index 00000000..eb164d16 --- /dev/null +++ b/base/gdb/gdb.go @@ -0,0 +1,97 @@ +package gdb + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "os" + "os/signal" + "syscall" + "time" +) + +type Frame struct { + signal chan os.Signal +} + +func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server { + return &Frame{} +} +func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server { + if f, p, e := kit.Create(m.Conf("logpid")); m.Assert(e) { + defer f.Close() + f.WriteString(kit.Format(os.Getpid())) + m.Log("info", "pid %d %s", os.Getpid(), p) + } + + f.signal = make(chan os.Signal, 10) + m.Confm("signal", nil, func(sig string, action string) { + m.Log("signal", "add %s: %s", sig, action) + signal.Notify(f.signal, syscall.Signal(kit.Int(sig))) + }) + + m.Gos(m, func(m *ice.Message) { + if s, ok := <-f.signal; ok { + m.Log("info", "signal %v", s) + m.Cmd(m.Confv("signal", kit.Format(s))) + } + }) + m.Gos(m, func(m *ice.Message) { + for { + time.Sleep(100 * time.Millisecond) + now := int(time.Now().Unix()) + m.Confm("timer", "hash", func(key string, value map[string]interface{}) { + if kit.Int(value["next"]) <= now { + m.Log("info", "timer %v %v", key, value["next"]) + m.Cmd(value["cmd"]) + value["next"] = now + int(kit.Duration(value["interval"]))/int(time.Second) + } + }) + } + }) + return f +} +func (f *Frame) Start(m *ice.Message, arg ...string) bool { + return true +} +func (f *Frame) Close(m *ice.Message, arg ...string) bool { + return true +} + +var Index = &ice.Context{Name: "gdb", Help: "调试模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "logpid": &ice.Config{Name: "logpid", Value: "var/run/shy.pid", Help: ""}, + "signal": &ice.Config{Name: "signal", Value: map[string]interface{}{ + "2": []interface{}{"exit"}, "3": "QUIT", "15": "TERM", + "28": "WINCH", + "30": "restart", + "31": "upgrade", + }, Help: "信号"}, + "timer": {Name: "定时器", Value: map[string]interface{}{ + "meta": map[string]interface{}{}, + "hash": map[string]interface{}{}, + "list": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "timer": {Name: "timer", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "start": + h := kit.ShortKey(m.Confm("timer", "hash"), 6) + + next := time.Now().Add(kit.Duration(arg[1])).Unix() + m.Conf("timer", "hash."+h, map[string]interface{}{ + "interval": arg[1], + "next": next, + "cmd": arg[2:], + }) + m.Echo(h) + + case "stop": + m.Conf("timer", "hash."+arg[1], "") + } + }}, + }, +} + +func init() { ice.Index.Register(Index, &Frame{}) } diff --git a/core/lex/lex.go b/base/lex/lex.go similarity index 100% rename from core/lex/lex.go rename to base/lex/lex.go diff --git a/core/log/log.go b/base/log/log.go similarity index 100% rename from core/log/log.go rename to base/log/log.go diff --git a/core/mdb/mdb.go b/base/mdb/mdb.go similarity index 100% rename from core/mdb/mdb.go rename to base/mdb/mdb.go diff --git a/base/nfs/nfs.go b/base/nfs/nfs.go new file mode 100644 index 00000000..732a26c0 --- /dev/null +++ b/base/nfs/nfs.go @@ -0,0 +1,140 @@ +package nfs + +import ( + "bufio" + "crypto/sha1" + "encoding/hex" + "fmt" + "github.com/shylinux/icebergs" + "io/ioutil" + "os" + "path" + "regexp" + "sort" + "strings" +) + +func dir(m *ice.Message, root string, name string, level int, deep bool, dir_type string, dir_reg *regexp.Regexp, fields []string, format string) { + + if fs, e := ioutil.ReadDir(name); m.Assert(e) { + for _, f := range fs { + if f.Name() == "." || f.Name() == ".." { + continue + } + if strings.HasPrefix(f.Name(), ".") && dir_type != "all" { + continue + } + + p := path.Join(name, f.Name()) + if f, e = os.Lstat(p); e != nil { + m.Log("info", "%s", e) + continue + } else if (f.Mode()&os.ModeSymlink) != 0 && f.IsDir() { + continue + } + + if !(dir_type == "file" && f.IsDir() || dir_type == "dir" && !f.IsDir()) && (dir_reg == nil || dir_reg.MatchString(f.Name())) { + for _, field := range fields { + switch field { + case "time": + m.Push("time", f.ModTime().Format(format)) + case "type": + if m.Assert(e) && f.IsDir() { + m.Push("type", "dir") + } else { + m.Push("type", "file") + } + case "full": + if f.IsDir() { + m.Push("full", path.Join(root, name, f.Name())+"/") + } else { + m.Push("full", path.Join(root, name, f.Name())) + } + case "path": + if f.IsDir() { + m.Push("path", path.Join(name, f.Name())+"/") + } else { + m.Push("path", path.Join(name, f.Name())) + } + case "file": + if f.IsDir() { + m.Push("file", f.Name()+"/") + } else { + m.Push("file", f.Name()) + } + case "name": + m.Push("name", f.Name()) + case "tree": + if level == 0 { + m.Push("tree", f.Name()) + } else { + m.Push("tree", strings.Repeat("| ", level-1)+"|-"+f.Name()) + } + case "size": + m.Push("size", f.Size()) + case "line": + if f.IsDir() { + if d, e := ioutil.ReadDir(p); m.Assert(e) { + count := 0 + for _, f := range d { + if strings.HasPrefix(f.Name(), ".") { + continue + } + count++ + } + m.Push("line", count) + } + } else { + nline := 0 + if f, e := os.Open(p); m.Assert(e) { + defer f.Close() + for bio := bufio.NewScanner(f); bio.Scan(); nline++ { + bio.Text() + } + } + m.Push("line", nline) + } + case "hash", "hashs": + var h [20]byte + if f.IsDir() { + if d, e := ioutil.ReadDir(p); m.Assert(e) { + meta := []string{} + for _, v := range d { + meta = append(meta, fmt.Sprintf("%s%d%s", v.Name(), v.Size(), v.ModTime())) + } + sort.Strings(meta) + h = sha1.Sum([]byte(strings.Join(meta, ""))) + } + } else { + if f, e := ioutil.ReadFile(path.Join(name, f.Name())); m.Assert(e) { + h = sha1.Sum(f) + } + } + if field == "hash" { + m.Push("hash", hex.EncodeToString(h[:])) + } else { + m.Push("hash", hex.EncodeToString(h[:4])) + } + } + } + } + if f.IsDir() && deep { + dir(m, root, p, level+1, deep, dir_type, dir_reg, fields, format) + } + } + } +} + +var Index = &ice.Context{Name: "nfs", Help: "文件模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "dir": {Name: "dir", Help: "目录", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + rg, _ := regexp.Compile(m.Option("dir_reg")) + dir(m, arg[0], arg[1], 0, false, "both", rg, + strings.Split("time size line path", " "), "2006-01-02 15:04:05") + }}, + }, +} + +func init() { ice.Index.Register(Index, nil) } diff --git a/base/shy.go b/base/shy.go new file mode 100644 index 00000000..f86e3603 --- /dev/null +++ b/base/shy.go @@ -0,0 +1,11 @@ +package shy + +import ( + _ "github.com/shylinux/icebergs/base/aaa" + _ "github.com/shylinux/icebergs/base/cli" + _ "github.com/shylinux/icebergs/base/ctx" + _ "github.com/shylinux/icebergs/base/gdb" + _ "github.com/shylinux/icebergs/base/log" + _ "github.com/shylinux/icebergs/base/nfs" + _ "github.com/shylinux/icebergs/base/web" +) diff --git a/core/ssh/ssh.go b/base/ssh/ssh.go similarity index 100% rename from core/ssh/ssh.go rename to base/ssh/ssh.go diff --git a/core/tcp/tcp.go b/base/tcp/tcp.go similarity index 100% rename from core/tcp/tcp.go rename to base/tcp/tcp.go diff --git a/core/web/web.go b/base/web/web.go similarity index 67% rename from core/web/web.go rename to base/web/web.go index bb92bc55..528f3eb6 100644 --- a/core/web/web.go +++ b/base/web/web.go @@ -1,15 +1,20 @@ package web import ( - "encoding/json" "github.com/gorilla/websocket" "github.com/shylinux/icebergs" "github.com/shylinux/toolkits" + + "bytes" + "encoding/json" + "math/rand" "net" "net/http" "net/url" "path" "strings" + "text/template" + "time" ) const ( @@ -24,6 +29,22 @@ type WEB struct { send map[string]*ice.Message } +func Cookie(msg *ice.Message, sessid string) string { + w := msg.Optionv("response").(http.ResponseWriter) + expire := time.Now().Add(kit.Duration(msg.Conf("aaa.sess", "meta.expire"))) + msg.Log("cookie", "expire %v sessid %s", kit.Format(expire), sessid) + http.SetCookie(w, &http.Cookie{Name: "sessid", Value: sessid, Path: "/", Expires: expire}) + return sessid +} + +func (web *WEB) Login(msg *ice.Message, w http.ResponseWriter, r *http.Request) bool { + if msg.Options("sessid") { + sub := msg.Cmd("aaa.sess", "check", msg.Option("sessid")) + msg.Log("info", "user %s %s", msg.Option("userrole", sub.Append("userrole")), + msg.Option("username", sub.Append("username"))) + } + return true +} func (web *WEB) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn) { for { if t, b, e := c.ReadMessage(); e != nil { @@ -61,7 +82,11 @@ func (web *WEB) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn) { } else { msg.Log("space", "run") // 本地执行 - msg = msg.Cmd() + if safe { + msg = msg.Cmd() + } else { + msg.Echo("no right") + } msg.Optionv("_handle", "true") kit.Revert(source) source, target = []string{source[0]}, source[1:] @@ -76,11 +101,53 @@ func (web *WEB) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn) { } } } +func (web *WEB) HandleCGI(m *ice.Message, which string) *template.Template { + cgi := template.FuncMap{ + "result": func(msg *ice.Message) string { + return msg.Result() + }, + } + tmpl := template.New("render") + for k, v := range m.Target().Commands { + m.Log("info", "%v, %v", k, v.Name) + if strings.HasPrefix(k, "/") || strings.HasPrefix(k, "_") { + continue + } + + func(k string, v *ice.Command) { + cgi[k] = func(arg ...interface{}) (res interface{}) { + m.TryCatch(m.Spawn(), true, func(msg *ice.Message) { + msg.Option("render", "table") + v.Hand(msg, m.Target(), k, kit.Simple(arg)...) + + buffer := bytes.NewBuffer([]byte{}) + m.Assert(tmpl.ExecuteTemplate(buffer, msg.Option("render"), msg)) + res = string(buffer.Bytes()) + }) + return + } + }(k, v) + } + tmpl = tmpl.Funcs(cgi) + tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf("serve", "template.path"), "/*.tmpl"))) + tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf("serve", "template.path"), m.Target().Name, "/*.tmpl"))) + tmpl = template.Must(tmpl.ParseFiles(which)) + m.Confm("serve", "template.list", func(index int, value string) { tmpl = template.Must(tmpl.Parse(value)) }) + for i, v := range tmpl.Templates() { + m.Log("info", "%v, %v", i, v.Name()) + } + return tmpl +} func (web *WEB) HandleCmd(m *ice.Message, key string, cmd *ice.Command) { web.HandleFunc(key, func(w http.ResponseWriter, r *http.Request) { m.TryCatch(m.Spawns(), true, func(msg *ice.Message) { + defer func() { + msg.Log("cost", msg.Format("cost")) + }() + msg.Optionv("request", r) msg.Optionv("response", w) + msg.Option("remote_ip", r.Header.Get("remote_ip")) msg.Option("agent", r.Header.Get("User-Agent")) msg.Option("referer", r.Header.Get("Referer")) msg.Option("accept", r.Header.Get("Accept")) @@ -129,14 +196,12 @@ func (web *WEB) HandleCmd(m *ice.Message, key string, cmd *ice.Command) { } } - msg.Log("cmd", "%s %s", msg.Target().Name, key) - cmd.Hand(msg, msg.Target(), msg.Option("path")) - msg.Set("option") - if msg.Optionv("append") == nil { - msg.Result() + if web.Login(msg, w, r) { + msg.Log("cmd", "%s %s", msg.Target().Name, key) + cmd.Hand(msg, msg.Target(), msg.Option("path"), kit.Simple(msg.Optionv("cmds"))...) + msg.Set("option") + w.Write([]byte(msg.Formats("meta"))) } - w.Write([]byte(msg.Formats("meta"))) - msg.Log("cost", msg.Format("cost")) }) }) } @@ -176,16 +241,23 @@ func (web *WEB) Start(m *ice.Message, arg ...string) bool { if w.ServeMux != nil { return } - - msg := m.Spawns(s) w.ServeMux = http.NewServeMux() + msg := m.Spawns(s) + // 级联路由 route := "/" + s.Name + "/" if n, ok := p.Server().(*WEB); ok && n.ServeMux != nil { msg.Log("route", "%s <- %s", p.Name, route) n.Handle(route, http.StripPrefix(path.Dir(route), w)) } + // 静态路由 + m.Confm("web.serve", "static", func(key string, value string) { + msg.Log("route", "%s <- %s <- %s", s.Name, key, value) + w.Handle(key, http.StripPrefix(key, http.FileServer(http.Dir(value)))) + }) + + // 命令路由 for k, x := range s.Commands { if k[0] == '/' { msg.Log("route", "%s <- %s", s.Name, k) @@ -212,9 +284,23 @@ var Index = &ice.Context{Name: "web", Help: "网页模块", "spide": {Name: "客户端", Value: map[string]interface{}{ "self": map[string]interface{}{"port": ":9020"}, }}, - "serve": {Name: "服务端", Value: map[string]interface{}{}}, + "serve": {Name: "服务端", Value: map[string]interface{}{ + "static": map[string]interface{}{"/": "usr/volcanos/", + "/static/volcanos/": "usr/volcanos/", + }, + "template": map[string]interface{}{ + "path": "usr/template", + "list": []interface{}{ + `{{define "raw"}}{{.|result}}{{end}}`, + `{{define "title"}}{{.|result}}{{end}}`, + `{{define "chapter"}}{{.|result}}{{end}}`, + `{{define "section"}}{{.|result}}{{end}}`, + `{{define "block"}}
{{.|result}}
{{end}}`, + }, + }, + }}, "space": {Name: "空间端", Value: map[string]interface{}{ - "meta": map[string]interface{}{"buffer": 4096}, + "meta": map[string]interface{}{"buffer": 4096, "redial": 3000}, "hash": map[string]interface{}{}, "list": map[string]interface{}{}, }}, @@ -259,16 +345,22 @@ var Index = &ice.Context{Name: "web", Help: "网页模块", host := kit.Select(m.Conf("web.spide", "self.port"), arg, 1) p := "ws://" + host + kit.Select("/space", arg, 2) + "?node=" + node + "&name=" + name - if s, e := net.Dial("tcp", host); m.Assert(e) { - if u, e := url.Parse(p); m.Assert(e) { - if s, _, e := websocket.NewClient(s, u, nil, m.Confi("web.space", "meta.buffer"), m.Confi("web.space", "meta.buffer")); m.Assert(e) { + if u, e := url.Parse(p); m.Assert(e) { + m.TryCatch(m, true, func(m *ice.Message) { + for { + if s, e := net.Dial("tcp", host); e == nil { + if s, _, e := websocket.NewClient(s, u, nil, m.Confi("web.space", "meta.buffer"), m.Confi("web.space", "meta.buffer")); e == nil { + id := m.Option("_source", []string{kit.Format(c.ID()), "some"}) + web.send[id] = m + s.WriteMessage(MSG_MAPS, []byte(m.Format("meta"))) - id := m.Option("_source", []string{kit.Format(c.ID()), "some"}) - web.send[id] = m - s.WriteMessage(MSG_MAPS, []byte(m.Format("meta"))) - web.HandleWSS(m, true, s) + web.HandleWSS(m, true, s) + } + } + time.Sleep(time.Duration(rand.Intn(m.Confi("web.space", "meta.redial"))) * time.Millisecond) + m.Log("info", "reconnect %v", host) } - } + }) } default: if arg[0] == "" { diff --git a/core/yac/yac.go b/base/yac/yac.go similarity index 100% rename from core/yac/yac.go rename to base/yac/yac.go diff --git a/core/aaa/aaa.go b/core/aaa/aaa.go deleted file mode 100644 index 3035b444..00000000 --- a/core/aaa/aaa.go +++ /dev/null @@ -1,17 +0,0 @@ -package aaa - -import ( - "github.com/shylinux/icebergs" -) - -var Index = &ice.Context{Name: "aaa", Help: "认证模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{}, - Commands: map[string]*ice.Command{ - "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) - }}, - }, -} - -func init() { ice.Index.Register(Index, nil) } diff --git a/core/chat/chat.go b/core/chat/chat.go new file mode 100644 index 00000000..b353baa3 --- /dev/null +++ b/core/chat/chat.go @@ -0,0 +1,166 @@ +package chat + +import ( + "github.com/shylinux/icebergs" + _ "github.com/shylinux/icebergs/base" + "github.com/shylinux/icebergs/base/web" + "github.com/shylinux/toolkits" +) + +var Index = &ice.Context{Name: "chat", Help: "聊天模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{ + "group": {Name: "group", Value: map[string]interface{}{ + "meta": map[string]interface{}{}, + "list": map[string]interface{}{}, + "hash": map[string]interface{}{}, + }}, + }, + Commands: map[string]*ice.Command{ + "_init": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Cmd("ctx.config", "load", "var/conf/cli.json") + m.Cmd("ctx.config", "load", "var/conf/aaa.json") + m.Cmd("ctx.config", "load", "var/conf/chat.json") + }}, + "_exit": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Cmd("ctx.config", "save", "var/conf/chat.json", "web.chat.group") + m.Cmd("ctx.config", "save", "var/conf/aaa.json", "aaa.role", "aaa.user", "aaa.sess") + m.Cmd("ctx.config", "save", "var/conf/cli.json", "cli.runtime") + }}, + "/login": {Name: "/login", Help: "登录", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "check": + m.Push("username", m.Option("username")) + m.Push("userrole", m.Option("userrole")) + m.Echo(m.Option("username")) + case "login": + m.Echo(web.Cookie(m, m.Cmdx("aaa.user", "login", arg[1], arg[2]))) + } + }}, + "/favor": {Name: "/favor", Help: "登录", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Cmdy("cli.system", arg) + }}, + "/ocean": {Name: "/ocean", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + m.Confm("aaa.user", "hash", func(key string, value map[string]interface{}) { + m.Push("key", key) + m.Push("user.route", m.Conf("cli.runtime", "node.name")) + }) + return + } + switch arg[0] { + case "spawn": + if arg[1] == "" { + arg[1] = kit.ShortKey(m.Confm("group", "hash"), 6) + } + user := map[string]interface{}{ + "meta": map[string]interface{}{}, + "hash": map[string]interface{}{}, + "list": []interface{}{}, + } + tool := map[string]interface{}{ + "meta": map[string]interface{}{}, + "hash": map[string]interface{}{}, + "list": []interface{}{}, + } + for _, v := range arg[3:] { + kit.Value(user, "hash."+v, map[string]interface{}{ + "create_time": m.Time(), + }) + kit.Value(user, "list."+v+".-2", map[string]interface{}{ + "create_time": m.Time(), + "operation": "add", + "username": v, + }) + } + + m.Conf("group", "hash."+arg[1], map[string]interface{}{ + "meta": map[string]interface{}{ + "create_time": m.Time(), + "name": arg[2], + }, + "user": user, + "tool": tool, + }) + + m.Log("info", "river create %v %v", arg[1], kit.Formats(m.Confv("group", "hash."+arg[1]))) + m.Echo(arg[1]) + } + }}, + "/river": {Name: "/river", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 0 { + m.Confm("group", "hash", func(key string, value map[string]interface{}) { + m.Push("key", key) + m.Push("name", kit.Value(value["meta"], "name")) + }) + return + } + }}, + "/action": {Name: "/action", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) == 2 { + m.Confm("group", "hash."+arg[0]+".tool.hash."+arg[1]+".list", func(index int, value map[string]interface{}) { + m.Push("river", arg[0]) + m.Push("storm", arg[1]) + m.Push("action", index) + + m.Push("node", value["pod"]) + m.Push("group", value["ctx"]) + m.Push("index", value["cmd"]) + + msg := m.Cmd("web.space", value["pod"], "ctx.command", value["ctx"], value["cmd"]) + m.Push("name", value["cmd"]) + m.Push("help", msg.Append("help")) + m.Push("inputs", msg.Append("list")) + m.Push("feature", msg.Append("meta")) + }) + return + } + + m.Confm("group", "hash."+arg[0]+".tool.hash."+arg[1]+".list."+arg[2], func(value map[string]interface{}) { + m.Cmdy("web.space", value["pod"], "ctx.command", value["ctx"], value["cmd"], "run", arg[3:]) + }) + }}, + "/storm": {Name: "/storm", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) < 2 { + m.Confm("group", "hash."+arg[0]+".tool.hash", func(key string, value map[string]interface{}) { + m.Push("key", key).Push("count", len(value["list"].([]interface{}))) + }) + return + } + }}, + "/steam": {Name: "/steam", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) < 2 { + m.Confm("web.space", "hash", func(key string, value map[string]interface{}) { + m.Push("user", m.Conf("cli.runtime", "boot.username")) + m.Push("node", value["name"]) + }) + return + } + switch arg[1] { + case "spawn": + list := []interface{}{} + for i := 3; i < len(arg)-3; i += 4 { + list = append(list, map[string]interface{}{ + "pod": arg[i], + "ctx": arg[i+1], + "cmd": arg[i+2], + "key": arg[i+3], + }) + } + m.Conf("group", "hash."+arg[0]+".tool.hash."+arg[2], map[string]interface{}{ + "meta": map[string]interface{}{ + "create_time": m.Time(), + }, + "hash": map[string]interface{}{}, + "list": list, + }) + + m.Log("info", "steam spawn %v %v %v", arg[0], arg[2], list) + default: + m.Cmdy("web.space", arg[2], "ctx.command") + } + }}, + }, +} + +func init() { web.Index.Register(Index, &web.WEB{}) } diff --git a/core/ctx/ctx.go b/core/ctx/ctx.go deleted file mode 100644 index 3a7aa51e..00000000 --- a/core/ctx/ctx.go +++ /dev/null @@ -1,17 +0,0 @@ -package ctx - -import ( - "github.com/shylinux/icebergs" -) - -var Index = &ice.Context{Name: "ctx", Help: "元始模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{}, - Commands: map[string]*ice.Command{ - "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) - }}, - }, -} - -func init() { ice.Index.Register(Index, nil) } diff --git a/core/gdb/gdb.go b/core/gdb/gdb.go deleted file mode 100644 index 91bcc34b..00000000 --- a/core/gdb/gdb.go +++ /dev/null @@ -1,17 +0,0 @@ -package gdb - -import ( - "github.com/shylinux/icebergs" -) - -var Index = &ice.Context{Name: "gdb", Help: "调试模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{}, - Commands: map[string]*ice.Command{ - "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) - }}, - }, -} - -func init() { ice.Index.Register(Index, nil) } diff --git a/core/nfs/nfs.go b/core/nfs/nfs.go deleted file mode 100644 index 3b0e9159..00000000 --- a/core/nfs/nfs.go +++ /dev/null @@ -1,17 +0,0 @@ -package nfs - -import ( - "github.com/shylinux/icebergs" -) - -var Index = &ice.Context{Name: "nfs", Help: "文件模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{}, - Commands: map[string]*ice.Command{ - "hi": {Name: "hi", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Echo("hello %s world", c.Name) - }}, - }, -} - -func init() { ice.Index.Register(Index, nil) } diff --git a/core/wiki/chart.go b/core/wiki/chart.go new file mode 100644 index 00000000..0954474f --- /dev/null +++ b/core/wiki/chart.go @@ -0,0 +1,276 @@ +package wiki + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "strings" +) + +// 图形接口 +type Chart interface { + Init(*ice.Message, ...string) Chart + Draw(*ice.Message, int, int) Chart + + GetWidth(...string) int + GetHeight(...string) int +} + +// 图形基类 +type Block struct { + Text string + FontColor string + FontFamily string + BackGround string + + FontSize int + LineSize int + Padding int + Margin int + + Width, Height int + + TextData string + RectData string +} + +func (b *Block) Init(m *ice.Message, arg ...string) Chart { + b.Text = kit.Select(b.Text, arg, 0) + b.FontColor = kit.Select("white", kit.Select(b.FontColor, arg, 1)) + b.BackGround = kit.Select("red", kit.Select(b.BackGround, arg, 2)) + b.FontSize = kit.Int(kit.Select("24", kit.Select(kit.Format(b.FontSize), arg, 3))) + b.LineSize = kit.Int(kit.Select("12", kit.Select(kit.Format(b.LineSize), arg, 4))) + return b +} +func (b *Block) Draw(m *ice.Message, x, y int) Chart { + m.Echo(``, + x+b.Margin/2, y+b.Margin/2, b.GetWidth(), b.GetHeight(), b.BackGround, b.RectData) + m.Echo("\n") + m.Echo(`%v`, + x+b.GetWidths()/2, y+b.GetHeights()/2, b.FontSize, b.FontColor, b.TextData, b.Text) + m.Echo("\n") + return b +} +func (b *Block) Data(root interface{}) { + kit.Table(kit.Value(root, "data"), 0, 100, func(key string, value string) { + b.TextData += key + "='" + value + "' " + }) + kit.Table(kit.Value(root, "rect"), 0, 100, func(key string, value string) { + b.RectData += key + "='" + value + "' " + }) + b.FontColor = kit.Select(b.FontColor, kit.Value(root, "fg")) + b.BackGround = kit.Select(b.BackGround, kit.Value(root, "bg")) +} +func (b *Block) GetWidth(str ...string) int { + if b.Width != 0 { + return b.Width + } + return len(kit.Select(b.Text, str, 0))*b.FontSize*6/10 + b.Padding +} +func (b *Block) GetHeight(str ...string) int { + if b.Height != 0 { + return b.Height + } + return b.FontSize*b.LineSize/10 + b.Padding +} +func (b *Block) GetWidths(str ...string) int { + return b.GetWidth(str...) + b.Margin +} +func (b *Block) GetHeights(str ...string) int { + return b.GetHeight() + b.Margin +} + +// 树 +type Chain struct { + data map[string]interface{} + max map[int]int + Block +} + +func (b *Chain) Init(m *ice.Message, arg ...string) Chart { + // 解数据 + b.data = kit.Parse(nil, "", b.show(m, arg[0])...).(map[string]interface{}) + b.FontColor = kit.Select("white", arg, 1) + b.BackGround = kit.Select("red", arg, 2) + b.FontSize = kit.Int(kit.Select("24", arg, 3)) + b.LineSize = kit.Int(kit.Select("12", arg, 4)) + b.Padding = kit.Int(kit.Select("8", arg, 5)) + b.Margin = kit.Int(kit.Select("8", arg, 6)) + m.Log("info", "data %v", kit.Formats(b.data)) + + // 计算尺寸 + b.max = map[int]int{} + b.Height = b.size(m, b.data, 0, b.max) * b.GetHeights() + width := 0 + for _, v := range b.max { + width += b.GetWidths(strings.Repeat(" ", v)) + } + b.Width = width + m.Log("info", "data %v", kit.Formats(b.data)) + return b +} +func (b *Chain) Draw(m *ice.Message, x, y int) Chart { + return b.draw(m, b.data, 0, b.max, x, y, &Block{}) +} +func (b *Chain) show(m *ice.Message, str string) (res []string) { + miss := []int{} + list := kit.Split(str, "\n") + for _, line := range list { + // 计算缩进 + dep := 0 + loop: + for _, v := range []rune(line) { + switch v { + case ' ': + dep++ + case '\t': + dep += 4 + default: + break loop + } + } + + // 计算层次 + if len(miss) > 0 { + if miss[len(miss)-1] > dep { + for i := len(miss) - 1; i >= 0; i-- { + if miss[i] < dep { + break + } + res = append(res, "]", "}") + miss = miss[:i] + } + miss = append(miss, dep) + } else if miss[len(miss)-1] < dep { + miss = append(miss, dep) + } else { + res = append(res, "]", "}") + } + } else { + miss = append(miss, dep) + } + + // 输出节点 + word := kit.Split(line) + res = append(res, "{", "meta", "{", "text") + res = append(res, word...) + res = append(res, "}", "list", "[") + } + return +} +func (b *Chain) size(m *ice.Message, root map[string]interface{}, depth int, width map[int]int) int { + meta := root["meta"].(map[string]interface{}) + + // 最大宽度 + text := kit.Format(meta["text"]) + if len(text) > width[depth] { + width[depth] = len(text) + } + + // 计算高度 + height := 0 + if list, ok := root["list"].([]interface{}); ok && len(list) > 0 { + kit.Fetch(root["list"], func(index int, value map[string]interface{}) { + height += b.size(m, value, depth+1, width) + }) + } else { + height = 1 + } + + meta["height"] = height + return height +} +func (b *Chain) draw(m *ice.Message, root map[string]interface{}, depth int, width map[int]int, x, y int, p *Block) Chart { + meta := root["meta"].(map[string]interface{}) + b.Width, b.Height = 0, 0 + + // 当前节点 + block := &Block{ + BackGround: kit.Select(b.BackGround, kit.Select(p.BackGround, meta["bg"])), + FontColor: kit.Select(b.FontColor, kit.Select(p.FontColor, meta["fg"])), + FontSize: b.FontSize, + LineSize: b.LineSize, + Padding: b.Padding, + Margin: b.Margin, + Width: b.GetWidth(strings.Repeat(" ", width[depth])), + } + + block.Data(root["meta"]) + block.Init(m, kit.Format(meta["text"])).Draw(m, x, y+(kit.Int(meta["height"])-1)*b.GetHeights()/2) + + // 递归节点 + kit.Fetch(root["list"], func(index int, value map[string]interface{}) { + b.draw(m, value, depth+1, width, x+b.GetWidths(strings.Repeat(" ", width[depth])), y, block) + y += kit.Int(kit.Value(value, "meta.height")) * b.GetHeights() + }) + return b +} + +// 表 +type Table struct { + data [][]string + max map[int]int + Block +} + +func (b *Table) Init(m *ice.Message, arg ...string) Chart { + // 解析数据 + b.max = map[int]int{} + for _, v := range kit.Split(arg[0], "\n") { + l := kit.Split(v) + for i, v := range l { + switch data := kit.Parse(nil, "", kit.Split(v)...).(type) { + case map[string]interface{}: + v = kit.Select("", data["text"]) + } + if len(v) > b.max[i] { + b.max[i] = len(v) + } + } + b.data = append(b.data, l) + } + b.FontColor = kit.Select("white", arg, 1) + b.BackGround = kit.Select("red", arg, 2) + b.FontSize = kit.Int(kit.Select("24", arg, 3)) + b.LineSize = kit.Int(kit.Select("12", arg, 4)) + b.Padding = kit.Int(kit.Select("8", arg, 5)) + b.Margin = kit.Int(kit.Select("8", arg, 6)) + + // 计算尺寸 + width := 0 + for _, v := range b.max { + width += b.GetWidths(strings.Repeat(" ", v)) + } + b.Width = width + b.Height = len(b.data) * b.GetHeights() + + m.Log("info", "data %v", kit.Formats(b.data)) + return b +} +func (b *Table) Draw(m *ice.Message, x, y int) Chart { + b.Width, b.Height = 0, 0 + for n, line := range b.data { + for i, text := range line { + l := 0 + for j := 0; j < i; j++ { + l += b.GetWidths(strings.Repeat(" ", b.max[i])) + } + block := &Block{ + BackGround: kit.Select(b.BackGround), + FontColor: kit.Select(b.FontColor), + FontSize: b.FontSize, + LineSize: b.LineSize, + Padding: b.Padding, + Margin: b.Margin, + Width: b.GetWidth(strings.Repeat(" ", b.max[i])), + } + + switch data := kit.Parse(nil, "", kit.Split(text)...).(type) { + case map[string]interface{}: + text = kit.Select(text, data["text"]) + block.Data(data) + } + block.Init(m, text).Draw(m, x+l, y+n*b.GetHeights()) + } + } + return b +} diff --git a/core/wiki/wiki.go b/core/wiki/wiki.go new file mode 100644 index 00000000..72ff4355 --- /dev/null +++ b/core/wiki/wiki.go @@ -0,0 +1,92 @@ +package wiki + +import ( + "bytes" + "github.com/gomarkdown/markdown" + "github.com/shylinux/icebergs" + _ "github.com/shylinux/icebergs/base" + "github.com/shylinux/icebergs/base/web" + "github.com/shylinux/toolkits" + "path" + "strings" +) + +var Index = &ice.Context{Name: "wiki", Help: "文档模块", + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, + Commands: map[string]*ice.Command{ + "chart": {Name: "chart", Help: "绘图", List: []interface{}{ + map[string]interface{}{"type": "select", "value": "chain", "values": "chain table"}, + map[string]interface{}{"type": "text", "value": ""}, + map[string]interface{}{"type": "button", "value": "执行"}, + }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + m.Option("render", "raw") + var chart Chart + switch arg[0] { + case "block": + chart = &Block{} + case "chain": + chart = &Chain{} + case "table": + chart = &Table{} + } + arg[1] = strings.TrimSpace(arg[1]) + + chart.Init(m, arg[1:]...) + m.Echo(``, + chart.GetWidth(), chart.GetHeight(), m.Option("style")) + m.Echo("\n") + chart.Draw(m, 0, 0) + m.Echo(``) + m.Echo("\n") + return + }}, + + "_tree": {Name: "_tree", Help: "目录", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + m.Cmdy("nfs.dir", "", arg[0]) + }}, + "_text": {Name: "_text", Help: "文章", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + tmpl := m.Target().Server().(*web.WEB).HandleCGI(m, arg[0]) + m.Optionv("title", map[string]int{}) + + buffer := bytes.NewBuffer([]byte{}) + m.Assert(tmpl.ExecuteTemplate(buffer, m.Option("filename", path.Base(arg[0])), m)) + + if f, p, e := kit.Create(path.Join("var/tmp/file", arg[0])); e == nil { + defer f.Close() + if n, e := f.Write(buffer.Bytes()); e == nil { + m.Log("info", "save %d %v", n, p) + } + } + + data := markdown.ToHTML(buffer.Bytes(), nil, nil) + m.Echo(string(data)) + }}, + "note": {Name: "note file|favor|commit", Help: "笔记", Meta: map[string]interface{}{ + "display": "inner", + }, List: []interface{}{ + map[string]interface{}{"type": "text", "value": "miss.md"}, + map[string]interface{}{"type": "button", "value": "执行"}, + }, Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) { + if len(arg) == 0 { + m.Cmdy("_tree") + return + } + + switch arg[0] { + case "favor", "commit": + m.Cmdy("story", arg[0], arg[1:]) + default: + m.Cmdy(kit.Select("_tree", "_text", strings.HasSuffix(arg[0], ".md")), arg[0]) + } + }}, + + "title": {Name: "title text", Help: "一级标题", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + ns := strings.Split(m.Conf("runtime", "node.name"), "-") + m.Option("render", cmd) + m.Echo(kit.Select(ns[len(ns)-1], arg, 0)) + }}, + }, +} + +func init() { web.Index.Register(Index, &web.WEB{}) } diff --git a/demo/Makefile b/demo/Makefile index c86d9c6f..a83a356a 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -1,2 +1,2 @@ all: - go build main.go + sh build.sh build && sh build.sh restart diff --git a/demo/build.sh b/demo/build.sh new file mode 100755 index 00000000..eff8f67c --- /dev/null +++ b/demo/build.sh @@ -0,0 +1,53 @@ +#! /bin/sh + +export ice_err=${ice_err:="boot.log"} +export ice_serve=${ice_serve:="web.serve"} + +prepare() { + [ -f main.go ] || cat >> main.go <> Makefile <> usr/template/common.tmpl <> usr/template/wiki/common.tmpl <$ice_err && log "\n\nrestarting..." && sleep 1 || break + done +} +log() { echo -e $*; } +restart() { + kill -2 `cat var/run/shy.pid` +} +help() { + echo "usage: $0 cmd arg" +} + +cmd=$1 && shift +[ -z "$cmd" ] && cmd=start +$cmd $* diff --git a/demo/go.sum b/demo/go.sum index 39df5266..367eb797 100644 --- a/demo/go.sum +++ b/demo/go.sum @@ -1,10 +1,9 @@ +github.com/gomarkdown/markdown v0.0.0-20191207194928-fbea82c4bb03 h1:m13UZm540+0yrpGOIXd7q4AvPGQPSTo+2jxrBK26o64= +github.com/gomarkdown/markdown v0.0.0-20191207194928-fbea82c4bb03/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/shylinux/icebergs v0.0.0-20191209060320-9f0b85bae35a h1:0RgyhBEPMnRwECFoDPYsz/FgB9yVA9/nJ+tJacwSAig= -github.com/shylinux/icebergs v0.0.0-20191209060320-9f0b85bae35a/go.mod h1:FowoSa6syu4hP1QWT9UNoQICuIwfP2PGQ/ao1fSeoOc= -github.com/shylinux/icebergs v0.0.0-20191212145348-fe6226481eaa h1:DcLg8BUz5a0ON2UICK7b61LPFJ2+HVC7E8vV5ItYABo= -github.com/shylinux/icebergs v0.0.0-20191212145348-fe6226481eaa/go.mod h1:FowoSa6syu4hP1QWT9UNoQICuIwfP2PGQ/ao1fSeoOc= +github.com/shylinux/icebergs v0.0.0-20191214121808-47bb65632d9d h1:154JIv2bSmesyjggRhH1lQKZ6WmvnvDQfWvQiWqvAbk= +github.com/shylinux/icebergs v0.0.0-20191214121808-47bb65632d9d/go.mod h1:lDuCMlmptkcvDfdAfRGo/Dt0xLS/cOaIXnliLYwpFWA= github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 h1:xwu6cGDBy/ZCrVklmCqSgx9O7Hr1v+hkzsIh1yc0hxg= github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= -github.com/shylinux/toolkits v0.0.0-20191212145555-d32eaba90a9e h1:eFZMsw0LuDeeVgwYwNJISebbMUZCVHEdQRaYBNWvC9s= -github.com/shylinux/toolkits v0.0.0-20191212145555-d32eaba90a9e/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= +golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= diff --git a/demo/main.go b/demo/main.go index 0335d4be..4f378dd5 100644 --- a/demo/main.go +++ b/demo/main.go @@ -2,7 +2,8 @@ package main import ( "github.com/shylinux/icebergs" - _ "github.com/shylinux/icebergs/misc/chat" + _ "github.com/shylinux/icebergs/core/chat" + _ "github.com/shylinux/icebergs/core/wiki" ) func main() { diff --git a/demo/miss.md b/demo/miss.md new file mode 100644 index 00000000..3fa96add --- /dev/null +++ b/demo/miss.md @@ -0,0 +1,17 @@ +# {{title "hello world"}} + +{{chart "chain" ` +context + volcanos + proto.js + frame.js + order.js + style.css + index.html + icebergs bg blue + web.go + aaa.go + cli.go + ctx.go + toolkits +`}} diff --git a/go.mod b/go.mod index cec98e45..4e0f9312 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/shylinux/icebergs go 1.13 require ( + github.com/gomarkdown/markdown v0.0.0-20191207194928-fbea82c4bb03 github.com/gorilla/websocket v1.4.1 github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 ) diff --git a/go.sum b/go.sum index 66bd88cb..31fa5d63 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,7 @@ +github.com/gomarkdown/markdown v0.0.0-20191207194928-fbea82c4bb03 h1:m13UZm540+0yrpGOIXd7q4AvPGQPSTo+2jxrBK26o64= +github.com/gomarkdown/markdown v0.0.0-20191207194928-fbea82c4bb03/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477 h1:xwu6cGDBy/ZCrVklmCqSgx9O7Hr1v+hkzsIh1yc0hxg= github.com/shylinux/toolkits v0.0.0-20191205193931-8b65f7e78477/go.mod h1:e1dV0lMyoKz4Luib6XyMNpfpn5Sn7POnq7XTT4wfN7k= +golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= diff --git a/misc/chat/chat.go b/misc/chat/chat.go deleted file mode 100644 index a1a505ff..00000000 --- a/misc/chat/chat.go +++ /dev/null @@ -1,65 +0,0 @@ -package chat - -import ( - "github.com/shylinux/icebergs" - "github.com/shylinux/icebergs/core/web" - _ "github.com/shylinux/icebergs/misc" - "github.com/shylinux/toolkits" -) - -var Index = &ice.Context{Name: "chat", Help: "聊天模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{ - "group": {Name: "group", Value: map[string]interface{}{ - "meta": map[string]interface{}{}, - "list": map[string]interface{}{}, - "hash": map[string]interface{}{}, - }}, - }, - Commands: map[string]*ice.Command{ - "_init": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - }}, - "/ocean": {Name: "/ocean", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - }}, - "/river": {Name: "/river", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) == 0 { - arg = kit.Simple(m.Optionv("cmds")) - } - - if len(arg) == 0 { - m.Confm("group", "hash", func(key string, value map[string]interface{}) { - m.Push("key", key) - m.Push("create_time", value["create_time"]) - m.Push("name", value["name"]) - }) - return - } - - switch arg[0] { - case "create": - // h := kit.Hashs("uniq") - h := kit.ShortKey(m.Confm("group", "hash"), 6) - m.Conf("group", "hash."+h, map[string]interface{}{ - "create_time": m.Time(), - "create_name": arg[1], - }) - m.Log("info", "river create %v %v", h, kit.Formats(m.Confv("group", "hash."+h))) - m.Echo(h) - } - }}, - "/action": {Name: "/action", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if cmds, ok := m.Optionv("cmds").([]string); ok { - m.Cmdy("web.space", cmds) - return - } - }}, - "/storm": {Name: "/storm", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - }}, - "/steam": {Name: "/steam", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - }}, - "_exit": {Name: "_init", Help: "hello", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - }}, - }, -} - -func init() { web.Index.Register(Index, &web.WEB{}) } diff --git a/misc/shy.go b/misc/shy.go deleted file mode 100644 index b77ba317..00000000 --- a/misc/shy.go +++ /dev/null @@ -1,16 +0,0 @@ -package shy - -import ( - _ "github.com/shylinux/icebergs/core/aaa" - _ "github.com/shylinux/icebergs/core/cli" - _ "github.com/shylinux/icebergs/core/ctx" - _ "github.com/shylinux/icebergs/core/gdb" - _ "github.com/shylinux/icebergs/core/lex" - _ "github.com/shylinux/icebergs/core/log" - _ "github.com/shylinux/icebergs/core/mdb" - _ "github.com/shylinux/icebergs/core/nfs" - _ "github.com/shylinux/icebergs/core/ssh" - _ "github.com/shylinux/icebergs/core/tcp" - _ "github.com/shylinux/icebergs/core/web" - _ "github.com/shylinux/icebergs/core/yac" -) diff --git a/type.go b/type.go index ef45c148..0f6ab027 100644 --- a/type.go +++ b/type.go @@ -3,12 +3,15 @@ package ice import ( "github.com/shylinux/toolkits" + "encoding/csv" "encoding/json" "errors" "fmt" "io" "os" + "path" "runtime" + "sort" "strings" "time" ) @@ -26,7 +29,8 @@ type Config struct { type Command struct { Name string Help interface{} - Form map[string]int + Meta map[string]interface{} + List []interface{} Hand func(m *Message, c *Context, key string, arg ...string) } type Context struct { @@ -183,7 +187,7 @@ func (m *Message) Spawn(arg ...interface{}) *Message { } func (m *Message) Spawns(arg ...interface{}) *Message { msg := m.Spawn(arg...) - msg.code = Index.ID() + msg.code = m.target.root.ID() m.messages = append(m.messages, msg) return msg } @@ -244,6 +248,9 @@ func (m *Message) Echo(str string, arg ...interface{}) *Message { func (m *Message) Option(key string, arg ...interface{}) string { return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) } +func (m *Message) Options(key string, arg ...interface{}) bool { + return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) != "" +} func (m *Message) Optionv(key string, arg ...interface{}) interface{} { if len(arg) > 0 { if kit.IndexOf(m.meta["option"], key) == -1 { @@ -270,6 +277,9 @@ func (m *Message) Optionv(key string, arg ...interface{}) interface{} { } return nil } +func (m *Message) Append(key string, arg ...interface{}) string { + return kit.Select("", m.meta[key], 0) +} func (m *Message) Resultv(arg ...interface{}) []string { return m.meta["result"] } @@ -343,9 +353,11 @@ func (m *Message) Search(key interface{}, cb func(p *Context, s *Context, key st break } } - if p != nil { - cb(p.context, p, list[len(list)-1]) + if p == nil { + m.Log("warn", "not found %s", key) + break } + cb(p.context, p, list[len(list)-1]) } else { cb(m.target.context, m.target, key) } @@ -378,11 +390,198 @@ func (m *Message) Back(sub *Message) *Message { return m } +func (m *Message) Grow(key string, args interface{}, data interface{}) interface{} { + cache := m.Confm(key, args) + if cache == nil { + cache = map[string]interface{}{} + } + meta, ok := cache["meta"].(map[string]interface{}) + if !ok { + meta = map[string]interface{}{} + } + list, _ := cache["list"].([]interface{}) + + list = append(list, data) + if len(list) > kit.Int(kit.Select(m.Conf("cache", "limit"), meta["limit"])) { + least := kit.Int(kit.Select(m.Conf("cache", "least"), meta["least"])) + + // 创建文件 + name := kit.Select(path.Join(m.Conf("cache", "store"), key+".csv"), meta["store"]) + f, e := os.OpenFile(name, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) + if e != nil { + f, _, e = kit.Create(name) + } + defer f.Close() + s, e := f.Stat() + m.Assert(e) + + // 保存数据 + keys := []string{} + w := csv.NewWriter(f) + if s.Size() == 0 { + for k := range list[0].(map[string]interface{}) { + keys = append(keys, k) + } + sort.Strings(keys) + w.Write(keys) + w.Flush() + s, e = f.Stat() + } else { + r := csv.NewReader(f) + keys, e = r.Read() + } + + // 保存状态 + count := len(list) - least + offset := kit.Int(meta["offset"]) + record, _ := meta["record"].([]interface{}) + meta["record"] = append(record, map[string]interface{}{ + "time": m.Time(), + "offset": offset, + "position": s.Size(), + "count": count, + "file": name, + }) + + // 保存数据 + for i, v := range list { + if i >= count { + break + } + + val := v.(map[string]interface{}) + + values := []string{} + for _, k := range keys { + values = append(values, kit.Format(val[k])) + } + w.Write(values) + + if i < least { + list[i] = list[count+i] + } + } + + m.Log("info", "save %s offset %v+%v", name, offset, count) + meta["offset"] = offset + count + list = list[:least] + w.Flush() + } + cache["meta"] = meta + cache["list"] = list + if args == nil { + m.Conf(key, cache) + } else { + m.Conf(key, args, cache) + } + return list +} +func (m *Message) Grows(key string, args interface{}, cb interface{}) map[string]interface{} { + cache := m.Confm(key, args) + if cache == nil { + return nil + } + meta, ok := cache["meta"].(map[string]interface{}) + if !ok { + return nil + } + list, ok := cache["list"].([]interface{}) + if !ok { + return nil + } + + offend := kit.Int(kit.Select("0", m.Option("cache.offend"))) + limit := kit.Int(kit.Select("10", m.Option("cache.limit"))) + match := kit.Select("", m.Option("cache.match")) + value := kit.Select("", m.Option("cache.value")) + current := kit.Int(meta["offset"]) + end := current + len(list) - offend + begin := end - limit + + data := make([]interface{}, 0, limit) + m.Log("info", "read %v-%v from %v-%v", begin, end, current, current+len(list)) + if begin < current { + store, _ := meta["record"].([]interface{}) + for s := len(store) - 1; s > -1; s-- { + item, _ := store[s].(map[string]interface{}) + line := kit.Int(item["offset"]) + m.Log("info", "check history %v %v %v", s, line, item) + if begin < line && s > 0 { + continue + } + + for ; s < len(store); s++ { + if begin >= end { + break + } + item, _ := store[s].(map[string]interface{}) + if line+kit.Int(item["count"]) < begin { + continue + } + + name := kit.Format(item["file"]) + pos := kit.Int(item["position"]) + line := kit.Int(item["offset"]) + m.Log("info", "load history %v %v %v", s, line, item) + if f, e := os.Open(name); m.Assert(e) { + defer f.Close() + r := csv.NewReader(f) + heads, _ := r.Read() + m.Log("info", "load head %v", heads) + + f.Seek(int64(pos), os.SEEK_SET) + r = csv.NewReader(f) + for i := line; i < end; i++ { + lines, e := r.Read() + if e != nil { + break + } + + if i >= begin { + item := map[string]interface{}{} + for i := range heads { + item[heads[i]] = lines[i] + } + m.Log("info", "load line %v %v %v", i, len(data), item) + if match == "" || strings.Contains(kit.Format(item[match]), value) { + data = append(data, item) + } + begin = i + 1 + } else { + m.Log("info", "skip line %v", i) + } + } + } + } + break + } + } + + if begin < current { + begin = current + } + m.Log("info", "cache %v-%v", begin-current, end-current) + for i := begin - current; i < end-current; i++ { + if match == "" || strings.Contains(kit.Format(kit.Value(list[i], match)), value) { + data = append(data, list[i]) + } + } + val := map[string]interface{}{"meta": meta, "list": data} + kit.Fetch(val, cb) + return val +} + func (m *Message) Cmdy(arg ...interface{}) *Message { msg := m.Cmd(arg...) m.Copy(msg) return m } +func (m *Message) Cmdx(arg ...interface{}) string { + return kit.Select("", m.Cmd(arg...).meta["result"], 0) +} +func (m *Message) Cmds(arg ...interface{}) bool { + return kit.Select("", m.Cmd(arg...).meta["result"], 0) != "" +} func (m *Message) Cmd(arg ...interface{}) *Message { list := kit.Simple(arg...) if len(list) == 0 { @@ -410,8 +609,11 @@ func (m *Message) Confv(arg ...interface{}) (val interface{}) { m.Search(arg[0], func(p *Context, s *Context, key string) { for c := s; c != nil; c = c.context { if conf, ok := c.Configs[key]; ok { - if len(arg) > 0 { - val = kit.Value(conf.Value, arg[1:]...) + if len(arg) > 1 { + if len(arg) > 2 { + kit.Value(conf.Value, arg[1:]...) + } + val = kit.Value(conf.Value, arg[1]) } else { val = conf.Value } @@ -423,15 +625,7 @@ func (m *Message) Confv(arg ...interface{}) (val interface{}) { func (m *Message) Confm(key string, chain interface{}, cbs ...interface{}) map[string]interface{} { val := m.Confv(key, chain) if len(cbs) > 0 { - switch val := val.(type) { - case map[string]interface{}: - switch cb := cbs[0].(type) { - case func(string, map[string]interface{}): - for k, v := range val { - cb(k, v.(map[string]interface{})) - } - } - } + kit.Fetch(val, cbs[0]) } value, _ := val.(map[string]interface{}) return value