1
0
mirror of https://shylinux.com/x/icebergs synced 2025-04-26 01:24:05 +08:00
This commit is contained in:
shaoying 2020-06-17 22:37:35 +08:00
parent 84d5e3800e
commit 6aff305ded
18 changed files with 731 additions and 745 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()},

View File

@ -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()))

View File

@ -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{}{}

View File

@ -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":

View File

@ -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) {

View File

@ -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":

87
base/web/dream.go Normal file
View File

@ -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)
}

245
base/web/serve.go Normal file
View File

@ -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{}{`<a href="mailto:shylinuxc@gmail.com">shylinuxc@gmail.com</a>`},
"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)
}

View File

@ -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{

View File

@ -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), "")

273
base/web/spide.go Normal file
View File

@ -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)
}

View File

@ -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{}{`<a href="mailto:shylinuxc@gmail.com">shylinuxc@gmail.com</a>`},
"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) }

View File

@ -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) {

View File

@ -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)
})

View File

@ -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")