From 6aff305ded0d28cf5018c007beb4e21e57cdd2ec Mon Sep 17 00:00:00 2001 From: shaoying Date: Wed, 17 Jun 2020 22:37:35 +0800 Subject: [PATCH] opt web --- base/aaa/aaa.go | 4 +- base/aaa/sess.go | 8 +- base/aaa/user.go | 51 +-- base/cli/cli.go | 6 + base/cli/system.go | 3 +- base/ctx/ctx.go | 3 - base/ssh/ssh.go | 2 + base/tcp/tcp.go | 10 +- base/web/cache.go | 15 +- base/web/dream.go | 87 +++++ base/web/serve.go | 245 ++++++++++++++ base/web/share.go | 10 +- base/web/space.go | 68 +++- base/web/spide.go | 273 ++++++++++++++++ base/web/web.go | 685 +--------------------------------------- misc/lark/lark.go | 2 +- misc/mp/mp.go | 2 +- misc/railway/railway.go | 2 +- 18 files changed, 731 insertions(+), 745 deletions(-) create mode 100644 base/web/dream.go create mode 100644 base/web/serve.go create mode 100644 base/web/spide.go diff --git a/base/aaa/aaa.go b/base/aaa/aaa.go index abc421da..4c2897be 100644 --- a/base/aaa/aaa.go +++ b/base/aaa/aaa.go @@ -2,7 +2,7 @@ package aaa import ( "github.com/shylinux/icebergs" - "github.com/shylinux/icebergs/base/cli" + // "github.com/shylinux/icebergs/base/cli" "github.com/shylinux/toolkits" ) @@ -26,8 +26,6 @@ var Index = &ice.Context{Name: "aaa", Help: "认证模块", Commands: map[string m.Rich(ROLE, nil, kit.Dict(kit.MDB_NAME, TECH, Black, kit.Dict(), White, kit.Dict())) m.Rich(ROLE, nil, kit.Dict(kit.MDB_NAME, VOID, White, kit.Dict(), Black, kit.Dict())) m.Load() - cli.PassWord = kit.Hashs("uniq") - _user_create(m, cli.UserName, cli.PassWord) }}, ice.ICE_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Save(ROLE, USER, SESS) diff --git a/base/aaa/sess.go b/base/aaa/sess.go index c9a9cd7c..972bc0c0 100644 --- a/base/aaa/sess.go +++ b/base/aaa/sess.go @@ -21,17 +21,19 @@ func _sess_auth(m *ice.Message, sessid string, username string, userrole string) } value["username"] = username m.Log_AUTH(SESSID, sessid, USERNAME, username, USERROLE, userrole) - m.Echo("%v", value[USERROLE]) }) } func _sess_check(m *ice.Message, sessid string) { m.Richs(SESS, nil, sessid, func(value map[string]interface{}) { - m.Push(sessid, value, []string{USERNAME, USERROLE}) - m.Echo("%s", value[USERROLE]) + m.Log_AUTH( + USERROLE, m.Option(ice.MSG_USERROLE, value[USERROLE]), + USERNAME, m.Option(ice.MSG_USERNAME, value[USERNAME]), + ) }) } func _sess_create(m *ice.Message, username string) string { h := m.Rich(SESS, nil, kit.Dict( + kit.MDB_TIME, m.Time(m.Conf(SESS, "meta.expire")), USERNAME, username, "from", m.Option(ice.MSG_SESSID), )) m.Log_CREATE(SESSID, h, USERNAME, username) diff --git a/base/aaa/user.go b/base/aaa/user.go index 679c1b87..a71b5213 100644 --- a/base/aaa/user.go +++ b/base/aaa/user.go @@ -25,12 +25,17 @@ func _user_create(m *ice.Message, name, word string) { // 创建用户 m.Rich(USER, nil, kit.Dict( USERNAME, name, PASSWORD, word, - USERNICK, name, USERNODE, m.Conf(ice.CLI_RUNTIME, "boot.hostname"), + USERNICK, name, USERNODE, cli.NodeName, )) m.Log_CREATE(USERNAME, name) m.Event(ice.USER_CREATE, name) } +func UserRoot(m *ice.Message) { + cli.PassWord = kit.Hashs("uniq") + cli.PassWord = cli.UserName + _user_create(m, cli.UserName, cli.PassWord) +} func UserRole(m *ice.Message, username string) string { if username == cli.UserName { return ROOT @@ -42,12 +47,6 @@ func UserLogin(m *ice.Message, username, password string) bool { m.Option(ice.MSG_USERNAME, username) m.Option(ice.MSG_USERROLE, UserRole(m, username)) m.Option(ice.MSG_SESSID, SessCreate(m, m.Option(ice.MSG_USERNAME), m.Option(ice.MSG_USERROLE))) - - m.Log_AUTH( - USERROLE, m.Option(ice.MSG_USERROLE), - USERNAME, m.Option(ice.MSG_USERNAME), - SESSID, m.Option(ice.MSG_SESSID), - ) return true } return false @@ -66,43 +65,7 @@ func init() { _user_login(m, arg[0], arg[1]) }}, }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) == 0 { - _user_list(m) - return - } - - switch arg[0] { - case "first": - // 超级用户 - if m.Richs(ice.AAA_USER, nil, "%", nil) == nil { - } - - case "login": - // 用户认证 - user := m.Richs(USER, nil, arg[1], nil) - if word := kit.Select("", arg, 2); user == nil { - nick := arg[1] - if len(nick) > 8 { - nick = nick[:8] - } - _user_create(m, arg[1], word) - - } else if word != "" { - if !_user_login(m, arg[1], word) { - m.Info("login fail user: %s", arg[1]) - break - } - } - - if m.Options(ice.MSG_SESSID) && m.Cmdx(ice.AAA_SESS, "check", m.Option(ice.MSG_SESSID)) == arg[1] { - // 复用会话 - m.Echo(m.Option(ice.MSG_SESSID)) - break - } - - // 创建会话 - m.Echo(m.Cmdx(ice.AAA_SESS, "create", arg[1])) - } + _user_list(m) }}, }, }, nil) diff --git a/base/cli/cli.go b/base/cli/cli.go index 55265743..4eb134bd 100644 --- a/base/cli/cli.go +++ b/base/cli/cli.go @@ -24,6 +24,12 @@ var HostName = "" var PathName = "" var NodeName = "" +func NodeType(m *ice.Message, kind, name string) { + m.Conf(ice.CLI_RUNTIME, "node.type", kind) + m.Conf(ice.CLI_RUNTIME, "node.name", name) + NodeName = name +} + var Index = &ice.Context{Name: "cli", Help: "命令模块", Configs: map[string]*ice.Config{ RUNTIME: {Name: "runtime", Help: "运行环境", Value: kit.Dict()}, diff --git a/base/cli/system.go b/base/cli/system.go index ebbb48d4..57f13be1 100644 --- a/base/cli/system.go +++ b/base/cli/system.go @@ -30,7 +30,8 @@ func _system_show(m *ice.Message, cmd *exec.Cmd) { cmd.Stderr = err defer m.Cost("%v exit: %v out: %v err: %v ", cmd.Args, 0, out.Len(), err.Len()) - if e := cmd.Run(); !m.Warn(e != nil, "%v run: %s", cmd.Args, kit.Select(e.Error(), err.String())) { + if e := cmd.Run(); e != nil { + m.Warn(e != nil, "%v run: %s", cmd.Args, kit.Select(e.Error(), err.String())) } m.Push(CMD_CODE, int(cmd.ProcessState.ExitCode())) diff --git a/base/ctx/ctx.go b/base/ctx/ctx.go index f9b638b8..fb8563f6 100644 --- a/base/ctx/ctx.go +++ b/base/ctx/ctx.go @@ -40,9 +40,6 @@ func _config_list(m *ice.Message, all bool) { func _config_save(m *ice.Message, name string, arg ...string) { msg := m.Spawn(m.Source()) // 保存配置 - if m.Cap(ice.CTX_STATUS) != ice.ICE_START { - return - } name = path.Join(msg.Conf(ice.CTX_CONFIG, "meta.path"), name) if f, p, e := kit.Create(name); m.Assert(e) { data := map[string]interface{}{} diff --git a/base/ssh/ssh.go b/base/ssh/ssh.go index bcc9fc34..bc7b77ac 100644 --- a/base/ssh/ssh.go +++ b/base/ssh/ssh.go @@ -2,6 +2,7 @@ package ssh import ( "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" "github.com/shylinux/toolkits" "bufio" @@ -178,6 +179,7 @@ func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server { func (f *Frame) Start(m *ice.Message, arg ...string) bool { m.Option(ice.MSG_PROMPT, m.Confv("prompt", "meta.PS1")) f.target = m.Source() + aaa.UserRoot(m) switch kit.Select("stdio", arg, 0) { case "stdio": diff --git a/base/tcp/tcp.go b/base/tcp/tcp.go index ccd0e11d..98d41467 100644 --- a/base/tcp/tcp.go +++ b/base/tcp/tcp.go @@ -57,7 +57,7 @@ func _ip_islocal(m *ice.Message, ip string) (ok bool) { }) return ok } -func _tcp_port(m *ice.Message) { +func _tcp_port(m *ice.Message) string { current := kit.Int(m.Conf(GETPORT, "meta.current")) end := kit.Int(m.Conf(GETPORT, "meta.end")) if current >= end { @@ -67,15 +67,19 @@ func _tcp_port(m *ice.Message) { if m.Cmd(cli.SYSTEM, "lsof", "-i", kit.Format(":%d", i)).Append(cli.CMD_CODE) != "0" { m.Conf(GETPORT, "meta.current", i) m.Log_CREATE(GETPORT, i) - m.Echo("%d", i) + return kit.Format("%d", i) break } } + return "" } func IPIsLocal(m *ice.Message, ip string) bool { return _ip_islocal(m, ip) } +func TCPPort(m *ice.Message) string { + return _tcp_port(m) +} var Index = &ice.Context{Name: "tcp", Help: "通信模块", Caches: map[string]*ice.Cache{}, @@ -92,7 +96,7 @@ var Index = &ice.Context{Name: "tcp", Help: "通信模块", _ip_list(m, "") }}, GETPORT: {Name: "getport", Help: "分配端口", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - _tcp_port(m) + m.Echo(_tcp_port(m)) }}, "ip": {Name: "ifconfig [name]", Help: "网络配置", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { diff --git a/base/web/cache.go b/base/web/cache.go index e4abef2d..c2510561 100644 --- a/base/web/cache.go +++ b/base/web/cache.go @@ -101,6 +101,12 @@ func _cache_watch(m *ice.Message, key, file string) { } func _cache_catch(m *ice.Message, arg ...string) []string { + if r, ok := m.Optionv("response").(*http.Response); ok { + return _cache_download(m, r, arg...) + } else if m.R != nil { + return _cache_upload(m, arg...) + } + if f, e := os.Open(arg[2]); m.Assert(e) { defer f.Close() @@ -188,16 +194,9 @@ func init() { } switch arg[0] { - case "catch": + case "download", "upload", "catch": arg = _cache_catch(m, arg...) fallthrough - case "download", "upload": - if r, ok := m.Optionv("response").(*http.Response); ok { - arg = _cache_download(m, r, arg...) - } else if m.R != nil { - arg = _cache_upload(m, arg...) - } - fallthrough case "add": _cache_save(m, arg[0], arg[1], arg[2], arg[3], arg[4:]...) case "watch": diff --git a/base/web/dream.go b/base/web/dream.go new file mode 100644 index 00000000..78ed2d24 --- /dev/null +++ b/base/web/dream.go @@ -0,0 +1,87 @@ +package web + +import ( + ice "github.com/shylinux/icebergs" + kit "github.com/shylinux/toolkits" + + "io/ioutil" + "os" + "path" + "strings" + "time" +) + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + ice.WEB_DREAM: {Name: "dream", Help: "梦想家", Value: kit.Data("path", "usr/local/work", + // "cmd", []interface{}{ice.CLI_SYSTEM, "ice.sh", "start", ice.WEB_SPACE, "connect"}, + "cmd", []interface{}{ice.CLI_SYSTEM, "ice.bin", ice.WEB_SPACE, "connect"}, + )}, + }, + Commands: map[string]*ice.Command{ + ice.WEB_DREAM: {Name: "dream name auto", Help: "梦想家", Meta: kit.Dict( + "exports", []string{"you", "name"}, "detail", []interface{}{"启动", "停止"}, + ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) > 1 && arg[0] == "action" { + switch arg[1] { + case "启动", "start": + arg = []string{arg[4]} + case "停止", "stop": + m.Cmd(ice.WEB_SPACE, kit.Select(m.Option("name"), arg, 4), "exit", "1") + m.Event(ice.DREAM_CLOSE, arg[4]) + return + } + } + + if len(arg) == 0 { + // 任务列表 + m.Cmdy("nfs.dir", m.Conf(ice.WEB_DREAM, "meta.path"), "time name") + m.Table(func(index int, value map[string]string, head []string) { + if m.Richs(ice.WEB_SPACE, nil, value["name"], func(key string, value map[string]interface{}) { + m.Push("type", value["type"]) + m.Push("status", "start") + }) == nil { + m.Push("type", "none") + m.Push("status", "stop") + } + }) + m.Sort("name") + m.Sort("status") + return + } + + // 规范命名 + if !strings.Contains(arg[0], "-") || !strings.HasPrefix(arg[0], "20") { + arg[0] = m.Time("20060102-") + arg[0] + } + + // 创建目录 + p := path.Join(m.Conf(ice.WEB_DREAM, "meta.path"), arg[0]) + os.MkdirAll(p, 0777) + + if b, e := ioutil.ReadFile(path.Join(p, m.Conf(ice.GDB_SIGNAL, "meta.pid"))); e == nil { + if s, e := os.Stat("/proc/" + string(b)); e == nil && s.IsDir() { + m.Info("already exists %v", string(b)) + return + } + } + + if m.Richs(ice.WEB_SPACE, nil, arg[0], nil) == nil { + // 启动任务 + m.Option("cmd_dir", p) + m.Option("cmd_type", "daemon") + m.Optionv("cmd_env", + "ctx_dev", m.Conf(ice.CLI_RUNTIME, "conf.ctx_dev"), + "ctx_log", "boot.log", "ctx_mod", "ctx,log,gdb,ssh", + "PATH", kit.Path(path.Join(p, "bin"))+":"+os.Getenv("PATH"), + ) + m.Cmd(m.Confv(ice.WEB_DREAM, "meta.cmd"), "self", arg[0]) + time.Sleep(time.Second * 1) + m.Event(ice.DREAM_START, arg...) + } + m.Cmdy("nfs.dir", p) + }}, + }, + }, nil) +} diff --git a/base/web/serve.go b/base/web/serve.go new file mode 100644 index 00000000..c0ac8a34 --- /dev/null +++ b/base/web/serve.go @@ -0,0 +1,245 @@ +package web + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/aaa" + "github.com/shylinux/icebergs/base/cli" + "github.com/shylinux/icebergs/base/tcp" + kit "github.com/shylinux/toolkits" + + "encoding/json" + "net/http" + "net/url" + "os" + "strings" +) + +func Login(msg *ice.Message, w http.ResponseWriter, r *http.Request) bool { + msg.Option(ice.MSG_USERNAME, "") + msg.Option(ice.MSG_USERROLE, "") + + if msg.Options(ice.MSG_SESSID) { + // 会话认证 + aaa.SessCheck(msg, msg.Option(ice.MSG_SESSID)) + } + + if !msg.Options(ice.MSG_USERNAME) && tcp.IPIsLocal(msg, msg.Option(ice.MSG_USERIP)) { + // 自动认证 + if aaa.UserLogin(msg, cli.UserName, cli.PassWord) { + Render(msg, "cookie", msg.Option(ice.MSG_SESSID)) + } + } + + if s, ok := msg.Target().Commands[ice.WEB_LOGIN]; ok { + // 权限检查 + msg.Target().Run(msg, s, ice.WEB_LOGIN, kit.Simple(msg.Optionv("cmds"))...) + + } else if ls := strings.Split(msg.Option(ice.MSG_USERURL), "/"); msg.Conf(SERVE, kit.Keys("meta.black", ls[1])) == "true" { + return false // black + + } else if msg.Conf(SERVE, kit.Keys("meta.white", ls[1])) == "true" { + return true // white + + } else { + if msg.Warn(!msg.Options(ice.MSG_USERNAME), "not login %s", msg.Option(ice.MSG_USERURL)) { + msg.Render("status", 401, "not login") + return false + } + if !msg.Right(msg.Option(ice.MSG_USERURL)) { + msg.Render("status", 403, "not auth") + return false + } + } + + return msg.Option(ice.MSG_USERURL) != "" +} +func Trans(web *Frame, 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.Cost("%s %v %v", r.URL.Path, msg.Optionv("cmds"), msg.Format("append")) }() + if u, e := url.Parse(r.Header.Get("Referer")); e == nil { + for k, v := range u.Query() { + msg.Logs("refer", k, v) + msg.Option(k, v) + } + } + + // 用户请求 + msg.Option(ice.MSG_USERWEB, m.Conf(SHARE, "meta.domain")) + msg.Option(ice.MSG_USERIP, r.Header.Get(ice.MSG_USERIP)) + msg.Option(ice.MSG_USERUA, r.Header.Get("User-Agent")) + msg.Option(ice.MSG_USERURL, r.URL.Path) + if msg.R, msg.W = r, w; r.Header.Get("X-Real-Port") != "" { + msg.Option(ice.MSG_USERADDR, msg.Option(ice.MSG_USERIP)+":"+r.Header.Get("X-Real-Port")) + } else { + msg.Option(ice.MSG_USERADDR, r.RemoteAddr) + } + + // 请求变量 + msg.Option(ice.MSG_SESSID, "") + msg.Option(ice.MSG_OUTPUT, "") + for _, v := range r.Cookies() { + msg.Option(v.Name, v.Value) + } + + // 解析引擎 + switch r.Header.Get("Content-Type") { + case "application/json": + var data interface{} + if e := json.NewDecoder(r.Body).Decode(&data); !msg.Warn(e != nil, "%s", e) { + msg.Optionv(ice.MSG_USERDATA, data) + msg.Logs("json", "value", kit.Formats(data)) + } + + switch d := data.(type) { + case map[string]interface{}: + for k, v := range d { + msg.Optionv(k, v) + } + } + default: + r.ParseMultipartForm(kit.Int64(kit.Select(r.Header.Get("Content-Length"), "4096"))) + if r.ParseForm(); len(r.PostForm) > 0 { + for k, v := range r.PostForm { + msg.Logs("form", k, v) + } + } + } + + // 请求参数 + for k, v := range r.Form { + if msg.Optionv(k, v); k == ice.MSG_SESSID { + msg.Render("cookie", v[0]) + } + } + + // 请求命令 + if msg.Option(ice.MSG_USERPOD, msg.Option("pod")); msg.Optionv("cmds") == nil { + if p := strings.TrimPrefix(msg.Option(ice.MSG_USERURL), key); p != "" { + msg.Optionv("cmds", strings.Split(p, "/")) + } + } + + // 执行命令 + if cmds := kit.Simple(msg.Optionv("cmds")); Login(msg, w, r) { + msg.Option("_option", msg.Optionv(ice.MSG_OPTION)) + msg.Target().Run(msg, cmd, msg.Option(ice.MSG_USERURL), cmds...) + } + + // 渲染引擎 + _args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{}) + Render(msg, msg.Option(ice.MSG_OUTPUT), _args...) + }) + }) +} +func (web *Frame) ServeHTTP(w http.ResponseWriter, r *http.Request) { + m := web.m + + if r.Header.Get("index.module") == "" { + // 解析地址 + if ip := r.Header.Get("X-Forwarded-For"); ip != "" { + r.Header.Set(ice.MSG_USERIP, ip) + } else if ip := r.Header.Get("X-Real-Ip"); ip != "" { + r.Header.Set(ice.MSG_USERIP, ip) + } else if strings.HasPrefix(r.RemoteAddr, "[") { + r.Header.Set(ice.MSG_USERIP, strings.Split(r.RemoteAddr, "]")[0][1:]) + } else { + r.Header.Set(ice.MSG_USERIP, strings.Split(r.RemoteAddr, ":")[0]) + } + m.Info("").Info("%s %s %s", r.Header.Get(ice.MSG_USERIP), r.Method, r.URL) + + r.Header.Set("index.module", m.Target().Name) + r.Header.Set("index.path", r.URL.Path) + r.Header.Set("index.url", r.URL.String()) + + if m.Conf(SERVE, "meta.logheaders") == "true" { + // 请求参数 + for k, v := range r.Header { + m.Info("%s: %v", k, kit.Format(v)) + } + m.Info(" ") + + defer func() { + // 响应参数 + for k, v := range w.Header() { + m.Info("%s: %v", k, kit.Format(v)) + } + m.Info(" ") + }() + } + } + + if strings.HasPrefix(r.URL.Path, "/debug") { + r.URL.Path = strings.Replace(r.URL.Path, "/debug", "/code", -1) + } + + if r.URL.Path == "/" && m.Conf(SERVE, "meta.init") != "true" { + if _, e := os.Stat(m.Conf(SERVE, "meta.volcanos.path")); e == nil { + // 初始化成功 + m.Conf(SERVE, "meta.init", "true") + } + m.W = w + Render(m, "refresh", m.Conf(SERVE, "meta.volcanos.refresh")) + m.Event(ice.SYSTEM_INIT) + m.W = nil + } else if r.URL.Path == "/share" && r.Method == "GET" { + http.ServeFile(w, r, m.Conf(SERVE, "meta.page.share")) + } else { + web.ServeMux.ServeHTTP(w, r) + } +} +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + ice.WEB_SERVE: {Name: "serve", Help: "服务器", Value: kit.Data( + "init", "false", "logheaders", "false", + "black", kit.Dict(), + "white", kit.Dict( + "login", true, + "share", true, + "space", true, + "route", true, + "static", true, + "plugin", true, + "publish", true, + ), + + "title", "github.com/shylinux/contexts", + "legal", []interface{}{`shylinuxc@gmail.com`}, + + "static", kit.Dict("/", "usr/volcanos/"), + "volcanos", kit.Dict("path", "usr/volcanos", "branch", "master", + "repos", "https://github.com/shylinux/volcanos", + "require", ".ish/pluged", + "refresh", "5", + ), "page", kit.Dict( + "index", "usr/volcanos/page/index.html", + "share", "usr/volcanos/page/share.html", + ), "publish", "usr/publish/", + + "template", kit.Dict("path", "usr/template", "list", []interface{}{ + `{{define "raw"}}{{.Result}}{{end}}`, + }), + )}, + }, + Commands: map[string]*ice.Command{ + ice.WEB_SERVE: {Name: "serve [random] [ups...]", Help: "服务器", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if cli.NodeType(m, ice.WEB_SERVER, cli.HostName); len(arg) > 0 && arg[0] == "random" { + cli.NodeType(m, ice.WEB_SERVER, cli.PathName) + // 随机端口 + SpideCreate(m, "self", "http://random") + arg = arg[1:] + } + + // 启动服务 + m.Target().Start(m, "self") + defer m.Cmd(ice.WEB_SPACE, "connect", "self") + m.Sleep("1s") + + // 连接服务 + for _, k := range arg { + m.Cmd(ice.WEB_SPACE, "connect", k) + } + }}, + }}, nil) +} diff --git a/base/web/share.go b/base/web/share.go index 973178bc..3dceb53b 100644 --- a/base/web/share.go +++ b/base/web/share.go @@ -10,8 +10,6 @@ import ( "strings" ) -var SHARE = ice.Name(kit.MDB_SHARE, Index) - func _share_list(m *ice.Message, key string, fields ...string) { if key == "" { m.Grows(SHARE, nil, "", "", func(index int, value map[string]interface{}) { @@ -85,7 +83,7 @@ func _share_remote(m *ice.Message, pod string, arg ...string) { m.Cmdy(ice.WEB_SPACE, pod, "web./publish/", arg) m.Render(ice.RENDER_RESULT) } -func _share_create(m *ice.Message, kind, name, text string, arg ...string) { +func _share_create(m *ice.Message, kind, name, text string, arg ...string) string { for _, k := range []string{"river", "storm"} { arg = append(arg, k, m.Option(k)) } @@ -103,6 +101,7 @@ func _share_create(m *ice.Message, kind, name, text string, arg ...string) { )) m.Log_CREATE(kit.MDB_SHARE, h, kit.MDB_TYPE, kind, kit.MDB_NAME, name) m.Echo(h) + return h } func _share_story(m *ice.Message, value map[string]interface{}, arg ...string) map[string]interface{} { @@ -221,6 +220,11 @@ func _trash(m *ice.Message, arg ...string) { } } } + +func ShareCreate(m *ice.Message, kind, name, text string, arg ...string) string { + return _share_create(m, kind, name, text, arg...) +} + func init() { Index.Merge(&ice.Context{ Configs: map[string]*ice.Config{ diff --git a/base/web/space.go b/base/web/space.go index b5a6b06f..07b93f5e 100644 --- a/base/web/space.go +++ b/base/web/space.go @@ -58,7 +58,7 @@ func _space_dial(m *ice.Message, dev, name string, arg ...string) { "socket", s, )) msg.Log(ice.LOG_CMDS, "%d conn %s success %s", i, dev, u) - if i = 0; web.HandleWSS(msg, true, s, dev) { + if i = 0; HandleWSS(msg, true, web.send, s, dev) { break } } @@ -112,6 +112,70 @@ func _space_send(m *ice.Message, space string, arg ...string) { }) == nil, "not found %s", space) } +func HandleWSS(m *ice.Message, safe bool, send map[string]*ice.Message, c *websocket.Conn, name string) bool { + for running := true; running; { + if t, b, e := c.ReadMessage(); m.Warn(e != nil, "space recv %d msg %v", t, e) { + // 解析失败 + break + } else { + socket, msg := c, m.Spawns(b) + target := kit.Simple(msg.Optionv(ice.MSG_TARGET)) + source := kit.Simple(msg.Optionv(ice.MSG_SOURCE), name) + msg.Info("recv %v<-%v %s %v", target, source, msg.Detailv(), msg.Format("meta")) + + if len(target) == 0 { + msg.Option(ice.MSG_USERROLE, msg.Cmdx(ice.AAA_ROLE, "check", msg.Option(ice.MSG_USERNAME))) + msg.Logs(ice.LOG_AUTH, "role", msg.Option(ice.MSG_USERROLE), "user", msg.Option(ice.MSG_USERNAME)) + if msg.Optionv(ice.MSG_HANDLE, "true"); !msg.Warn(!safe, "no right") { + // 本地执行 + m.Option("_dev", name) + msg = msg.Cmd() + msg.Set("_option") + msg.Set("_option") + } + if source, target = []string{}, kit.Revert(source)[1:]; msg.Detail() == "exit" { + // 重启进程 + return true + } + } else if msg.Richs(ice.WEB_SPACE, nil, target[0], func(key string, value map[string]interface{}) { + // 查询节点 + if s, ok := value["socket"].(*websocket.Conn); ok { + socket, source, target = s, source, target[1:] + } else { + socket, source, target = s, source, target[1:] + } + }) != nil { + // 转发报文 + + } else if res, ok := send[msg.Option(ice.MSG_TARGET)]; len(target) == 1 && ok { + // 接收响应 + delete(send, msg.Option(ice.MSG_TARGET)) + res.Cost("%v->%v %v %v", target, source, res.Detailv(), msg.Format("append")) + res.Back(msg) + continue + + } else if msg.Warn(msg.Option("_handle") == "true", "space miss") { + // 回复失败 + continue + + } else { + // 下发失败 + msg.Warn(true, "space error") + source, target = []string{}, kit.Revert(source)[1:] + } + + // 发送报文 + msg.Optionv(ice.MSG_SOURCE, source) + msg.Optionv(ice.MSG_TARGET, target) + socket.WriteMessage(t, []byte(msg.Format("meta"))) + target = append([]string{name}, target...) + msg.Info("send %v %v->%v %v %v", t, source, target, msg.Detailv(), msg.Format("meta")) + msg.Cost("%v->%v %v %v", source, target, msg.Detailv(), msg.Format("append")) + } + } + return false +} + func init() { Index.Merge(&ice.Context{ Configs: map[string]*ice.Config{ @@ -180,7 +244,7 @@ func init() { m.Gos(m, func(m *ice.Message) { // 监听消息 m.Event(ice.SPACE_START, m.Option("node"), m.Option("name")) - m.Target().Server().(*Frame).HandleWSS(m, false, s, m.Option("name")) + HandleWSS(m, false, m.Target().Server().(*Frame).send, s, m.Option("name")) m.Log(ice.LOG_CLOSE, "%s: %s", m.Option(kit.MDB_NAME), kit.Format(m.Confv(ice.WEB_SPACE, kit.Keys(kit.MDB_HASH, h)))) m.Event(ice.SPACE_CLOSE, m.Option("node"), m.Option("name")) m.Confv(ice.WEB_SPACE, kit.Keys(kit.MDB_HASH, h), "") diff --git a/base/web/spide.go b/base/web/spide.go new file mode 100644 index 00000000..3e1f1f73 --- /dev/null +++ b/base/web/spide.go @@ -0,0 +1,273 @@ +package web + +import ( + ice "github.com/shylinux/icebergs" + "github.com/shylinux/icebergs/base/tcp" + kit "github.com/shylinux/toolkits" + + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/url" + "os" + "path" + "strings" +) + +func _spide_list(m *ice.Message, name string) { + if name == "" { + m.Richs(SPIDE, nil, kit.MDB_FOREACH, func(key string, value map[string]interface{}) { + m.Push(key, value["client"], []string{"name", "share", "login", "method", "url"}) + }) + m.Sort("name") + return + } + + m.Richs(SPIDE, nil, name, func(key string, value map[string]interface{}) { + m.Push("detail", value) + if kit.Value(value, "client.share") != nil { + m.Push("key", "share") + m.Push("value", fmt.Sprintf(m.Conf(SHARE, "meta.template.text"), m.Conf(SHARE, "meta.domain"), kit.Value(value, "client.share"))) + } + }) +} +func _spide_show(m *ice.Message, name string) { +} +func _spide_login(m *ice.Message, name string) { + m.Richs(SPIDE, nil, name, func(key string, value map[string]interface{}) { + msg := m.Cmd(SPIDE, name, "msg", "/route/login", "login") + if msg.Append(ice.MSG_USERNAME) != "" { + m.Echo(msg.Append(ice.MSG_USERNAME)) + return + } + if msg.Result() != "" { + kit.Value(value, "client.login", msg.Result()) + kit.Value(value, "client.share", m.Cmdx(SHARE, ice.TYPE_SPIDE, name, + kit.Format("%s?sessid=%s", kit.Value(value, "client.url"), kit.Value(value, "cookie.sessid")))) + } + m.Render(ice.RENDER_QRCODE, kit.Dict( + kit.MDB_TYPE, "login", kit.MDB_NAME, name, + kit.MDB_TEXT, kit.Value(value, "cookie.sessid"), + )) + }) +} +func _spide_create(m *ice.Message, name, address string, arg ...string) { + if uri, e := url.Parse(address); e == nil && address != "" { + if uri.Host == "random" { + uri.Host = ":" + tcp.TCPPort(m) + address = strings.Replace(address, "random", uri.Host, -1) + } + + if m.Richs(SPIDE, nil, name, func(key string, value map[string]interface{}) { + kit.Value(value, "client.hostname", uri.Host) + kit.Value(value, "client.url", address) + }) == nil { + dir, file := path.Split(uri.EscapedPath()) + m.Rich(SPIDE, nil, kit.Dict( + "cookie", kit.Dict(), "header", kit.Dict(), "client", kit.Dict( + "share", ShareCreate(m.Spawn(), ice.TYPE_SPIDE, name, address), + "name", name, "url", address, "method", "POST", + "protocol", uri.Scheme, "hostname", uri.Host, + "path", dir, "file", file, "query", uri.RawQuery, + "timeout", "100s", "logheaders", false, + ), + )) + } + m.Log_CREATE(SPIDE, name, "address", address) + } +} + +func SpideCreate(m *ice.Message, name, address string, arg ...string) { + _spide_create(m, name, address, arg...) +} +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + SPIDE: {Name: "spide", Help: "蜘蛛侠", Value: kit.Data(kit.MDB_SHORT, "client.name")}, + }, + Commands: map[string]*ice.Command{ + SPIDE: {Name: "spide name=auto [action:select=msg|raw|cache] [method:select=POST|GET] url [format:select=json|form|part|data|file] arg... auto", Help: "蜘蛛侠", Action: map[string]*ice.Action{ + kit.MDB_CREATE: {Name: "create name address", Help: "", Hand: func(m *ice.Message, arg ...string) { + _spide_create(m, arg[0], arg[1]) + }}, + "login": {Name: "login name", Help: "", Hand: func(m *ice.Message, arg ...string) { + _spide_login(m, arg[0]) + }}, + }, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) < 2 || len(arg) > 3 && arg[3] == "" { + _spide_list(m, kit.Select("", arg, 1)) + return + } + + m.Richs(SPIDE, nil, arg[0], func(key string, value map[string]interface{}) { + client := value["client"].(map[string]interface{}) + // 缓存数据 + cache := "" + switch arg[1] { + case "raw": + cache, arg = arg[1], arg[1:] + case "msg": + cache, arg = arg[1], arg[1:] + case "cache": + cache, arg = arg[1], arg[1:] + } + + // 请求方法 + method := kit.Select("POST", client["method"]) + switch arg = arg[1:]; arg[0] { + case "POST": + method, arg = "POST", arg[1:] + case "GET": + method, arg = "GET", arg[1:] + } + + // 请求地址 + uri, arg := arg[0], arg[1:] + + // 渲染引擎 + head := map[string]string{} + body, ok := m.Optionv("body").(io.Reader) + if !ok && len(arg) > 0 && method != "GET" { + switch arg[0] { + case "file": + if f, e := os.Open(arg[1]); m.Warn(e != nil, "%s", e) { + return + } else { + defer f.Close() + body, arg = f, arg[2:] + } + case "data": + body, arg = bytes.NewBufferString(arg[1]), arg[2:] + case "part": + buf := &bytes.Buffer{} + mp := multipart.NewWriter(buf) + for i := 1; i < len(arg)-1; i += 2 { + if strings.HasPrefix(arg[i+1], "@") { + if f, e := os.Open(arg[i+1][1:]); m.Assert(e) { + defer f.Close() + if p, e := mp.CreateFormFile(arg[i], path.Base(arg[i+1][1:])); m.Assert(e) { + io.Copy(p, f) + } + } + } else { + mp.WriteField(arg[i], arg[i+1]) + } + } + mp.Close() + head["Content-Type"] = mp.FormDataContentType() + body = buf + case "form": + data := []string{} + for i := 1; i < len(arg)-1; i += 2 { + data = append(data, url.QueryEscape(arg[i])+"="+url.QueryEscape(arg[i+1])) + } + body = bytes.NewBufferString(strings.Join(data, "&")) + head["Content-Type"] = "application/x-www-form-urlencoded" + case "json": + arg = arg[1:] + fallthrough + default: + data := map[string]interface{}{} + for i := 0; i < len(arg)-1; i += 2 { + kit.Value(data, arg[i], arg[i+1]) + } + if b, e := json.Marshal(data); m.Assert(e) { + head["Content-Type"] = "application/json" + body = bytes.NewBuffer(b) + } + m.Log(ice.LOG_EXPORT, "json: %s", kit.Format(data)) + } + arg = arg[:0] + } else { + body = bytes.NewBuffer([]byte{}) + } + + // 请求地址 + uri = kit.MergeURL2(kit.Format(client["url"]), uri, arg) + req, e := http.NewRequest(method, uri, body) + m.Info("%s %s", req.Method, req.URL) + m.Assert(e) + + // 请求变量 + kit.Fetch(value["cookie"], func(key string, value string) { + req.AddCookie(&http.Cookie{Name: key, Value: value}) + m.Info("%s: %s", key, value) + }) + kit.Fetch(value["header"], func(key string, value string) { + req.Header.Set(key, value) + }) + list := kit.Simple(m.Optionv("header")) + for i := 0; i < len(list)-1; i += 2 { + req.Header.Set(list[i], list[i+1]) + } + for k, v := range head { + req.Header.Set(k, v) + } + + // 请求代理 + web := m.Target().Server().(*Frame) + if web.Client == nil { + web.Client = &http.Client{Timeout: kit.Duration(kit.Format(client["timeout"]))} + } + if method == "POST" { + m.Info("%s: %s", req.Header.Get("Content-Length"), req.Header.Get("Content-Type")) + } + + // 发送请求 + res, e := web.Client.Do(req) + if m.Warn(e != nil, "%s", e) { + return + } + + // 检查结果 + if m.Cost("%s %s: %s", res.Status, res.Header.Get("Content-Length"), res.Header.Get("Content-Type")); m.Warn(res.StatusCode != http.StatusOK, "%s", res.Status) { + return + } + + // 缓存变量 + for _, v := range res.Cookies() { + kit.Value(value, "cookie."+v.Name, v.Value) + m.Log(ice.LOG_IMPORT, "%s: %s", v.Name, v.Value) + } + + // 解析引擎 + switch cache { + case "cache": + m.Optionv("response", res) + CacheCatch(m, res.Header.Get("Content-Type"), uri) + m.Echo(m.Append("data")) + case "raw": + if b, e := ioutil.ReadAll(res.Body); m.Assert(e) { + m.Echo(string(b)) + } + case "msg": + var data map[string][]string + m.Assert(json.NewDecoder(res.Body).Decode(&data)) + m.Info("res: %s", kit.Formats(data)) + for _, k := range data[ice.MSG_APPEND] { + for i := range data[k] { + m.Push(k, data[k][i]) + } + } + m.Resultv(data[ice.MSG_RESULT]) + default: + if strings.HasPrefix(res.Header.Get("Content-Type"), "text/html") { + b, _ := ioutil.ReadAll(res.Body) + m.Echo(string(b)) + break + } + + var data interface{} + m.Assert(json.NewDecoder(res.Body).Decode(&data)) + data = kit.KeyValue(map[string]interface{}{}, "", data) + m.Info("res: %s", kit.Formats(data)) + m.Push("", data) + } + }) + }}, + }}, nil) +} diff --git a/base/web/web.go b/base/web/web.go index a0806b6a..c1226e27 100644 --- a/base/web/web.go +++ b/base/web/web.go @@ -1,31 +1,23 @@ package web import ( - "github.com/gorilla/websocket" ice "github.com/shylinux/icebergs" - "github.com/shylinux/icebergs/base/aaa" - "github.com/shylinux/icebergs/base/cli" - "github.com/shylinux/icebergs/base/tcp" kit "github.com/shylinux/toolkits" "github.com/skip2/go-qrcode" - "bytes" - "encoding/json" "fmt" - "io" - "io/ioutil" - "mime/multipart" "net/http" - "net/url" - "os" "path" "strings" "sync" - "text/template" "time" ) -var SERVE = ice.Name("serve", Index) +const ( + SPIDE = "spide" + SERVE = "serve" + SHARE = "share" +) type Frame struct { *http.Client @@ -99,306 +91,6 @@ func Render(msg *ice.Message, cmd string, args ...interface{}) { } msg.Append(ice.MSG_OUTPUT, ice.RENDER_OUTPUT) } -func IsLocalIP(msg *ice.Message, ip string) (ok bool) { - if ip == "::1" || strings.HasPrefix(ip, "127.") { - return true - } - - msg.Log_AUTH("ip", ip) - if msg.Richs(SERVE, kit.Keys("meta.white"), ip, nil) != nil { - msg.Log_AUTH("ip", ip) - return true - } - - msg.Cmd("tcp.ifconfig").Table(func(index int, value map[string]string, head []string) { - if value["ip"] == ip { - ok = true - } - }) - return ok -} - -func (web *Frame) Login(msg *ice.Message, w http.ResponseWriter, r *http.Request) bool { - msg.Option(ice.MSG_USERNAME, "") - msg.Option(ice.MSG_USERROLE, "") - - if msg.Options(ice.MSG_SESSID) { - // 会话认证 - sub := aaa.SessCheck(msg.Spawn(), msg.Option(ice.MSG_SESSID)) - msg.Log_AUTH( - aaa.USERROLE, msg.Option(ice.MSG_USERROLE, sub.Append(aaa.USERROLE)), - aaa.USERNAME, msg.Option(ice.MSG_USERNAME, sub.Append(aaa.USERNAME)), - ) - } - - if !msg.Options(ice.MSG_USERNAME) && tcp.IPIsLocal(msg, msg.Option(ice.MSG_USERIP)) { - // 自动认证 - if aaa.UserLogin(msg, cli.UserName, cli.PassWord) { - Render(msg, "cookie", msg.Option(ice.MSG_SESSID)) - } - } - - if s, ok := msg.Target().Commands[ice.WEB_LOGIN]; ok { - // 权限检查 - msg.Target().Run(msg, s, ice.WEB_LOGIN, kit.Simple(msg.Optionv("cmds"))...) - } else if ls := strings.Split(msg.Option(ice.MSG_USERURL), "/"); kit.IndexOf([]string{ - "static", "plugin", "login", "space", "route", "share", - "publish", - }, ls[1]) > -1 { - - } else { - if msg.Warn(!msg.Options(ice.MSG_USERNAME), "not login %s", msg.Option(ice.MSG_USERURL)) { - msg.Render("status", 401, "not login") - return false - } - if !msg.Right(msg.Option(ice.MSG_USERURL)) { - msg.Render("status", 403, "not auth") - return false - } - } - - return msg.Option(ice.MSG_USERURL) != "" -} -func (web *Frame) HandleWSS(m *ice.Message, safe bool, c *websocket.Conn, name string) bool { - for running := true; running; { - if t, b, e := c.ReadMessage(); m.Warn(e != nil, "space recv %d msg %v", t, e) { - // 解析失败 - break - } else { - socket, msg := c, m.Spawns(b) - target := kit.Simple(msg.Optionv(ice.MSG_TARGET)) - source := kit.Simple(msg.Optionv(ice.MSG_SOURCE), name) - msg.Info("recv %v<-%v %s %v", target, source, msg.Detailv(), msg.Format("meta")) - - if len(target) == 0 { - msg.Option(ice.MSG_USERROLE, msg.Cmdx(ice.AAA_ROLE, "check", msg.Option(ice.MSG_USERNAME))) - msg.Logs(ice.LOG_AUTH, "role", msg.Option(ice.MSG_USERROLE), "user", msg.Option(ice.MSG_USERNAME)) - if msg.Optionv(ice.MSG_HANDLE, "true"); !msg.Warn(!safe, "no right") { - // 本地执行 - m.Option("_dev", name) - msg = msg.Cmd() - msg.Set("_option") - msg.Set("_option") - } - if source, target = []string{}, kit.Revert(source)[1:]; msg.Detail() == "exit" { - // 重启进程 - return true - } - } else if msg.Richs(ice.WEB_SPACE, nil, target[0], func(key string, value map[string]interface{}) { - // 查询节点 - if s, ok := value["socket"].(*websocket.Conn); ok { - socket, source, target = s, source, target[1:] - } else { - socket, source, target = s, source, target[1:] - } - }) != nil { - // 转发报文 - - } else if res, ok := web.send[msg.Option(ice.MSG_TARGET)]; len(target) == 1 && ok { - // 接收响应 - delete(web.send, msg.Option(ice.MSG_TARGET)) - res.Cost("%v->%v %v %v", target, source, res.Detailv(), msg.Format("append")) - res.Back(msg) - continue - - } else if msg.Warn(msg.Option("_handle") == "true", "space miss") { - // 回复失败 - continue - - } else { - // 下发失败 - msg.Warn(true, "space error") - source, target = []string{}, kit.Revert(source)[1:] - } - - // 发送报文 - msg.Optionv(ice.MSG_SOURCE, source) - msg.Optionv(ice.MSG_TARGET, target) - socket.WriteMessage(t, []byte(msg.Format("meta"))) - target = append([]string{name}, target...) - msg.Info("send %v %v->%v %v %v", t, source, target, msg.Detailv(), msg.Format("meta")) - msg.Cost("%v->%v %v %v", source, target, msg.Detailv(), msg.Format("append")) - } - } - return false -} -func (web *Frame) HandleCGI(m *ice.Message, alias map[string]interface{}, which string) *template.Template { - cgi := template.FuncMap{} - - tmpl := template.New(ice.WEB_TMPL) - cb := func(k string, p []string, v *ice.Command) { - cgi[k] = func(arg ...interface{}) (res interface{}) { - m.TryCatch(m.Spawn(), true, func(msg *ice.Message) { - msg.Target().Run(msg, v, k, kit.Simple(p, arg)...) - - buffer := bytes.NewBuffer([]byte{}) - m.Assert(tmpl.ExecuteTemplate(buffer, msg.Option(ice.WEB_TMPL), msg)) - res = string(buffer.Bytes()) - }) - return - } - } - for k, v := range alias { - list := kit.Simple(v) - if v, ok := m.Target().Commands[list[0]]; ok { - cb(k, list[1:], v) - } - } - for k, v := range m.Target().Commands { - if strings.HasPrefix(k, "/") || strings.HasPrefix(k, "_") { - continue - } - cb(k, nil, v) - } - - tmpl = tmpl.Funcs(cgi) - // tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf(ice.WEB_SERVE, ice.Meta("template", "path")), "/*.tmpl"))) - // tmpl = template.Must(tmpl.ParseGlob(path.Join(m.Conf(ice.WEB_SERVE, ice.Meta("template", "path")), m.Target().Name, "/*.tmpl"))) - tmpl, e := tmpl.ParseFiles(which) - if e != nil { - } - // m.Confm(ice.WEB_SERVE, ice.Meta("template", "list"), func(index int, value string) { tmpl = template.Must(tmpl.Parse(value)) }) - return tmpl -} -func (web *Frame) 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.Cost("%s %v %v", r.URL.Path, msg.Optionv("cmds"), msg.Format("append")) }() - if u, e := url.Parse(r.Header.Get("Referer")); e == nil { - for k, v := range u.Query() { - msg.Logs("refer", k, v) - msg.Option(k, v) - } - } - - // 用户请求 - msg.Option(ice.MSG_USERWEB, m.Conf(ice.WEB_SHARE, "meta.domain")) - msg.Option(ice.MSG_USERIP, r.Header.Get(ice.MSG_USERIP)) - msg.Option(ice.MSG_USERUA, r.Header.Get("User-Agent")) - msg.Option(ice.MSG_USERURL, r.URL.Path) - if msg.R, msg.W = r, w; r.Header.Get("X-Real-Port") != "" { - msg.Option(ice.MSG_USERADDR, msg.Option(ice.MSG_USERIP)+":"+r.Header.Get("X-Real-Port")) - } else { - msg.Option(ice.MSG_USERADDR, r.RemoteAddr) - } - - // 请求变量 - msg.Option(ice.MSG_SESSID, "") - msg.Option(ice.MSG_OUTPUT, "") - for _, v := range r.Cookies() { - msg.Option(v.Name, v.Value) - } - - // 解析引擎 - switch r.Header.Get("Content-Type") { - case "application/json": - var data interface{} - if e := json.NewDecoder(r.Body).Decode(&data); !msg.Warn(e != nil, "%s", e) { - msg.Optionv(ice.MSG_USERDATA, data) - msg.Info("%s", kit.Formats(data)) - } - - switch d := data.(type) { - case map[string]interface{}: - for k, v := range d { - msg.Optionv(k, v) - } - } - default: - r.ParseMultipartForm(kit.Int64(kit.Select(r.Header.Get("Content-Length"), "4096"))) - if r.ParseForm(); len(r.PostForm) > 0 { - for k, v := range r.PostForm { - msg.Logs("form", k, v) - } - } - } - - // 请求参数 - for k, v := range r.Form { - if msg.Optionv(k, v); k == ice.MSG_SESSID { - msg.Render("cookie", v[0]) - } - } - - // 请求命令 - if msg.Option(ice.MSG_USERPOD, msg.Option("pod")); msg.Optionv("cmds") == nil { - if p := strings.TrimPrefix(msg.Option(ice.MSG_USERURL), key); p != "" { - msg.Optionv("cmds", strings.Split(p, "/")) - } - } - - // 执行命令 - if cmds := kit.Simple(msg.Optionv("cmds")); web.Login(msg, w, r) { - msg.Option("_option", msg.Optionv(ice.MSG_OPTION)) - msg.Target().Run(msg, cmd, msg.Option(ice.MSG_USERURL), cmds...) - } - - // 渲染引擎 - _args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{}) - Render(msg, msg.Option(ice.MSG_OUTPUT), _args...) - }) - }) -} -func (web *Frame) ServeHTTP(w http.ResponseWriter, r *http.Request) { - m, index := web.m, r.Header.Get("index.module") == "" - if index { - // 解析地址 - if ip := r.Header.Get("X-Forwarded-For"); ip != "" { - r.Header.Set(ice.MSG_USERIP, ip) - } else if ip := r.Header.Get("X-Real-Ip"); ip != "" { - r.Header.Set(ice.MSG_USERIP, ip) - } else if strings.HasPrefix(r.RemoteAddr, "[") { - r.Header.Set(ice.MSG_USERIP, strings.Split(r.RemoteAddr, "]")[0][1:]) - } else { - r.Header.Set(ice.MSG_USERIP, strings.Split(r.RemoteAddr, ":")[0]) - } - m.Info("").Info("%s %s %s", r.Header.Get(ice.MSG_USERIP), r.Method, r.URL) - - // 解析地址 - r.Header.Set("index.module", "some") - r.Header.Set("index.path", r.URL.Path) - r.Header.Set("index.url", r.URL.String()) - } - - if index && kit.Right(m.Conf(ice.WEB_SERVE, "meta.logheaders")) { - // 请求参数 - for k, v := range r.Header { - m.Info("%s: %v", k, kit.Format(v)) - } - m.Info(" ") - } - - if strings.HasPrefix(r.URL.Path, "/debug") { - r.URL.Path = strings.Replace(r.URL.Path, "/debug", "/code", -1) - } - - if r.URL.Path == "/" && m.Conf(ice.WEB_SERVE, "meta.init") != "true" { - if _, e := os.Stat(m.Conf(ice.WEB_SERVE, "meta.volcanos.path")); e == nil { - // 初始化成功 - m.Conf(ice.WEB_SERVE, "meta.init", "true") - } - m.W = w - Render(m, "refresh", m.Conf(ice.WEB_SERVE, "meta.volcanos.refresh")) - m.Event(ice.SYSTEM_INIT) - m.W = nil - } else if r.URL.Path == "/share" && r.Method == "GET" { - http.ServeFile(w, r, m.Conf(ice.WEB_SERVE, "meta.page.share")) - - // } else if r.URL.Path == "/" && r.Method == "GET" { - // http.ServeFile(w, r, m.Conf(ice.WEB_SERVE, "meta.page.index")) - // - } else { - web.ServeMux.ServeHTTP(w, r) - } - - if index && kit.Right(m.Conf(ice.WEB_SERVE, "meta.logheaders")) { - // 响应参数 - for k, v := range w.Header() { - m.Info("%s: %v", k, kit.Format(v)) - } - m.Info(" ") - } -} func (web *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server { return &Frame{} @@ -417,7 +109,7 @@ func (web *Frame) Start(m *ice.Message, arg ...string) bool { // 静态路由 msg := m.Spawns(s) - m.Confm(ice.WEB_SERVE, "meta.static", func(key string, value string) { + m.Confm(SERVE, "meta.static", func(key string, value string) { m.Log("route", "%s <- %s <- %s", s.Name, key, value) w.Handle(key, http.StripPrefix(key, http.FileServer(http.Dir(value)))) }) @@ -433,7 +125,7 @@ func (web *Frame) Start(m *ice.Message, arg ...string) bool { m.Travel(func(p *ice.Context, sub *ice.Context, k string, x *ice.Command) { if s == sub && k[0] == '/' { msg.Log("route", "%s <- %s", s.Name, k) - w.HandleCmd(msg, k, x) + Trans(w, msg, k, x) } }) } @@ -460,41 +152,15 @@ func (web *Frame) Close(m *ice.Message, arg ...string) bool { } var Index = &ice.Context{Name: "web", Help: "网络模块", - Caches: map[string]*ice.Cache{}, - Configs: map[string]*ice.Config{ - ice.WEB_SPIDE: {Name: "spide", Help: "蜘蛛侠", Value: kit.Data(kit.MDB_SHORT, "client.name")}, - ice.WEB_SERVE: {Name: "serve", Help: "服务器", Value: kit.Data( - "title", "github.com/shylinux/contexts", - "legal", []interface{}{`shylinuxc@gmail.com`}, - "page", kit.Dict( - "index", "usr/volcanos/page/index.html", - "share", "usr/volcanos/page/share.html", - ), - "static", kit.Dict("/", "usr/volcanos/"), - "publish", "usr/publish/", - "volcanos", kit.Dict("path", "usr/volcanos", "branch", "master", - "repos", "https://github.com/shylinux/volcanos", - "require", ".ish/pluged", - "refresh", "5", - ), - "template", kit.Dict("path", "usr/template", "list", []interface{}{ - `{{define "raw"}}{{.Result}}{{end}}`, - }), - "logheaders", "false", "init", "false", - "black", kit.Dict(), - )}, - ice.WEB_DREAM: {Name: "dream", Help: "梦想家", Value: kit.Data("path", "usr/local/work", - // "cmd", []interface{}{ice.CLI_SYSTEM, "ice.sh", "start", ice.WEB_SPACE, "connect"}, - "cmd", []interface{}{ice.CLI_SYSTEM, "ice.bin", ice.WEB_SPACE, "connect"}, - )}, - }, + Caches: map[string]*ice.Cache{}, + Configs: map[string]*ice.Config{}, Commands: map[string]*ice.Command{ ice.ICE_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() - m.Cmd(ice.WEB_SPIDE, "add", "self", kit.Select("http://:9020", m.Conf(ice.CLI_RUNTIME, "conf.ctx_self"))) - m.Cmd(ice.WEB_SPIDE, "add", "dev", kit.Select("http://:9020", m.Conf(ice.CLI_RUNTIME, "conf.ctx_dev"))) - m.Cmd(ice.WEB_SPIDE, "add", "shy", kit.Select("https://shylinux.com:443", m.Conf(ice.CLI_RUNTIME, "conf.ctx_shy"))) + SpideCreate(m, "self", kit.Select("http://:9020", m.Conf(ice.CLI_RUNTIME, "conf.ctx_self"))) + SpideCreate(m, "dev", kit.Select("http://:9020", m.Conf(ice.CLI_RUNTIME, "conf.ctx_dev"))) + SpideCreate(m, "shy", kit.Select("https://shylinux.com:443", m.Conf(ice.CLI_RUNTIME, "conf.ctx_shy"))) m.Cmd(ice.APP_SEARCH, "add", "favor", "base", m.AddCmd(&ice.Command{Name: "search word", Help: "搜索引擎", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { switch arg[0] { @@ -594,332 +260,7 @@ var Index = &ice.Context{Name: "web", Help: "网络模块", } }) }}, - - ice.WEB_SPIDE: {Name: "spide name=auto [action:select=msg|raw|cache] [method:select=POST|GET] url [format:select=json|form|part|data|file] arg... auto", Help: "蜘蛛侠", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) == 0 || arg[0] == "" { - // 爬虫列表 - m.Richs(ice.WEB_SPIDE, nil, "*", func(key string, value map[string]interface{}) { - m.Push(key, value["client"], []string{"name", "share", "login", "method", "url"}) - }) - m.Sort("name") - return - } - if len(arg) == 1 || len(arg) > 3 && arg[3] == "" { - // 爬虫详情 - m.Richs(ice.WEB_SPIDE, nil, arg[0], func(key string, value map[string]interface{}) { - m.Push("detail", value) - if kit.Value(value, "client.share") != nil { - m.Push("key", "share") - m.Push("value", fmt.Sprintf(m.Conf(ice.WEB_SHARE, "meta.template.text"), m.Conf(ice.WEB_SHARE, "meta.domain"), kit.Value(value, "client.share"))) - } - }) - return - } - - switch arg[0] { - case "add": - // 添加爬虫 - if uri, e := url.Parse(arg[2]); e == nil && arg[2] != "" { - if uri.Host == "random" { - uri.Host = ":" + m.Cmdx("tcp.getport") - arg[2] = strings.Replace(arg[2], "random", uri.Host, -1) - } - dir, file := path.Split(uri.EscapedPath()) - if m.Richs(ice.WEB_SPIDE, nil, arg[1], func(key string, value map[string]interface{}) { - // kit.Value(value, "client.name", arg[1]) - // kit.Value(value, "client.text", arg[2]) - kit.Value(value, "client.hostname", uri.Host) - kit.Value(value, "client.url", arg[2]) - }) == nil { - m.Rich(ice.WEB_SPIDE, nil, kit.Dict( - "cookie", kit.Dict(), "header", kit.Dict(), "client", kit.Dict( - "share", m.Cmdx(ice.WEB_SHARE, ice.TYPE_SPIDE, arg[1], arg[2]), - // "type", "POST", "name", arg[1], "text", arg[2], - "name", arg[1], "url", arg[2], "method", "POST", - "protocol", uri.Scheme, "hostname", uri.Host, - "path", dir, "file", file, "query", uri.RawQuery, - "timeout", "100s", "logheaders", false, - ), - )) - } - m.Log(ice.LOG_CREATE, "%s: %v", arg[1], arg[2:]) - } - return - case "login": - m.Richs(ice.WEB_SPIDE, nil, arg[1], func(key string, value map[string]interface{}) { - msg := m.Cmd(ice.WEB_SPIDE, arg[1], "msg", "/route/login", "login") - if msg.Append(ice.MSG_USERNAME) != "" { - m.Echo(msg.Append(ice.MSG_USERNAME)) - return - } - if msg.Result() != "" { - kit.Value(value, "client.login", msg.Result()) - kit.Value(value, "client.share", m.Cmdx(ice.WEB_SHARE, ice.TYPE_SPIDE, arg[1], - kit.Format("%s?sessid=%s", kit.Value(value, "client.url"), kit.Value(value, "cookie.sessid")))) - } - m.Render(ice.RENDER_QRCODE, kit.Dict( - kit.MDB_TYPE, "login", kit.MDB_NAME, arg[1], - kit.MDB_TEXT, kit.Value(value, "cookie.sessid"), - )) - }) - return - } - - m.Richs(ice.WEB_SPIDE, nil, arg[0], func(key string, value map[string]interface{}) { - client := value["client"].(map[string]interface{}) - // 缓存数据 - cache := "" - switch arg[1] { - case "raw": - cache, arg = arg[1], arg[1:] - case "msg": - cache, arg = arg[1], arg[1:] - case "cache": - cache, arg = arg[1], arg[1:] - } - - // 请求方法 - method := kit.Select("POST", client["method"]) - switch arg = arg[1:]; arg[0] { - case "POST": - method, arg = "POST", arg[1:] - case "GET": - method, arg = "GET", arg[1:] - } - - // 请求地址 - uri, arg := arg[0], arg[1:] - - // 渲染引擎 - head := map[string]string{} - body, ok := m.Optionv("body").(io.Reader) - if !ok && len(arg) > 0 && method != "GET" { - switch arg[0] { - case "file": - if f, e := os.Open(arg[1]); m.Warn(e != nil, "%s", e) { - return - } else { - defer f.Close() - body, arg = f, arg[2:] - } - case "data": - body, arg = bytes.NewBufferString(arg[1]), arg[2:] - case "part": - buf := &bytes.Buffer{} - mp := multipart.NewWriter(buf) - for i := 1; i < len(arg)-1; i += 2 { - if strings.HasPrefix(arg[i+1], "@") { - if f, e := os.Open(arg[i+1][1:]); m.Assert(e) { - defer f.Close() - if p, e := mp.CreateFormFile(arg[i], path.Base(arg[i+1][1:])); m.Assert(e) { - io.Copy(p, f) - } - } - } else { - mp.WriteField(arg[i], arg[i+1]) - } - } - mp.Close() - head["Content-Type"] = mp.FormDataContentType() - body = buf - case "form": - data := []string{} - for i := 1; i < len(arg)-1; i += 2 { - data = append(data, url.QueryEscape(arg[i])+"="+url.QueryEscape(arg[i+1])) - } - body = bytes.NewBufferString(strings.Join(data, "&")) - head["Content-Type"] = "application/x-www-form-urlencoded" - case "json": - arg = arg[1:] - fallthrough - default: - data := map[string]interface{}{} - for i := 0; i < len(arg)-1; i += 2 { - kit.Value(data, arg[i], arg[i+1]) - } - if b, e := json.Marshal(data); m.Assert(e) { - head["Content-Type"] = "application/json" - body = bytes.NewBuffer(b) - } - m.Log(ice.LOG_EXPORT, "json: %s", kit.Format(data)) - } - arg = arg[:0] - } else { - body = bytes.NewBuffer([]byte{}) - } - - // 请求地址 - uri = kit.MergeURL2(kit.Format(client["url"]), uri, arg) - req, e := http.NewRequest(method, uri, body) - m.Info("%s %s", req.Method, req.URL) - m.Assert(e) - - // 请求变量 - kit.Fetch(value["cookie"], func(key string, value string) { - req.AddCookie(&http.Cookie{Name: key, Value: value}) - m.Info("%s: %s", key, value) - }) - kit.Fetch(value["header"], func(key string, value string) { - req.Header.Set(key, value) - }) - list := kit.Simple(m.Optionv("header")) - for i := 0; i < len(list)-1; i += 2 { - req.Header.Set(list[i], list[i+1]) - } - for k, v := range head { - req.Header.Set(k, v) - } - - // 请求代理 - web := m.Target().Server().(*Frame) - if web.Client == nil { - web.Client = &http.Client{Timeout: kit.Duration(kit.Format(client["timeout"]))} - } - if method == "POST" { - m.Info("%s: %s", req.Header.Get("Content-Length"), req.Header.Get("Content-Type")) - } - - // 发送请求 - res, e := web.Client.Do(req) - if m.Warn(e != nil, "%s", e) { - return - } - - // 检查结果 - if m.Cost("%s %s: %s", res.Status, res.Header.Get("Content-Length"), res.Header.Get("Content-Type")); m.Warn(res.StatusCode != http.StatusOK, "%s", res.Status) { - return - } - - // 缓存变量 - for _, v := range res.Cookies() { - kit.Value(value, "cookie."+v.Name, v.Value) - m.Log(ice.LOG_IMPORT, "%s: %s", v.Name, v.Value) - } - - // 解析引擎 - switch cache { - case "cache": - m.Optionv("response", res) - m.Cmdy(ice.WEB_CACHE, "download", res.Header.Get("Content-Type"), uri) - m.Echo(m.Append("data")) - case "raw": - if b, e := ioutil.ReadAll(res.Body); m.Assert(e) { - m.Echo(string(b)) - } - case "msg": - var data map[string][]string - m.Assert(json.NewDecoder(res.Body).Decode(&data)) - m.Info("res: %s", kit.Formats(data)) - for _, k := range data[ice.MSG_APPEND] { - for i := range data[k] { - m.Push(k, data[k][i]) - } - } - m.Resultv(data[ice.MSG_RESULT]) - default: - if strings.HasPrefix(res.Header.Get("Content-Type"), "text/html") { - b, _ := ioutil.ReadAll(res.Body) - m.Echo(string(b)) - break - } - - var data interface{} - m.Assert(json.NewDecoder(res.Body).Decode(&data)) - data = kit.KeyValue(map[string]interface{}{}, "", data) - m.Info("res: %s", kit.Formats(data)) - m.Push("", data) - } - }) - }}, - ice.WEB_SERVE: {Name: "serve [random] [ups...]", Help: "服务器", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - m.Conf(ice.CLI_RUNTIME, "node.name", m.Conf(ice.CLI_RUNTIME, "boot.hostname")) - m.Conf(ice.CLI_RUNTIME, "node.type", ice.WEB_SERVER) - - if len(arg) > 0 && arg[0] == "random" { - // 随机端口 - m.Conf(ice.CLI_RUNTIME, "node.name", m.Conf(ice.CLI_RUNTIME, "boot.pathname")) - m.Cmd(ice.WEB_SPIDE, "add", "self", "http://random") - arg = arg[1:] - } - - // 启动服务 - m.Target().Start(m, "self") - m.Sleep("1s") - - // 连接服务 - m.Cmd(ice.WEB_SPACE, "connect", "self") - for _, k := range arg { - m.Cmd(ice.WEB_SPACE, "connect", k) - } - - m.Watch(ice.SYSTEM_INIT, "web.code.git.repos", "volcanos", m.Conf(ice.WEB_SERVE, "meta.volcanos.path"), - m.Conf(ice.WEB_SERVE, "meta.volcanos.repos"), m.Conf(ice.WEB_SERVE, "meta.volcanos.branch")) - m.Conf(ice.WEB_FAVOR, "meta.template", favor_template) - m.Conf(ice.WEB_SHARE, "meta.template", share_template) - }}, - ice.WEB_DREAM: {Name: "dream name auto", Help: "梦想家", Meta: kit.Dict( - "exports", []string{"you", "name"}, "detail", []interface{}{"启动", "停止"}, - ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) > 1 && arg[0] == "action" { - switch arg[1] { - case "启动", "start": - arg = []string{arg[4]} - case "停止", "stop": - m.Cmd(ice.WEB_SPACE, kit.Select(m.Option("name"), arg, 4), "exit", "1") - m.Event(ice.DREAM_CLOSE, arg[4]) - return - } - } - - if len(arg) == 0 { - // 任务列表 - m.Cmdy("nfs.dir", m.Conf(ice.WEB_DREAM, "meta.path"), "time name") - m.Table(func(index int, value map[string]string, head []string) { - if m.Richs(ice.WEB_SPACE, nil, value["name"], func(key string, value map[string]interface{}) { - m.Push("type", value["type"]) - m.Push("status", "start") - }) == nil { - m.Push("type", "none") - m.Push("status", "stop") - } - }) - m.Sort("name") - m.Sort("status") - return - } - - // 规范命名 - if !strings.Contains(arg[0], "-") || !strings.HasPrefix(arg[0], "20") { - arg[0] = m.Time("20060102-") + arg[0] - } - - // 创建目录 - p := path.Join(m.Conf(ice.WEB_DREAM, "meta.path"), arg[0]) - os.MkdirAll(p, 0777) - - if b, e := ioutil.ReadFile(path.Join(p, m.Conf(ice.GDB_SIGNAL, "meta.pid"))); e == nil { - if s, e := os.Stat("/proc/" + string(b)); e == nil && s.IsDir() { - m.Info("already exists %v", string(b)) - return - } - } - - if m.Richs(ice.WEB_SPACE, nil, arg[0], nil) == nil { - // 启动任务 - m.Option("cmd_dir", p) - m.Option("cmd_type", "daemon") - m.Optionv("cmd_env", - "ctx_dev", m.Conf(ice.CLI_RUNTIME, "conf.ctx_dev"), - "ctx_log", "boot.log", "ctx_mod", "ctx,log,gdb,ssh", - "PATH", kit.Path(path.Join(p, "bin"))+":"+os.Getenv("PATH"), - ) - m.Cmd(m.Confv(ice.WEB_DREAM, "meta.cmd"), "self", arg[0]) - time.Sleep(time.Second * 1) - m.Event(ice.DREAM_START, arg...) - } - m.Cmdy("nfs.dir", p) - }}, }, } -func init() { ice.Index.Register(Index, &Frame{}) } +func init() { ice.Index.Register(Index, &Frame{}, SERVE) } diff --git a/misc/lark/lark.go b/misc/lark/lark.go index 831933bd..e0b229d6 100644 --- a/misc/lark/lark.go +++ b/misc/lark/lark.go @@ -66,7 +66,7 @@ var Index = &ice.Context{Name: "lark", Help: "机器人", Commands: map[string]*ice.Command{ ice.ICE_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() - m.Cmd(ice.WEB_SPIDE, "add", LARK, m.Conf(APP, "meta.lark")) + web.SpideCreate(m, LARK, m.Conf(APP, "meta.lark")) m.Cmd(DUTY, "boot", m.Conf(ice.CLI_RUNTIME, "boot.hostname"), m.Time()) }}, ice.ICE_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { diff --git a/misc/mp/mp.go b/misc/mp/mp.go index 721b5adc..0ba088fa 100644 --- a/misc/mp/mp.go +++ b/misc/mp/mp.go @@ -22,7 +22,7 @@ var Index = &ice.Context{Name: "mp", Help: "小程序", Commands: map[string]*ice.Command{ ice.ICE_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() - m.Cmd(ice.WEB_SPIDE, "add", "weixin", m.Conf("login", "meta.weixin")) + web.SpideCreate(m, "weixin", m.Conf("login", "meta.weixin")) m.Confm("login", "meta.userrole", func(key string, value string) { m.Cmd(ice.AAA_ROLE, value, key) }) diff --git a/misc/railway/railway.go b/misc/railway/railway.go index c9ca6e5d..ab34360b 100644 --- a/misc/railway/railway.go +++ b/misc/railway/railway.go @@ -14,7 +14,7 @@ var Index = &ice.Context{Name: "railway", Help: "railway", Commands: map[string]*ice.Command{ ice.ICE_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() - m.Cmd(ice.WEB_SPIDE, "add", "12306", m.Conf("railway", "meta.site")) + web.SpideCreate(m, "12306", m.Conf("railway", "meta.site")) }}, ice.ICE_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Save("railway")