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(``)
+ 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