diff --git a/base/web/label.go b/base/web/label.go new file mode 100644 index 00000000..70eb28fb --- /dev/null +++ b/base/web/label.go @@ -0,0 +1,186 @@ +package web + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + + "sync" +) + +func _label_add(m *ice.Message, cmd string) { + if m.Option(cmd) != "" && m.Option(kit.MDB_GROUP) != "" && m.Option(kit.MDB_NAME) != "" { + m.Cmdy(cmd, m.Option(cmd), "add", m.Option(kit.MDB_GROUP), m.Option(kit.MDB_NAME)) + m.Option(ice.FIELD_RELOAD, "true") + } +} +func _label_del(m *ice.Message, cmd string) { + if m.Option(cmd) != "" && m.Option(kit.MDB_GROUP) != "" && m.Option(kit.MDB_NAME) != "" { + m.Cmdy(cmd, m.Option(cmd), "del", m.Option(kit.MDB_GROUP), m.Option(kit.MDB_NAME)) + m.Option(ice.FIELD_RELOAD, "true") + } +} +func _label_prune(m *ice.Message, cmd string) { + m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(sub string, value map[string]interface{}) { + if value[kit.MDB_STATUS] != "busy" { + m.Cmdy(cmd, m.Option(cmd), "del", value[kit.MDB_GROUP], value[kit.MDB_NAME]) + m.Option(ice.FIELD_RELOAD, "true") + } + }) + }) +} +func _label_clear(m *ice.Message, cmd string) { + m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(sub string, value map[string]interface{}) { + if value[kit.MDB_STATUS] == "void" { + last := m.Conf(cmd, kit.Keys(kit.MDB_HASH, key, kit.MDB_HASH, sub)) + m.Logs(ice.LOG_DELETE, cmd, m.Option(cmd), kit.MDB_NAME, value[kit.MDB_NAME], kit.MDB_VALUE, last) + m.Conf(cmd, kit.Keys(kit.MDB_HASH, key, kit.MDB_HASH, sub), "") + m.Option(ice.FIELD_RELOAD, "true") + m.Echo(last) + } + }) + }) +} +func _label_delete(m *ice.Message, cmd string) { + m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { + m.Echo(m.Conf(cmd, kit.Keys(kit.MDB_HASH, key))) + m.Logs(ice.LOG_REMOVE, cmd, m.Option(cmd), kit.MDB_VALUE, m.Conf(cmd, kit.Keys(kit.MDB_HASH, key))) + m.Conf(cmd, kit.Keys(kit.MDB_HASH, key), "") + m.Option(ice.FIELD_RELOAD, "true") + }) +} + +func _label_select(m *ice.Message, cmd string, arg ...string) { + m.Richs(cmd, nil, kit.Select("*", arg, 0), func(key string, value map[string]interface{}) { + if len(arg) < 1 { + // 一级列表 + m.Option(ice.FIELD_DETAIL, "清理", "清空", "删除") + value = value[kit.MDB_META].(map[string]interface{}) + m.Push(key, value, []string{kit.MDB_TIME}) + status := map[string]int{} + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(key string, value map[string]interface{}) { + status[kit.Format(value[kit.MDB_STATUS])]++ + }) + m.Push("count", kit.Format("%d/%d/%d", status["busy"], status["free"], status["void"])) + m.Push(key, value, []string{cmd}) + return + } + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.Select("*", arg, 1), func(key string, value map[string]interface{}) { + if len(arg) < 2 { + // 二级列表 + m.Option(ice.FIELD_DETAIL, "添加", "退还", "清理", "清空") + m.Push(key, value, []string{kit.MDB_TIME, kit.MDB_GROUP, kit.MDB_STATUS, kit.MDB_NAME}) + return + } + // 分组详情 + m.Option(ice.FIELD_DETAIL, "添加", "退还") + m.Push("detail", value) + }) + }) + if len(arg) < 1 { + m.Sort(cmd) + } else if len(arg) < 2 { + m.Sort(kit.MDB_NAME) + } +} +func _label_create(m *ice.Message, cmd string, key string, arg ...string) { + if pod := m.Cmdx(ice.WEB_GROUP, arg[2], "get", arg[3:]); pod != "" { + if m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), pod, func(key string, value map[string]interface{}) { + if value[kit.MDB_STATUS] == "void" { + value[kit.MDB_STATUS] = "free" + m.Logs(ice.LOG_MODIFY, cmd, arg[0], kit.MDB_NAME, pod, kit.MDB_STATUS, value[kit.MDB_STATUS]) + } + }) == nil { + m.Logs(ice.LOG_INSERT, cmd, arg[0], kit.MDB_NAME, pod) + m.Rich(cmd, kit.Keys(kit.MDB_HASH, key), kit.Dict( + kit.MDB_NAME, pod, kit.MDB_GROUP, arg[2], kit.MDB_STATUS, "free", + )) + } + m.Echo(arg[0]) + } +} +func _label_remove(m *ice.Message, cmd string, key string, arg ...string) { + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), arg[3], func(sub string, value map[string]interface{}) { + if value[kit.MDB_STATUS] == "free" { + value[kit.MDB_STATUS] = "void" + m.Logs(ice.LOG_MODIFY, cmd, arg[0], kit.MDB_NAME, arg[3], kit.MDB_STATUS, "void") + m.Cmdx(ice.WEB_GROUP, value[kit.MDB_GROUP], "put", arg[3]) + m.Echo(arg[3]) + } + }) +} +func _label_remote(m *ice.Message, cmd string, key string, arg ...string) { + wg := &sync.WaitGroup{} + m.Option("_async", "true") + m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), arg[1], func(key string, value map[string]interface{}) { + wg.Add(1) + m.Option(ice.MSG_USERPOD, value[kit.MDB_NAME]) + m.Cmd(ice.WEB_SPACE, value[kit.MDB_NAME], arg[2:]).Call(false, func(res *ice.Message) *ice.Message { + if wg.Done(); res != nil && m != nil { + m.Copy(res) + } + return nil + }) + }) + wg.Wait() +} + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + ice.WEB_LABEL: {Name: "label", Help: "标签", Value: kit.Data(kit.MDB_SHORT, "label")}, + }, + Commands: map[string]*ice.Command{ + ice.WEB_LABEL: {Name: "label label=auto name=auto auto", Help: "标签", Meta: kit.Dict( + "exports", []string{"lab", "label"}, "detail", []string{"归还"}, + ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) > 1 && arg[0] == "action" { + switch arg[1] { + case "add", "添加": + _label_add(m, cmd) + case "del", "退还": + _label_del(m, cmd) + case "prune", "清理": + _label_prune(m, cmd) + case "clear", "清空": + _label_clear(m, cmd) + case "delete", "删除": + _label_delete(m, cmd) + } + return + } + + if len(arg) < 3 { + // 查询分组 + _label_select(m, cmd, arg...) + return + } + + if m.Richs(cmd, nil, arg[0], nil) == nil { + // 添加分组 + m.Logs(ice.LOG_CREATE, cmd, m.Rich(cmd, nil, kit.Data( + kit.MDB_SHORT, kit.MDB_NAME, cmd, arg[0], + ))) + } + + m.Richs(cmd, nil, arg[0], func(key string, value map[string]interface{}) { + switch arg[1] { + case "add": // 添加设备 + _label_create(m, cmd, key, arg...) + case "del": // 删除设备 + _label_remove(m, cmd, key, arg...) + default: // 远程命令 + if arg[0] == "route" { + m.Cmd(ice.WEB_ROUTE).Table(func(index int, value map[string]string, field []string) { + m.Rich(cmd, kit.Keys(kit.MDB_HASH, key), kit.Dict( + kit.MDB_NAME, value["name"], kit.MDB_GROUP, arg[0], kit.MDB_STATUS, "free", + )) + }) + } + _label_remote(m, cmd, key, arg...) + } + }) + }}, + }}, nil) +} diff --git a/base/web/route.go b/base/web/route.go new file mode 100644 index 00000000..cb09c9c2 --- /dev/null +++ b/base/web/route.go @@ -0,0 +1,96 @@ +package web + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + + "strings" +) + +func _route_split(arg ...string) (string, string) { + target, rest := "*", "" + if len(arg) > 0 { + ls := strings.SplitN(arg[0], ".", 2) + if target = ls[0]; len(ls) > 1 { + rest = ls[1] + } + } + return target, rest +} + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + ice.WEB_ROUTE: {Name: "route", Help: "路由", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME)}, + }, + Commands: map[string]*ice.Command{ + ice.WEB_ROUTE: {Name: "route name cmd auto", Help: "路由", Meta: kit.Dict("detail", []string{"分组"}), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) > 1 && arg[0] == "action" { + switch arg[1] { + case "group", "分组": + if m.Option("grp") != "" && m.Option("name") != "" { + m.Cmdy(ice.WEB_GROUP, m.Option("grp"), "add", m.Option("name")) + } + } + return + } + + target, rest := _route_split(arg...) + m.Richs(ice.WEB_SPACE, nil, target, func(key string, val map[string]interface{}) { + if len(arg) > 1 { + m.Call(false, func(res *ice.Message) *ice.Message { return res }) + ls := []interface{}{ice.WEB_SPACE, val[kit.MDB_NAME]} + // 发送命令 + if rest != "" { + ls = append(ls, ice.WEB_SPACE, rest) + } + m.Cmdy(ls, arg[1:]) + return + } + + switch val[kit.MDB_TYPE] { + case ice.WEB_SERVER: + if val[kit.MDB_NAME] == m.Conf(ice.CLI_RUNTIME, "node.name") { + // 避免循环 + return + } + + // 远程查询 + m.Cmd(ice.WEB_SPACE, val[kit.MDB_NAME], ice.WEB_ROUTE).Table(func(index int, value map[string]string, head []string) { + m.Push(kit.MDB_TYPE, value[kit.MDB_TYPE]) + m.Push(kit.MDB_NAME, kit.Keys(val[kit.MDB_NAME], value[kit.MDB_NAME])) + }) + fallthrough + case ice.WEB_WORKER: + // 本机查询 + m.Push(kit.MDB_TYPE, val[kit.MDB_TYPE]) + m.Push(kit.MDB_NAME, val[kit.MDB_NAME]) + } + }) + if m.W != nil { + m.Table(func(index int, value map[string]string, field []string) { + m.Push("link", Format("a", kit.MergeURL(m.Option(ice.MSG_USERWEB), "pod", value["name"]), value["name"])) + }) + } + }}, + "/route/": {Name: "/route/", Help: "路由器", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + switch arg[0] { + case "login": + if m.Option(ice.MSG_USERNAME) != "" { + m.Push(ice.MSG_USERNAME, m.Option(ice.MSG_USERNAME)) + m.Info("username: %v", m.Option(ice.MSG_USERNAME)) + break + } + if m.Option(ice.MSG_SESSID) != "" && m.Cmdx(ice.AAA_SESS, "check", m.Option(ice.MSG_SESSID)) != "" { + m.Info("sessid: %v", m.Option(ice.MSG_SESSID)) + break + } + + sessid := m.Cmdx(ice.AAA_SESS, "create", "") + share := m.Cmdx(ice.WEB_SHARE, "add", "login", m.Option(ice.MSG_USERIP), sessid) + Render(m, "cookie", sessid) + m.Render(share) + } + }}, + }}, nil) +} diff --git a/base/web/story.go b/base/web/story.go new file mode 100644 index 00000000..68ccdd7f --- /dev/null +++ b/base/web/story.go @@ -0,0 +1,386 @@ +package web + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + + "os" + "path" + "time" +) + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + ice.WEB_STORY: {Name: "story", Help: "故事会", Value: kit.Dict( + kit.MDB_META, kit.Dict(kit.MDB_SHORT, "data"), + "head", kit.Data(kit.MDB_SHORT, "story"), + "mime", kit.Dict("md", "txt"), + )}, + }, + Commands: map[string]*ice.Command{ + ice.WEB_STORY: {Name: "story story=auto key=auto auto", Help: "故事会", Meta: kit.Dict( + "exports", []string{"top", "story"}, "detail", []string{"共享", "更新", "推送"}, + ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) > 1 && arg[0] == "action" { + story, list := m.Option("story"), m.Option("list") + switch arg[2] { + case "story": + story = arg[3] + case "list": + list = arg[3] + } + + switch arg[1] { + case "share", "共享": + if m.Echo("share: "); list == "" { + msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, story) + m.Cmdy(ice.WEB_SHARE, "add", "story", story, msg.Append("list")) + } else { + msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, list) + m.Cmdy(ice.WEB_SHARE, "add", msg.Append("scene"), msg.Append("story"), msg.Append("text")) + } + } + return + } + + if len(arg) == 0 { + // 故事列表 + m.Richs(ice.WEB_STORY, "head", "*", func(key string, value map[string]interface{}) { + m.Push(key, value, []string{"time", "story", "count"}) + }) + m.Sort("time", "time_r") + return + } + + switch arg[0] { + case ice.STORY_PULL: // story [spide [story]] + // 起止节点 + prev, begin, end := "", arg[3], "" + repos := kit.Keys("remote", arg[2], arg[3]) + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { + end = kit.Format(kit.Value(val, kit.Keys(repos, "pull", "list"))) + prev = kit.Format(val["list"]) + }) + + pull := end + var first map[string]interface{} + for begin != "" && begin != end { + if m.Cmd(ice.WEB_SPIDE, arg[2], "msg", "/story/pull", "begin", begin, "end", end).Table(func(index int, value map[string]string, head []string) { + if m.Richs(ice.WEB_CACHE, nil, value["data"], nil) == nil { + m.Log(ice.LOG_IMPORT, "%v: %v", value["data"], value["save"]) + if node := kit.UnMarshal(value["save"]); kit.Format(kit.Value(node, "file")) != "" { + // 下载文件 + m.Cmd(ice.WEB_SPIDE, arg[2], "cache", "GET", "/story/download/"+value["data"]) + } else { + // 导入缓存 + m.Conf(ice.WEB_CACHE, kit.Keys("hash", value["data"]), node) + } + } + + node := kit.UnMarshal(value["node"]).(map[string]interface{}) + if m.Richs(ice.WEB_STORY, nil, value["list"], nil) == nil { + // 导入节点 + m.Log(ice.LOG_IMPORT, "%v: %v", value["list"], value["node"]) + m.Conf(ice.WEB_STORY, kit.Keys("hash", value["list"]), node) + } + + if first == nil { + if m.Richs(ice.WEB_STORY, "head", arg[1], nil) == nil { + // 自动创建 + h := m.Rich(ice.WEB_STORY, "head", kit.Dict( + "scene", node["scene"], "story", arg[1], + "count", node["count"], "list", value["list"], + )) + m.Log(ice.LOG_CREATE, "%v: %v", h, node["story"]) + } + + pull, first = kit.Format(value["list"]), node + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { + prev = kit.Format(val["list"]) + if kit.Int(node["count"]) > kit.Int(kit.Value(val, kit.Keys(repos, "pull", "count"))) { + // 更新分支 + m.Log(ice.LOG_IMPORT, "%v: %v", arg[2], pull) + kit.Value(val, kit.Keys(repos, "pull"), kit.Dict( + "head", arg[3], "time", node["time"], "list", pull, "count", node["count"], + )) + } + }) + } + + if prev == kit.Format(node["prev"]) || prev == kit.Format(node["push"]) { + // 快速合并 + m.Log(ice.LOG_IMPORT, "%v: %v", pull, arg[2]) + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { + val["count"] = first["count"] + val["time"] = first["time"] + val["list"] = pull + }) + prev = pull + } + + begin = kit.Format(node["prev"]) + }).Appendv("list") == nil { + break + } + } + + case ice.STORY_PUSH: + // 更新分支 + m.Cmdx(ice.WEB_STORY, "pull", arg[1:]) + + repos := kit.Keys("remote", arg[2], arg[3]) + // 查询索引 + prev, pull, some, list := "", "", "", "" + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { + prev = kit.Format(val["list"]) + pull = kit.Format(kit.Value(val, kit.Keys(repos, "pull", "list"))) + for some = pull; prev != some && some != ""; { + local := m.Richs(ice.WEB_STORY, nil, prev, nil) + remote := m.Richs(ice.WEB_STORY, nil, some, nil) + if diff := kit.Time(kit.Format(remote["time"])) - kit.Time(kit.Format(local["time"])); diff > 0 { + some = kit.Format(remote["prev"]) + } else if diff < 0 { + prev = kit.Format(local["prev"]) + } + } + + if prev = kit.Format(val["list"]); prev == pull { + // 相同节点 + return + } + + if some != pull { + // 合并节点 + local := m.Richs(ice.WEB_STORY, nil, prev, nil) + remote := m.Richs(ice.WEB_STORY, nil, pull, nil) + list = m.Rich(ice.WEB_STORY, nil, kit.Dict( + "scene", val["scene"], "story", val["story"], "count", kit.Int(remote["count"])+1, + "data", local["data"], "prev", pull, "push", prev, + )) + m.Log(ice.LOG_CREATE, "merge: %s %s->%s", list, prev, pull) + val["list"] = list + prev = list + val["count"] = kit.Int(remote["count"]) + 1 + } + + // 查询节点 + nodes := []string{} + for list = prev; list != some; { + m.Richs(ice.WEB_STORY, nil, list, func(key string, value map[string]interface{}) { + nodes, list = append(nodes, list), kit.Format(value["prev"]) + }) + } + + for _, v := range kit.Revert(nodes) { + m.Richs(ice.WEB_STORY, nil, v, func(list string, node map[string]interface{}) { + m.Richs(ice.WEB_CACHE, nil, node["data"], func(data string, save map[string]interface{}) { + if kit.Format(save["file"]) != "" { + // 推送缓存 + m.Cmd(ice.WEB_SPIDE, arg[2], "/story/upload", + "part", "upload", "@"+kit.Format(save["file"]), + ) + } + + // 推送节点 + m.Log(ice.LOG_EXPORT, "%s: %s", v, kit.Format(node)) + m.Cmd(ice.WEB_SPIDE, arg[2], "/story/push", + "story", arg[3], "list", v, "node", kit.Format(node), + "data", node["data"], "save", kit.Format(save), + ) + }) + }) + } + }) + + // 更新分支 + m.Cmd(ice.WEB_STORY, "pull", arg[1:]) + + case "commit": + // 查询索引 + head, prev, value, count := "", "", map[string]interface{}{}, 0 + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { + head, prev, value, count = key, kit.Format(val["list"]), val, kit.Int(val["count"]) + m.Log("info", "head: %v prev: %v count: %v", head, prev, count) + }) + + // 提交信息 + arg[2] = m.Cmdx(ice.WEB_STORY, "add", "submit", arg[2], "hostname,username") + + // 节点信息 + menu := map[string]string{} + for i := 3; i < len(arg); i++ { + menu[arg[i]] = m.Cmdx(ice.WEB_STORY, ice.STORY_INDEX, arg[i]) + } + + // 添加节点 + list := m.Rich(ice.WEB_STORY, nil, kit.Dict( + "scene", "commit", "story", arg[1], "count", count+1, "data", arg[2], "list", menu, "prev", prev, + )) + m.Log(ice.LOG_CREATE, "commit: %s %s: %s", list, arg[1], arg[2]) + m.Push("list", list) + + if head == "" { + // 添加索引 + m.Rich(ice.WEB_STORY, "head", kit.Dict("scene", "commit", "story", arg[1], "count", count+1, "list", list)) + } else { + // 更新索引 + value["count"] = count + 1 + value["time"] = m.Time() + value["list"] = list + } + m.Echo(list) + + case ice.STORY_TRASH: + bak := kit.Select(kit.Keys(arg[1], "bak"), arg, 2) + os.Remove(bak) + os.Rename(arg[1], bak) + + case ice.STORY_WATCH: + // 备份文件 + name := kit.Select(arg[1], arg, 2) + m.Cmd(ice.WEB_STORY, ice.STORY_TRASH, name) + + if msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]); msg.Append("file") != "" { + p := path.Dir(name) + os.MkdirAll(p, 0777) + + // 导出文件 + os.Link(msg.Append("file"), name) + m.Log(ice.LOG_EXPORT, "%s: %s", msg.Append("file"), name) + } else { + if f, p, e := kit.Create(name); m.Assert(e) { + defer f.Close() + // 导出数据 + f.WriteString(msg.Append("text")) + m.Log(ice.LOG_EXPORT, "%s: %s", msg.Append("text"), p) + } + } + m.Echo(name) + + case ice.STORY_CATCH: + if last := m.Richs(ice.WEB_STORY, "head", arg[2], nil); last != nil { + if t, e := time.ParseInLocation(ice.ICE_TIME, kit.Format(last["time"]), time.Local); e == nil { + // 文件对比 + if s, e := os.Stat(arg[2]); e == nil && s.ModTime().Before(t) { + m.Info("%s last: %s", arg[2], kit.Format(t)) + m.Echo("%s", last["list"]) + break + } + } + } + fallthrough + case "add", ice.STORY_UPLOAD, ice.STORY_DOWNLOAD: + if m.Richs(ice.WEB_CACHE, nil, kit.Select("", arg, 3), func(key string, value map[string]interface{}) { + // 复用缓存 + arg[3] = key + }) == nil { + // 添加缓存 + m.Cmdy(ice.WEB_CACHE, arg) + arg = []string{arg[0], m.Append("type"), m.Append("name"), m.Append("data")} + } + + // 查询索引 + head, prev, value, count := "", "", map[string]interface{}{}, 0 + m.Richs(ice.WEB_STORY, "head", arg[2], func(key string, val map[string]interface{}) { + head, prev, value, count = key, kit.Format(val["list"]), val, kit.Int(val["count"]) + m.Log("info", "head: %v prev: %v count: %v", head, prev, count) + }) + + if last := m.Richs(ice.WEB_STORY, nil, prev, nil); prev != "" && last != nil && last["data"] == arg[3] { + // 重复提交 + m.Echo(prev) + } else { + // 添加节点 + list := m.Rich(ice.WEB_STORY, nil, kit.Dict( + "scene", arg[1], "story", arg[2], "count", count+1, "data", arg[3], "prev", prev, + )) + m.Log(ice.LOG_CREATE, "story: %s %s: %s", list, arg[1], arg[2]) + m.Push("list", list) + + if head == "" { + // 添加索引 + m.Rich(ice.WEB_STORY, "head", kit.Dict("scene", arg[1], "story", arg[2], "count", count+1, "list", list)) + } else { + // 更新索引 + value["count"] = count + 1 + value["time"] = m.Time() + value["list"] = list + } + m.Echo(list) + } + + // 分发数据 + if p := kit.Select(m.Conf(ice.WEB_FAVOR, "meta.proxy"), m.Option("you")); p != "" { + m.Info("what %v", p) + m.Option("you", "") + m.Cmd(ice.WEB_PROXY, p, ice.WEB_STORY, ice.STORY_PULL, arg[2], "dev", arg[2]) + } + + case ice.STORY_INDEX: + m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, value map[string]interface{}) { + // 查询索引 + arg[1] = kit.Format(value["list"]) + }) + + m.Richs(ice.WEB_STORY, nil, arg[1], func(key string, value map[string]interface{}) { + // 查询节点 + m.Push("list", key) + m.Push(key, value, []string{"scene", "story"}) + arg[1] = kit.Format(value["data"]) + }) + + m.Richs(ice.WEB_CACHE, nil, arg[1], func(key string, value map[string]interface{}) { + // 查询数据 + m.Push("data", key) + m.Push(key, value, []string{"text", "time", "size", "type", "name", "file"}) + m.Echo("%s", value["text"]) + }) + + case ice.STORY_HISTORY: + // 历史记录 + list := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]).Append("list") + for i := 0; i < kit.Int(kit.Select("30", m.Option("cache.limit"))) && list != ""; i++ { + + m.Richs(ice.WEB_STORY, nil, list, func(key string, value map[string]interface{}) { + // 直连节点 + m.Push(key, value, []string{"time", "key", "count", "scene", "story"}) + m.Richs(ice.WEB_CACHE, nil, value["data"], func(key string, value map[string]interface{}) { + m.Push("drama", value["text"]) + m.Push("data", key) + }) + + kit.Fetch(value["list"], func(key string, val string) { + m.Richs(ice.WEB_STORY, nil, val, func(key string, value map[string]interface{}) { + // 复合节点 + m.Push(key, value, []string{"time", "key", "count", "scene", "story"}) + m.Richs(ice.WEB_CACHE, nil, value["data"], func(key string, value map[string]interface{}) { + m.Push("drama", value["text"]) + m.Push("data", key) + }) + }) + }) + + // 切换节点 + list = kit.Format(value["prev"]) + }) + } + + default: + if len(arg) == 1 { + // 故事记录 + m.Cmdy(ice.WEB_STORY, "history", arg) + break + } + // 故事详情 + m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]).Table(func(index int, value map[string]string, head []string) { + for k, v := range value { + m.Push("key", k) + m.Push("value", v) + } + m.Sort("key") + }) + } + }}, + }}, nil) +} diff --git a/base/web/web.go b/base/web/web.go index 67529412..b37e122e 100644 --- a/base/web/web.go +++ b/base/web/web.go @@ -39,6 +39,13 @@ func Count(m *ice.Message, cmd, key, name string) int { m.Conf(cmd, kit.Keys(key, name), count+1) return count } +func Format(key string, arg ...interface{}) string { + switch args := kit.Simple(arg); key { + case "a": + return fmt.Sprintf("%s", kit.Format(args[0]), kit.Select(kit.Format(args[0]), args, 1)) + } + return "" +} func Render(msg *ice.Message, cmd string, args ...interface{}) { msg.Log(ice.LOG_EXPORT, "%s: %v", cmd, args) switch arg := kit.Simple(args...); cmd { @@ -479,16 +486,9 @@ var Index = &ice.Context{Name: "web", Help: "网络模块", ice.WEB_CACHE: {Name: "cache", Help: "缓存池", Value: kit.Data( kit.MDB_SHORT, "text", "path", "var/file", "store", "var/data", "fsize", "100000", "limit", "50", "least", "30", )}, - ice.WEB_STORY: {Name: "story", Help: "故事会", Value: kit.Dict( - kit.MDB_META, kit.Dict(kit.MDB_SHORT, "data"), - "head", kit.Data(kit.MDB_SHORT, "story"), - "mime", kit.Dict("md", "txt"), - )}, - ice.WEB_ROUTE: {Name: "route", Help: "路由", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME)}, ice.WEB_PROXY: {Name: "proxy", Help: "代理", Value: kit.Data(kit.MDB_SHORT, "proxy")}, ice.WEB_GROUP: {Name: "group", Help: "分组", Value: kit.Data(kit.MDB_SHORT, "group")}, - ice.WEB_LABEL: {Name: "label", Help: "标签", Value: kit.Data(kit.MDB_SHORT, "label")}, }, Commands: map[string]*ice.Command{ ice.ICE_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { @@ -1359,420 +1359,7 @@ var Index = &ice.Context{Name: "web", Help: "网络模块", m.Echo(arg[2]) } }}, - ice.WEB_STORY: {Name: "story story=auto key=auto auto", Help: "故事会", Meta: kit.Dict( - "exports", []string{"top", "story"}, "detail", []string{"共享", "更新", "推送"}, - ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) > 1 && arg[0] == "action" { - story, list := m.Option("story"), m.Option("list") - switch arg[2] { - case "story": - story = arg[3] - case "list": - list = arg[3] - } - switch arg[1] { - case "share", "共享": - if m.Echo("share: "); list == "" { - msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, story) - m.Cmdy(ice.WEB_SHARE, "add", "story", story, msg.Append("list")) - } else { - msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, list) - m.Cmdy(ice.WEB_SHARE, "add", msg.Append("scene"), msg.Append("story"), msg.Append("text")) - } - } - return - } - - if len(arg) == 0 { - // 故事列表 - m.Richs(ice.WEB_STORY, "head", "*", func(key string, value map[string]interface{}) { - m.Push(key, value, []string{"time", "story", "count"}) - }) - m.Sort("time", "time_r") - return - } - - switch arg[0] { - case ice.STORY_PULL: // story [spide [story]] - // 起止节点 - prev, begin, end := "", arg[3], "" - repos := kit.Keys("remote", arg[2], arg[3]) - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { - end = kit.Format(kit.Value(val, kit.Keys(repos, "pull", "list"))) - prev = kit.Format(val["list"]) - }) - - pull := end - var first map[string]interface{} - for begin != "" && begin != end { - if m.Cmd(ice.WEB_SPIDE, arg[2], "msg", "/story/pull", "begin", begin, "end", end).Table(func(index int, value map[string]string, head []string) { - if m.Richs(ice.WEB_CACHE, nil, value["data"], nil) == nil { - m.Log(ice.LOG_IMPORT, "%v: %v", value["data"], value["save"]) - if node := kit.UnMarshal(value["save"]); kit.Format(kit.Value(node, "file")) != "" { - // 下载文件 - m.Cmd(ice.WEB_SPIDE, arg[2], "cache", "GET", "/story/download/"+value["data"]) - } else { - // 导入缓存 - m.Conf(ice.WEB_CACHE, kit.Keys("hash", value["data"]), node) - } - } - - node := kit.UnMarshal(value["node"]).(map[string]interface{}) - if m.Richs(ice.WEB_STORY, nil, value["list"], nil) == nil { - // 导入节点 - m.Log(ice.LOG_IMPORT, "%v: %v", value["list"], value["node"]) - m.Conf(ice.WEB_STORY, kit.Keys("hash", value["list"]), node) - } - - if first == nil { - if m.Richs(ice.WEB_STORY, "head", arg[1], nil) == nil { - // 自动创建 - h := m.Rich(ice.WEB_STORY, "head", kit.Dict( - "scene", node["scene"], "story", arg[1], - "count", node["count"], "list", value["list"], - )) - m.Log(ice.LOG_CREATE, "%v: %v", h, node["story"]) - } - - pull, first = kit.Format(value["list"]), node - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { - prev = kit.Format(val["list"]) - if kit.Int(node["count"]) > kit.Int(kit.Value(val, kit.Keys(repos, "pull", "count"))) { - // 更新分支 - m.Log(ice.LOG_IMPORT, "%v: %v", arg[2], pull) - kit.Value(val, kit.Keys(repos, "pull"), kit.Dict( - "head", arg[3], "time", node["time"], "list", pull, "count", node["count"], - )) - } - }) - } - - if prev == kit.Format(node["prev"]) || prev == kit.Format(node["push"]) { - // 快速合并 - m.Log(ice.LOG_IMPORT, "%v: %v", pull, arg[2]) - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { - val["count"] = first["count"] - val["time"] = first["time"] - val["list"] = pull - }) - prev = pull - } - - begin = kit.Format(node["prev"]) - }).Appendv("list") == nil { - break - } - } - - case ice.STORY_PUSH: - // 更新分支 - m.Cmdx(ice.WEB_STORY, "pull", arg[1:]) - - repos := kit.Keys("remote", arg[2], arg[3]) - // 查询索引 - prev, pull, some, list := "", "", "", "" - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { - prev = kit.Format(val["list"]) - pull = kit.Format(kit.Value(val, kit.Keys(repos, "pull", "list"))) - for some = pull; prev != some && some != ""; { - local := m.Richs(ice.WEB_STORY, nil, prev, nil) - remote := m.Richs(ice.WEB_STORY, nil, some, nil) - if diff := kit.Time(kit.Format(remote["time"])) - kit.Time(kit.Format(local["time"])); diff > 0 { - some = kit.Format(remote["prev"]) - } else if diff < 0 { - prev = kit.Format(local["prev"]) - } - } - - if prev = kit.Format(val["list"]); prev == pull { - // 相同节点 - return - } - - if some != pull { - // 合并节点 - local := m.Richs(ice.WEB_STORY, nil, prev, nil) - remote := m.Richs(ice.WEB_STORY, nil, pull, nil) - list = m.Rich(ice.WEB_STORY, nil, kit.Dict( - "scene", val["scene"], "story", val["story"], "count", kit.Int(remote["count"])+1, - "data", local["data"], "prev", pull, "push", prev, - )) - m.Log(ice.LOG_CREATE, "merge: %s %s->%s", list, prev, pull) - val["list"] = list - prev = list - val["count"] = kit.Int(remote["count"]) + 1 - } - - // 查询节点 - nodes := []string{} - for list = prev; list != some; { - m.Richs(ice.WEB_STORY, nil, list, func(key string, value map[string]interface{}) { - nodes, list = append(nodes, list), kit.Format(value["prev"]) - }) - } - - for _, v := range kit.Revert(nodes) { - m.Richs(ice.WEB_STORY, nil, v, func(list string, node map[string]interface{}) { - m.Richs(ice.WEB_CACHE, nil, node["data"], func(data string, save map[string]interface{}) { - if kit.Format(save["file"]) != "" { - // 推送缓存 - m.Cmd(ice.WEB_SPIDE, arg[2], "/story/upload", - "part", "upload", "@"+kit.Format(save["file"]), - ) - } - - // 推送节点 - m.Log(ice.LOG_EXPORT, "%s: %s", v, kit.Format(node)) - m.Cmd(ice.WEB_SPIDE, arg[2], "/story/push", - "story", arg[3], "list", v, "node", kit.Format(node), - "data", node["data"], "save", kit.Format(save), - ) - }) - }) - } - }) - - // 更新分支 - m.Cmd(ice.WEB_STORY, "pull", arg[1:]) - - case "commit": - // 查询索引 - head, prev, value, count := "", "", map[string]interface{}{}, 0 - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, val map[string]interface{}) { - head, prev, value, count = key, kit.Format(val["list"]), val, kit.Int(val["count"]) - m.Log("info", "head: %v prev: %v count: %v", head, prev, count) - }) - - // 提交信息 - arg[2] = m.Cmdx(ice.WEB_STORY, "add", "submit", arg[2], "hostname,username") - - // 节点信息 - menu := map[string]string{} - for i := 3; i < len(arg); i++ { - menu[arg[i]] = m.Cmdx(ice.WEB_STORY, ice.STORY_INDEX, arg[i]) - } - - // 添加节点 - list := m.Rich(ice.WEB_STORY, nil, kit.Dict( - "scene", "commit", "story", arg[1], "count", count+1, "data", arg[2], "list", menu, "prev", prev, - )) - m.Log(ice.LOG_CREATE, "commit: %s %s: %s", list, arg[1], arg[2]) - m.Push("list", list) - - if head == "" { - // 添加索引 - m.Rich(ice.WEB_STORY, "head", kit.Dict("scene", "commit", "story", arg[1], "count", count+1, "list", list)) - } else { - // 更新索引 - value["count"] = count + 1 - value["time"] = m.Time() - value["list"] = list - } - m.Echo(list) - - case ice.STORY_TRASH: - bak := kit.Select(kit.Keys(arg[1], "bak"), arg, 2) - os.Remove(bak) - os.Rename(arg[1], bak) - - case ice.STORY_WATCH: - // 备份文件 - name := kit.Select(arg[1], arg, 2) - m.Cmd(ice.WEB_STORY, ice.STORY_TRASH, name) - - if msg := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]); msg.Append("file") != "" { - p := path.Dir(name) - os.MkdirAll(p, 0777) - - // 导出文件 - os.Link(msg.Append("file"), name) - m.Log(ice.LOG_EXPORT, "%s: %s", msg.Append("file"), name) - } else { - if f, p, e := kit.Create(name); m.Assert(e) { - defer f.Close() - // 导出数据 - f.WriteString(msg.Append("text")) - m.Log(ice.LOG_EXPORT, "%s: %s", msg.Append("text"), p) - } - } - m.Echo(name) - - case ice.STORY_CATCH: - if last := m.Richs(ice.WEB_STORY, "head", arg[2], nil); last != nil { - if t, e := time.ParseInLocation(ice.ICE_TIME, kit.Format(last["time"]), time.Local); e == nil { - // 文件对比 - if s, e := os.Stat(arg[2]); e == nil && s.ModTime().Before(t) { - m.Info("%s last: %s", arg[2], kit.Format(t)) - m.Echo("%s", last["list"]) - break - } - } - } - fallthrough - case "add", ice.STORY_UPLOAD, ice.STORY_DOWNLOAD: - if m.Richs(ice.WEB_CACHE, nil, kit.Select("", arg, 3), func(key string, value map[string]interface{}) { - // 复用缓存 - arg[3] = key - }) == nil { - // 添加缓存 - m.Cmdy(ice.WEB_CACHE, arg) - arg = []string{arg[0], m.Append("type"), m.Append("name"), m.Append("data")} - } - - // 查询索引 - head, prev, value, count := "", "", map[string]interface{}{}, 0 - m.Richs(ice.WEB_STORY, "head", arg[2], func(key string, val map[string]interface{}) { - head, prev, value, count = key, kit.Format(val["list"]), val, kit.Int(val["count"]) - m.Log("info", "head: %v prev: %v count: %v", head, prev, count) - }) - - if last := m.Richs(ice.WEB_STORY, nil, prev, nil); prev != "" && last != nil && last["data"] == arg[3] { - // 重复提交 - m.Echo(prev) - } else { - // 添加节点 - list := m.Rich(ice.WEB_STORY, nil, kit.Dict( - "scene", arg[1], "story", arg[2], "count", count+1, "data", arg[3], "prev", prev, - )) - m.Log(ice.LOG_CREATE, "story: %s %s: %s", list, arg[1], arg[2]) - m.Push("list", list) - - if head == "" { - // 添加索引 - m.Rich(ice.WEB_STORY, "head", kit.Dict("scene", arg[1], "story", arg[2], "count", count+1, "list", list)) - } else { - // 更新索引 - value["count"] = count + 1 - value["time"] = m.Time() - value["list"] = list - } - m.Echo(list) - } - - // 分发数据 - if p := kit.Select(m.Conf(ice.WEB_FAVOR, "meta.proxy"), m.Option("you")); p != "" { - m.Info("what %v", p) - m.Option("you", "") - m.Cmd(ice.WEB_PROXY, p, ice.WEB_STORY, ice.STORY_PULL, arg[2], "dev", arg[2]) - } - - case ice.STORY_INDEX: - m.Richs(ice.WEB_STORY, "head", arg[1], func(key string, value map[string]interface{}) { - // 查询索引 - arg[1] = kit.Format(value["list"]) - }) - - m.Richs(ice.WEB_STORY, nil, arg[1], func(key string, value map[string]interface{}) { - // 查询节点 - m.Push("list", key) - m.Push(key, value, []string{"scene", "story"}) - arg[1] = kit.Format(value["data"]) - }) - - m.Richs(ice.WEB_CACHE, nil, arg[1], func(key string, value map[string]interface{}) { - // 查询数据 - m.Push("data", key) - m.Push(key, value, []string{"text", "time", "size", "type", "name", "file"}) - m.Echo("%s", value["text"]) - }) - - case ice.STORY_HISTORY: - // 历史记录 - list := m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]).Append("list") - for i := 0; i < kit.Int(kit.Select("30", m.Option("cache.limit"))) && list != ""; i++ { - - m.Richs(ice.WEB_STORY, nil, list, func(key string, value map[string]interface{}) { - // 直连节点 - m.Push(key, value, []string{"time", "key", "count", "scene", "story"}) - m.Richs(ice.WEB_CACHE, nil, value["data"], func(key string, value map[string]interface{}) { - m.Push("drama", value["text"]) - m.Push("data", key) - }) - - kit.Fetch(value["list"], func(key string, val string) { - m.Richs(ice.WEB_STORY, nil, val, func(key string, value map[string]interface{}) { - // 复合节点 - m.Push(key, value, []string{"time", "key", "count", "scene", "story"}) - m.Richs(ice.WEB_CACHE, nil, value["data"], func(key string, value map[string]interface{}) { - m.Push("drama", value["text"]) - m.Push("data", key) - }) - }) - }) - - // 切换节点 - list = kit.Format(value["prev"]) - }) - } - - default: - if len(arg) == 1 { - // 故事记录 - m.Cmdy(ice.WEB_STORY, "history", arg) - break - } - // 故事详情 - m.Cmd(ice.WEB_STORY, ice.STORY_INDEX, arg[1]).Table(func(index int, value map[string]string, head []string) { - for k, v := range value { - m.Push("key", k) - m.Push("value", v) - } - m.Sort("key") - }) - } - }}, - - ice.WEB_ROUTE: {Name: "route name cmd auto", Help: "路由", Meta: kit.Dict("detail", []string{"分组"}), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) > 1 && arg[0] == "action" { - switch arg[1] { - case "group", "分组": - if m.Option("grp") != "" && m.Option("name") != "" { - m.Cmdy(ice.WEB_GROUP, m.Option("grp"), "add", m.Option("name")) - } - } - return - } - - target, rest := "*", "" - if len(arg) > 0 { - ls := strings.SplitN(arg[0], ".", 2) - if target = ls[0]; len(ls) > 1 { - rest = ls[1] - } - } - m.Richs(ice.WEB_SPACE, nil, target, func(key string, val map[string]interface{}) { - if len(arg) > 1 { - m.Call(false, func(res *ice.Message) *ice.Message { return res }) - ls := []interface{}{ice.WEB_SPACE, val[kit.MDB_NAME]} - // 发送命令 - if rest != "" { - ls = append(ls, ice.WEB_SPACE, rest) - } - m.Cmdy(ls, arg[1:]) - return - } - - switch val[kit.MDB_TYPE] { - case ice.WEB_SERVER: - if val[kit.MDB_NAME] == m.Conf(ice.CLI_RUNTIME, "node.name") { - // 避免循环 - return - } - - // 远程查询 - m.Cmd(ice.WEB_SPACE, val[kit.MDB_NAME], ice.WEB_ROUTE).Table(func(index int, value map[string]string, head []string) { - m.Push(kit.MDB_TYPE, value[kit.MDB_TYPE]) - m.Push(kit.MDB_NAME, kit.Keys(val[kit.MDB_NAME], value[kit.MDB_NAME])) - }) - fallthrough - case ice.WEB_WORKER: - // 本机查询 - m.Push(kit.MDB_TYPE, val[kit.MDB_TYPE]) - m.Push(kit.MDB_NAME, val[kit.MDB_NAME]) - } - }) - }}, ice.WEB_PROXY: {Name: "proxy name cmd auto", Help: "代理", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { switch arg[0] { case "add": @@ -1934,159 +1521,7 @@ var Index = &ice.Context{Name: "web", Help: "网络模块", } }) }}, - ice.WEB_LABEL: {Name: "label label=auto name=auto auto", Help: "标签", Meta: kit.Dict( - "exports", []string{"lab", "label"}, "detail", []string{"归还"}, - ), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) > 1 && arg[0] == "action" { - switch arg[1] { - case "add", "添加": - if m.Option(cmd) != "" && m.Option(kit.MDB_GROUP) != "" && m.Option(kit.MDB_NAME) != "" { - m.Cmdy(cmd, m.Option(cmd), "add", m.Option(kit.MDB_GROUP), m.Option(kit.MDB_NAME)) - m.Option(ice.FIELD_RELOAD, "true") - } - case "del", "退还": - if m.Option(cmd) != "" && m.Option(kit.MDB_GROUP) != "" && m.Option(kit.MDB_NAME) != "" { - m.Cmdy(cmd, m.Option(cmd), "del", m.Option(kit.MDB_GROUP), m.Option(kit.MDB_NAME)) - m.Option(ice.FIELD_RELOAD, "true") - } - case "prune", "清理": - m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(sub string, value map[string]interface{}) { - if value[kit.MDB_STATUS] != "busy" { - m.Cmdy(cmd, m.Option(cmd), "del", value[kit.MDB_GROUP], value[kit.MDB_NAME]) - m.Option(ice.FIELD_RELOAD, "true") - } - }) - }) - case "clear", "清空": - m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(sub string, value map[string]interface{}) { - if value[kit.MDB_STATUS] == "void" { - last := m.Conf(cmd, kit.Keys(kit.MDB_HASH, key, kit.MDB_HASH, sub)) - m.Logs(ice.LOG_DELETE, cmd, m.Option(cmd), kit.MDB_NAME, value[kit.MDB_NAME], kit.MDB_VALUE, last) - m.Conf(cmd, kit.Keys(kit.MDB_HASH, key, kit.MDB_HASH, sub), "") - m.Option(ice.FIELD_RELOAD, "true") - m.Echo(last) - } - }) - }) - case "delete", "删除": - m.Richs(cmd, nil, m.Option(cmd), func(key string, value map[string]interface{}) { - m.Echo(m.Conf(cmd, kit.Keys(kit.MDB_HASH, key))) - m.Logs(ice.LOG_REMOVE, cmd, m.Option(cmd), kit.MDB_VALUE, m.Conf(cmd, kit.Keys(kit.MDB_HASH, key))) - m.Conf(cmd, kit.Keys(kit.MDB_HASH, key), "") - m.Option(ice.FIELD_RELOAD, "true") - }) - } - return - } - if len(arg) < 3 { - m.Richs(cmd, nil, kit.Select("*", arg, 0), func(key string, value map[string]interface{}) { - if len(arg) < 1 { - // 一级列表 - m.Option(ice.FIELD_DETAIL, "清理", "清空", "删除") - value = value[kit.MDB_META].(map[string]interface{}) - m.Push(key, value, []string{kit.MDB_TIME}) - status := map[string]int{} - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.MDB_FOREACH, func(key string, value map[string]interface{}) { - status[kit.Format(value[kit.MDB_STATUS])]++ - }) - m.Push("count", kit.Format("%d/%d/%d", status["busy"], status["free"], status["void"])) - m.Push(key, value, []string{cmd}) - return - } - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), kit.Select("*", arg, 1), func(key string, value map[string]interface{}) { - if len(arg) < 2 { - // 二级列表 - m.Option(ice.FIELD_DETAIL, "添加", "退还", "清理", "清空") - m.Push(key, value, []string{kit.MDB_TIME, kit.MDB_GROUP, kit.MDB_STATUS, kit.MDB_NAME}) - return - } - // 分组详情 - m.Option(ice.FIELD_DETAIL, "添加", "退还") - m.Push("detail", value) - }) - }) - if len(arg) < 1 { - m.Sort(cmd) - } else if len(arg) < 2 { - m.Sort(kit.MDB_NAME) - } - return - } - - if m.Richs(cmd, nil, arg[0], nil) == nil { - // 添加分组 - m.Logs(ice.LOG_CREATE, cmd, m.Rich(cmd, nil, kit.Data( - kit.MDB_SHORT, kit.MDB_NAME, cmd, arg[0], - ))) - } - - m.Richs(cmd, nil, arg[0], func(key string, value map[string]interface{}) { - switch arg[1] { - case "add": // 添加设备 - if pod := m.Cmdx(ice.WEB_GROUP, arg[2], "get", arg[3:]); pod != "" { - if m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), pod, func(key string, value map[string]interface{}) { - if value[kit.MDB_STATUS] == "void" { - value[kit.MDB_STATUS] = "free" - m.Logs(ice.LOG_MODIFY, cmd, arg[0], kit.MDB_NAME, pod, kit.MDB_STATUS, value[kit.MDB_STATUS]) - } - }) == nil { - m.Logs(ice.LOG_INSERT, cmd, arg[0], kit.MDB_NAME, pod) - m.Rich(cmd, kit.Keys(kit.MDB_HASH, key), kit.Dict( - kit.MDB_NAME, pod, kit.MDB_GROUP, arg[2], kit.MDB_STATUS, "free", - )) - } - m.Echo(arg[0]) - } - case "del": // 删除设备 - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), arg[3], func(sub string, value map[string]interface{}) { - if value[kit.MDB_STATUS] == "free" { - value[kit.MDB_STATUS] = "void" - m.Logs(ice.LOG_MODIFY, cmd, arg[0], kit.MDB_NAME, arg[3], kit.MDB_STATUS, "void") - m.Cmdx(ice.WEB_GROUP, value[kit.MDB_GROUP], "put", arg[3]) - m.Echo(arg[3]) - } - }) - default: - wg := &sync.WaitGroup{} - m.Option("_async", "true") - m.Richs(cmd, kit.Keys(kit.MDB_HASH, key), arg[1], func(key string, value map[string]interface{}) { - wg.Add(1) - // 远程命令 - m.Option(ice.MSG_USERPOD, value[kit.MDB_NAME]) - m.Cmd(ice.WEB_SPACE, value[kit.MDB_NAME], arg[2:]).Call(false, func(res *ice.Message) *ice.Message { - if wg.Done(); res != nil && m != nil { - m.Copy(res) - } - return nil - }) - }) - wg.Wait() - } - }) - }}, - - "/route/": {Name: "/route/", Help: "路由器", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - switch arg[0] { - case "login": - if m.Option(ice.MSG_USERNAME) != "" { - m.Push(ice.MSG_USERNAME, m.Option(ice.MSG_USERNAME)) - m.Info("username: %v", m.Option(ice.MSG_USERNAME)) - break - } - if m.Option(ice.MSG_SESSID) != "" && m.Cmdx(ice.AAA_SESS, "check", m.Option(ice.MSG_SESSID)) != "" { - m.Info("sessid: %v", m.Option(ice.MSG_SESSID)) - break - } - - sessid := m.Cmdx(ice.AAA_SESS, "create", "") - share := m.Cmdx(ice.WEB_SHARE, "add", "login", m.Option(ice.MSG_USERIP), sessid) - Render(m, "cookie", sessid) - m.Render(share) - } - }}, "/space/": {Name: "/space/", Help: "空间站", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { if s, e := websocket.Upgrade(m.W, m.R, nil, m.Confi(ice.WEB_SPACE, "meta.buffer.r"), m.Confi(ice.WEB_SPACE, "meta.buffer.w")); m.Assert(e) { m.Option("name", strings.Replace(kit.Select(m.Option(ice.MSG_USERADDR), m.Option("name")), ".", "_", -1)) diff --git a/core/chat/chat.go b/core/chat/chat.go index 85913cf3..d7ce083c 100644 --- a/core/chat/chat.go +++ b/core/chat/chat.go @@ -87,7 +87,6 @@ var Index = &ice.Context{Name: "chat", Help: "聊天中心", }), "fe", "volcanos", )}, - "search": {Name: "search", Help: "search", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME)}, "commend": {Name: "commend", Help: "commend", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME, "user", kit.Data(kit.MDB_SHORT, kit.MDB_NAME))}, }, Commands: map[string]*ice.Command{ @@ -375,65 +374,6 @@ var Index = &ice.Context{Name: "chat", Help: "聊天中心", "/target": {Name: "/target", Help: "对话框", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}}, "/source": {Name: "/source", Help: "输入框", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}}, - "search": {Name: "search label pod engine word", Help: "搜索引擎", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { - if len(arg) < 2 { - m.Cmdy(ice.WEB_LABEL, arg) - return - } - - switch arg[0] { - case "add": - if m.Richs(cmd, nil, arg[1], nil) == nil { - m.Rich(cmd, nil, kit.Data(kit.MDB_NAME, arg[1])) - } - m.Richs(cmd, nil, arg[1], func(key string, value map[string]interface{}) { - m.Grow(cmd, kit.Keys(kit.MDB_HASH, key), kit.Dict( - kit.MDB_NAME, arg[2], kit.MDB_TEXT, arg[3:], - )) - }) - case "get": - wg := &sync.WaitGroup{} - m.Richs(cmd, nil, arg[1], func(key string, value map[string]interface{}) { - wg.Add(1) - m.Gos(m, func(m *ice.Message) { - m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { - m.Cmdy(value[kit.MDB_TEXT], arg[2:]) - }) - wg.Done() - }) - }) - wg.Wait() - case "set": - if arg[1] != "" { - m.Cmdy(ice.WEB_SPACE, arg[1], "web.chat.search", "set", "", arg[2:]) - break - } - - m.Richs(cmd, nil, arg[2], func(key string, value map[string]interface{}) { - m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { - m.Cmdy(value[kit.MDB_TEXT], "set", arg[3:]) - }) - }) - default: - if len(arg) < 4 { - m.Richs(cmd, nil, kit.Select(kit.MDB_FOREACH, arg, 2), func(key string, val map[string]interface{}) { - if len(arg) < 3 { - m.Push(key, val[kit.MDB_META], []string{kit.MDB_TIME, kit.MDB_NAME, kit.MDB_COUNT}) - return - } - m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { - m.Push("", value, []string{kit.MDB_TIME}) - m.Push("group", arg[2]) - m.Push("", value, []string{kit.MDB_NAME, kit.MDB_TEXT}) - }) - }) - break - } - m.Option("pod", "") - m.Cmdy(ice.WEB_LABEL, arg[0], arg[1], "web.chat.search", "get", arg[2:]) - m.Sort("time", "time_r") - } - }}, "commend": {Name: "commend label pod engine work auto", Help: "推荐引擎", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { if len(arg) < 2 { m.Cmdy(ice.WEB_LABEL, arg) diff --git a/core/chat/search.go b/core/chat/search.go new file mode 100644 index 00000000..16a4b1e6 --- /dev/null +++ b/core/chat/search.go @@ -0,0 +1,75 @@ +package chat + +import ( + "github.com/shylinux/icebergs" + "github.com/shylinux/toolkits" + "sync" +) + +func init() { + Index.Merge(&ice.Context{ + Configs: map[string]*ice.Config{ + "search": {Name: "search", Help: "search", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME)}, + }, + Commands: map[string]*ice.Command{ + "search": {Name: "search label pod engine word", Help: "搜索引擎", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { + if len(arg) < 2 { + m.Cmdy(ice.WEB_LABEL, arg) + return + } + + switch arg[0] { + case "add": + if m.Richs(cmd, nil, arg[1], nil) == nil { + m.Rich(cmd, nil, kit.Data(kit.MDB_NAME, arg[1])) + } + m.Richs(cmd, nil, arg[1], func(key string, value map[string]interface{}) { + m.Grow(cmd, kit.Keys(kit.MDB_HASH, key), kit.Dict( + kit.MDB_NAME, arg[2], kit.MDB_TEXT, arg[3:], + )) + }) + case "get": + wg := &sync.WaitGroup{} + m.Richs(cmd, nil, arg[1], func(key string, value map[string]interface{}) { + wg.Add(1) + m.Gos(m, func(m *ice.Message) { + m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { + m.Cmdy(value[kit.MDB_TEXT], arg[2:]) + }) + wg.Done() + }) + }) + wg.Wait() + case "set": + if arg[1] != "" { + m.Cmdy(ice.WEB_SPACE, arg[1], "web.chat.search", "set", "", arg[2:]) + break + } + + m.Richs(cmd, nil, arg[2], func(key string, value map[string]interface{}) { + m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { + m.Cmdy(value[kit.MDB_TEXT], "set", arg[3:]) + }) + }) + default: + if len(arg) < 4 { + m.Richs(cmd, nil, kit.Select(kit.MDB_FOREACH, arg, 2), func(key string, val map[string]interface{}) { + if len(arg) < 3 { + m.Push(key, val[kit.MDB_META], []string{kit.MDB_TIME, kit.MDB_NAME, kit.MDB_COUNT}) + return + } + m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { + m.Push("", value, []string{kit.MDB_TIME}) + m.Push("group", arg[2]) + m.Push("", value, []string{kit.MDB_NAME, kit.MDB_TEXT}) + }) + }) + break + } + m.Option("pod", "") + m.Cmdy(ice.WEB_LABEL, arg[0], arg[1], "web.chat.search", "get", arg[2:]) + m.Sort("time", "time_r") + } + }}, + }}, nil) +} diff --git a/data.go b/data.go new file mode 100644 index 00000000..85a9f200 --- /dev/null +++ b/data.go @@ -0,0 +1,462 @@ +package ice + +import ( + kit "github.com/shylinux/toolkits" + + "encoding/csv" + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "os" + "path" + "sort" + "strings" +) + +func (m *Message) Prefile(favor string, id string) map[string]string { + res := map[string]string{} + m.Option("render", "") + m.Option("_action", "") + m.Cmd(WEB_FAVOR, kit.Select(m.Option("favor"), favor), id).Table(func(index int, value map[string]string, head []string) { + res[value["key"]] = value["value"] + }) + + res["content"] = m.Cmdx(CLI_SYSTEM, "sed", "-n", kit.Format("%d,%dp", kit.Int(res["extra.row"]), kit.Int(res["extra.row"])+3), res["extra.buf"]) + return res +} +func (m *Message) Prefix(arg ...string) string { + return kit.Keys(m.Cap(CTX_FOLLOW), arg) +} +func (m *Message) Save(arg ...string) *Message { + list := []string{} + for _, k := range arg { + list = append(list, kit.Keys(m.Cap(CTX_FOLLOW), k)) + } + m.Cmd(CTX_CONFIG, "save", kit.Keys(m.Cap(CTX_FOLLOW), "json"), list) + return m +} +func (m *Message) Load(arg ...string) *Message { + list := []string{} + for _, k := range arg { + list = append(list, kit.Keys(m.Cap(CTX_FOLLOW), k)) + } + m.Cmd(CTX_CONFIG, "load", kit.Keys(m.Cap(CTX_FOLLOW), "json"), list) + return m +} + +func (m *Message) Richs(key string, chain interface{}, raw interface{}, cb interface{}) (res map[string]interface{}) { + // 数据结构 + cache := m.Confm(key, chain) + if cache == nil { + return nil + } + meta, ok := cache[kit.MDB_META].(map[string]interface{}) + hash, ok := cache[kit.MDB_HASH].(map[string]interface{}) + if !ok { + return nil + } + + h := kit.Format(raw) + switch h { + case "*": + // 全部遍历 + switch cb := cb.(type) { + case func(string, string): + for k, v := range hash { + cb(k, kit.Format(v)) + } + case func(string, map[string]interface{}): + for k, v := range hash { + res = v.(map[string]interface{}) + cb(k, res) + } + } + return res + case "%": + // 随机选取 + if len(hash) > 0 { + list := []string{} + for k := range hash { + list = append(list, k) + } + h = list[rand.Intn(len(list))] + res, _ = hash[h].(map[string]interface{}) + } + default: + // 单个查询 + if res, ok = hash[h].(map[string]interface{}); !ok { + switch kit.Format(kit.Value(meta, kit.MDB_SHORT)) { + case "", "uniq": + default: + hh := kit.Hashs(h) + if res, ok = hash[hh].(map[string]interface{}); ok { + h = hh + break + } + + prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) + for _, k := range []string{h, hh} { + if f, e := os.Open(path.Join(prefix, kit.Keys(k, "json"))); e == nil { + defer f.Close() + if b, e := ioutil.ReadAll(f); e == nil { + if json.Unmarshal(b, &res) == e { + h = k + m.Log(LOG_IMPORT, "%s/%s.json", prefix, k) + break + } + } + } + } + } + } + } + + // 返回数据 + if res != nil { + switch cb := cb.(type) { + case func(map[string]interface{}): + cb(res) + case func(string, map[string]interface{}): + cb(h, res) + } + } + return res +} +func (m *Message) Rich(key string, chain interface{}, data interface{}) string { + // 数据结构 + cache := m.Confm(key, chain) + if cache == nil { + cache = map[string]interface{}{} + m.Confv(key, chain, cache) + } + meta, ok := cache[kit.MDB_META].(map[string]interface{}) + if !ok { + meta = map[string]interface{}{} + cache[kit.MDB_META] = meta + } + hash, ok := cache[kit.MDB_HASH].(map[string]interface{}) + if !ok { + hash = map[string]interface{}{} + cache[kit.MDB_HASH] = hash + } + + // 通用数据 + prefix := kit.Select("", "meta.", kit.Value(data, "meta") != nil) + if kit.Value(data, prefix+kit.MDB_TIME) == nil { + kit.Value(data, prefix+kit.MDB_TIME, m.Time()) + } + + // 生成键值 + h := "" + switch short := kit.Format(kit.Value(meta, kit.MDB_SHORT)); short { + case "": + h = kit.ShortKey(hash, 6) + case "uniq": + h = kit.Hashs("uniq") + case "data": + h = kit.Hashs(kit.Format(data)) + default: + if kit.Value(data, "meta") != nil { + h = kit.Hashs(kit.Format(kit.Value(data, "meta."+short))) + } else { + h = kit.Hashs(kit.Format(kit.Value(data, short))) + } + } + + // 添加数据 + if hash[h] = data; len(hash) >= kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.limit"), kit.Format(meta["limit"]))) { + least := kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.least"), kit.Format(meta["least"]))) + + // 时间淘汰 + list := []int{} + for _, v := range hash { + list = append(list, kit.Time(kit.Format(kit.Value(v, "time")))) + } + sort.Ints(list) + dead := list[len(list)-1-least] + + prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) + for k, v := range hash { + if kit.Time(kit.Format(kit.Value(v, "time"))) > dead { + break + } + + name := path.Join(prefix, kit.Keys(k, "json")) + if f, p, e := kit.Create(name); m.Assert(e) { + defer f.Close() + // 保存数据 + if n, e := f.WriteString(kit.Format(v)); m.Assert(e) { + m.Log(LOG_EXPORT, "%s: %d", p, n) + delete(hash, k) + } + } + } + } + + return h +} +func (m *Message) Grow(key string, chain interface{}, data interface{}) int { + // 数据结构 + cache := m.Confm(key, chain) + if cache == nil { + cache = map[string]interface{}{} + m.Confv(key, chain, cache) + } + meta, ok := cache[kit.MDB_META].(map[string]interface{}) + if !ok { + meta = map[string]interface{}{} + cache[kit.MDB_META] = meta + } + list, _ := cache[kit.MDB_LIST].([]interface{}) + + // 通用数据 + id := kit.Int(meta["count"]) + 1 + prefix := kit.Select("", "meta.", kit.Value(data, "meta") != nil) + if kit.Value(data, prefix+kit.MDB_ID, id); kit.Value(data, prefix+kit.MDB_TIME) == nil { + kit.Value(data, prefix+kit.MDB_TIME, kit.Select(m.Time(), m.Option("time"))) + } + + // 添加数据 + list = append(list, data) + cache[kit.MDB_LIST] = list + meta["count"] = id + + // 保存数据 + if len(list) >= kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.limit"), kit.Format(meta["limit"]))) { + least := kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.least"), kit.Format(meta["least"]))) + + record, _ := meta["record"].([]interface{}) + + // 文件命名 + prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) + name := path.Join(prefix, kit.Keys(kit.Select("list", chain), "csv")) + if len(record) > 0 { + name = kit.Format(kit.Value(record, kit.Keys(len(record)-1, "file"))) + if s, e := os.Stat(name); e == nil { + if s.Size() > kit.Int64(kit.Select(m.Conf(WEB_CACHE, "meta.fsize"), kit.Format(meta["fsize"]))) { + name = fmt.Sprintf("%s/%s_%d.csv", prefix, kit.Select("list", chain), kit.Int(meta["offset"])) + } + } + } + + // 打开文件 + f, e := os.OpenFile(name, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) + if e != nil { + f, _, e = kit.Create(name) + m.Info("%s.%v create: %s", key, chain, name) + } else { + m.Info("%s.%v append: %s", key, chain, name) + } + defer f.Close() + s, e := f.Stat() + m.Assert(e) + + // 保存表头 + keys := []string{} + w := csv.NewWriter(f) + if s.Size() == 0 { + for k := range list[0].(map[string]interface{}) { + keys = append(keys, k) + } + sort.Strings(keys) + w.Write(keys) + m.Info("write head: %v", keys) + w.Flush() + s, e = f.Stat() + } else { + r := csv.NewReader(f) + keys, e = r.Read() + m.Info("read head: %v", keys) + } + + // 创建索引 + count := len(list) - least + offset := kit.Int(meta["offset"]) + meta["record"] = append(record, map[string]interface{}{ + "time": m.Time(), "offset": offset, "count": count, + "file": name, "position": s.Size(), + }) + + // 保存数据 + for i, v := range list { + if i >= count { + break + } + + val := v.(map[string]interface{}) + + values := []string{} + for _, k := range keys { + values = append(values, kit.Format(val[k])) + } + w.Write(values) + + if i < least { + list[i] = list[count+i] + } + } + + m.Log(LOG_INFO, "%s.%v save %s offset %v+%v", key, chain, name, offset, count) + meta["offset"] = offset + count + list = list[count:] + cache[kit.MDB_LIST] = list + w.Flush() + } + return id +} +func (m *Message) Grows(key string, chain interface{}, match string, value string, cb interface{}) map[string]interface{} { + // 数据结构 + cache := m.Confm(key, chain) + if cache == nil { + return nil + } + meta, ok := cache[kit.MDB_META].(map[string]interface{}) + list, ok := cache[kit.MDB_LIST].([]interface{}) + if !ok { + return nil + } + + // 数据范围 + offend := kit.Int(kit.Select("0", m.Option("cache.offend"))) + limit := kit.Int(kit.Select("10", m.Option("cache.limit"))) + current := kit.Int(meta["offset"]) + end := current + len(list) - offend + begin := end - limit + switch limit { + case -1: + begin = current + case -2: + begin = 0 + } + + if match == kit.MDB_ID { + begin, end = kit.Int(value)-1, kit.Int(value) + match, value = "", "" + } + + order := 0 + if begin < current { + // 读取文件 + m.Log(LOG_INFO, "%s.%v read %v-%v from %v-%v", key, chain, begin, end, current, current+len(list)) + store, _ := meta["record"].([]interface{}) + for s := len(store) - 1; s > -1; s-- { + item, _ := store[s].(map[string]interface{}) + line := kit.Int(item["offset"]) + m.Logs(LOG_INFO, "action", "check", "record", s, "offset", line, "count", item["count"]) + if begin < line && s > 0 { + if kit.Int(item["count"]) != 0 { + s -= (line - begin) / kit.Int(item["count"]) + } + // 向后查找 + continue + } + + for ; begin < end && s < len(store); s++ { + item, _ := store[s].(map[string]interface{}) + name := kit.Format(item["file"]) + pos := kit.Int(item["position"]) + offset := kit.Int(item["offset"]) + if offset+kit.Int(item["count"]) <= begin { + m.Logs(LOG_INFO, "action", "check", "record", s, "offset", line, "count", item["count"]) + // 向前查找 + continue + } + + if f, e := os.Open(name); m.Assert(e) { + defer f.Close() + // 打开文件 + r := csv.NewReader(f) + heads, _ := r.Read() + m.Logs(LOG_IMPORT, "head", heads) + + f.Seek(int64(pos), os.SEEK_SET) + r = csv.NewReader(f) + for i := offset; i < end; i++ { + lines, e := r.Read() + if e != nil { + m.Log(LOG_IMPORT, "load head %v", e) + break + } + if i < begin { + m.Logs(LOG_INFO, "action", "skip", "offset", i) + continue + } + + // 读取数据 + item := map[string]interface{}{} + for i := range heads { + if heads[i] == "extra" { + item[heads[i]] = kit.UnMarshal(lines[i]) + } else { + item[heads[i]] = lines[i] + } + } + m.Logs(LOG_IMPORT, "offset", i, "type", item["type"], "name", item["name"], "text", item["text"]) + + if match == "" || strings.Contains(kit.Format(item[match]), value) { + // 匹配成功 + switch cb := cb.(type) { + case func(int, map[string]interface{}): + cb(order, item) + case func(int, map[string]interface{}) bool: + if cb(order, item) { + return meta + } + } + order++ + } + begin = i + 1 + } + } + } + break + } + } + + if begin < current { + begin = current + } + for i := begin - current; i < end-current; i++ { + // 读取缓存 + if match == "" || strings.Contains(kit.Format(kit.Value(list[i], match)), value) { + switch cb := cb.(type) { + case func(int, map[string]interface{}): + cb(order, list[i].(map[string]interface{})) + case func(int, map[string]interface{}) bool: + if cb(order, list[i].(map[string]interface{})) { + return meta + } + } + order++ + } + } + return meta +} +func (m *Message) Show(cmd string, arg ...string) bool { + if len(arg) == 0 { + // 日志分类 + m.Richs(cmd, nil, "*", func(key string, value map[string]interface{}) { + m.Push(key, value["meta"]) + }) + return true + } + if len(arg) < 3 { + if m.Richs(cmd, nil, arg[0], func(key string, val map[string]interface{}) { + if len(arg) == 1 { + // 日志列表 + m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { + m.Push(key, value) + }) + return + } + // 日志详情 + m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "id", arg[1], func(index int, value map[string]interface{}) { + m.Push("detail", value) + }) + }) != nil { + return true + } + } + return false +} diff --git a/info.go b/info.go new file mode 100644 index 00000000..f8e8de2a --- /dev/null +++ b/info.go @@ -0,0 +1,92 @@ +package ice + +import ( + kit "github.com/shylinux/toolkits" + + "fmt" + "os" + "runtime" + "strings" +) + +func (m *Message) Logs(level string, arg ...interface{}) *Message { + list := []string{} + for i := 0; i < len(arg)-1; i += 2 { + list = append(list, fmt.Sprintf("%v: %v", arg[i], arg[i+1])) + } + m.Log(level, strings.Join(list, " ")) + return m +} +func (m *Message) Log(level string, str string, arg ...interface{}) *Message { + return m.log(level, str, arg...) +} +func (m *Message) log(level string, str string, arg ...interface{}) *Message { + if str = strings.TrimSpace(fmt.Sprintf(str, arg...)); Log != nil { + // 日志模块 + Log(m, level, str) + } + + // 日志颜色 + prefix, suffix := "", "" + switch level { + case LOG_ENABLE, LOG_IMPORT, LOG_CREATE, LOG_INSERT, LOG_EXPORT: + prefix, suffix = "\033[36;44m", "\033[0m" + + case LOG_LISTEN, LOG_SIGNAL, LOG_TIMERS, LOG_EVENTS: + prefix, suffix = "\033[33m", "\033[0m" + + case LOG_CMDS, LOG_START, LOG_SERVE: + prefix, suffix = "\033[32m", "\033[0m" + case LOG_COST: + prefix, suffix = "\033[33m", "\033[0m" + case LOG_WARN, LOG_ERROR, LOG_CLOSE: + prefix, suffix = "\033[31m", "\033[0m" + } + + _, file, line, _ := runtime.Caller(2) + ls := strings.Split(file, "/") + if len(ls) > 2 { + ls = ls[len(ls)-2:] + } + + if os.Getenv("ctx_mod") != "" && m != nil { + // 输出日志 + fmt.Fprintf(os.Stderr, "%s %02d %9s %s%s %s%s %s\n", + m.time.Format(ICE_TIME), m.code, fmt.Sprintf("%4s->%-4s", m.source.Name, m.target.Name), + prefix, level, str, suffix, + fmt.Sprintf("%s:%d", strings.Join(ls, "/"), line), + ) + } + return m +} +func (m *Message) Cost(str string, arg ...interface{}) *Message { + return m.log(LOG_COST, "%s: %s", m.Format("cost"), kit.Format(str, arg...)) +} +func (m *Message) Info(str string, arg ...interface{}) *Message { + return m.log(LOG_INFO, str, arg...) +} +func (m *Message) Warn(err bool, str string, arg ...interface{}) bool { + if err { + _, file, line, _ := runtime.Caller(1) + m.Echo("warn: ").Echo(str, arg...) + return m.log(LOG_WARN, "%s:%d %s", file, line, fmt.Sprintf(str, arg...)) != nil + } + return false +} +func (m *Message) Error(err bool, str string, arg ...interface{}) bool { + if err { + m.Echo("error: ").Echo(str, arg...) + m.log(LOG_ERROR, m.Format("stack")) + m.log(LOG_ERROR, str, arg...) + m.log(LOG_ERROR, m.Format("chain")) + return true + } + return false +} +func (m *Message) Trace(key string, str string, arg ...interface{}) *Message { + if m.Options(key) { + m.Echo("trace: ").Echo(str, arg...) + return m.log(LOG_TRACE, str, arg...) + } + return m +} diff --git a/meta.go b/meta.go new file mode 100644 index 00000000..f6a84e75 --- /dev/null +++ b/meta.go @@ -0,0 +1,462 @@ +package ice + +import ( + kit "github.com/shylinux/toolkits" + + "bytes" + "encoding/csv" + "fmt" + "sort" + "strconv" + "strings" +) + +func (m *Message) Add(key string, arg ...string) *Message { + switch key { + case MSG_DETAIL, MSG_RESULT: + m.meta[key] = append(m.meta[key], arg...) + + case MSG_OPTION, MSG_APPEND: + if len(arg) > 0 { + if kit.IndexOf(m.meta[key], arg[0]) == -1 { + m.meta[key] = append(m.meta[key], arg[0]) + } + m.meta[arg[0]] = append(m.meta[arg[0]], arg[1:]...) + } + } + return m +} +func (m *Message) Set(key string, arg ...string) *Message { + switch key { + case MSG_DETAIL, MSG_RESULT: + delete(m.meta, key) + case MSG_OPTION, MSG_APPEND: + if len(arg) > 0 { + if delete(m.meta, arg[0]); len(arg) == 1 { + return m + } + } else { + for _, k := range m.meta[key] { + delete(m.meta, k) + } + delete(m.meta, key) + return m + } + default: + delete(m.meta, key) + } + return m.Add(key, arg...) +} +func (m *Message) Push(key string, value interface{}, arg ...interface{}) *Message { + switch value := value.(type) { + case map[string]string: + case map[string]interface{}: + if key == "detail" { + // 格式转换 + value = kit.KeyValue(map[string]interface{}{}, "", value) + } + + // 键值排序 + list := []string{} + if len(arg) > 0 { + list = kit.Simple(arg[0]) + } else { + for k := range value { + list = append(list, k) + } + sort.Strings(list) + } + + // 追加数据 + for _, k := range list { + switch key { + case "detail": + m.Add(MSG_APPEND, "key", k) + m.Add(MSG_APPEND, "value", kit.Format(value[k])) + default: + if k == "key" { + m.Add(MSG_APPEND, k, key) + } else { + m.Add(MSG_APPEND, k, kit.Format(kit.Value(value, k))) + } + } + } + return m + } + + for _, v := range kit.Simple(value) { + m.Add(MSG_APPEND, key, v) + } + return m +} +func (m *Message) Echo(str string, arg ...interface{}) *Message { + if len(arg) > 0 { + str = fmt.Sprintf(str, arg...) + } + m.meta[MSG_RESULT] = append(m.meta[MSG_RESULT], str) + return m +} +func (m *Message) Copy(msg *Message, arg ...string) *Message { + if m == msg { + return m + } + if m == nil { + return m + } + if len(arg) > 0 { + // 精确复制 + for _, k := range arg[1:] { + if kit.IndexOf(m.meta[arg[0]], k) == -1 { + m.meta[arg[0]] = append(m.meta[arg[0]], k) + } + m.meta[k] = append(m.meta[k], msg.meta[k]...) + } + return m + } + + // 复制选项 + for _, k := range msg.meta[MSG_OPTION] { + if kit.IndexOf(m.meta[MSG_OPTION], k) == -1 { + m.meta[MSG_OPTION] = append(m.meta[MSG_OPTION], k) + } + if _, ok := msg.meta[k]; ok { + m.meta[k] = msg.meta[k] + } else { + m.data[k] = msg.data[k] + } + } + + // 复制数据 + for _, k := range msg.meta[MSG_APPEND] { + if kit.IndexOf(m.meta[MSG_OPTION], k) > -1 && len(m.meta[k]) > 0 { + m.meta[k] = m.meta[k][:0] + } + if kit.IndexOf(m.meta[MSG_APPEND], k) == -1 { + m.meta[MSG_APPEND] = append(m.meta[MSG_APPEND], k) + } + m.meta[k] = append(m.meta[k], msg.meta[k]...) + } + + // 复制文本 + m.meta[MSG_RESULT] = append(m.meta[MSG_RESULT], msg.meta[MSG_RESULT]...) + return m +} +func (m *Message) Sort(key string, arg ...string) *Message { + // 排序方法 + cmp := "str" + if len(arg) > 0 && arg[0] != "" { + cmp = arg[0] + } else { + cmp = "int" + for _, v := range m.meta[key] { + if _, e := strconv.Atoi(v); e != nil { + cmp = "str" + } + } + } + + // 排序因子 + number := map[int]int{} + table := []map[string]string{} + m.Table(func(index int, line map[string]string, head []string) { + table = append(table, line) + switch cmp { + case "int": + number[index] = kit.Int(line[key]) + case "int_r": + number[index] = -kit.Int(line[key]) + case "time": + number[index] = kit.Time(line[key]) + case "time_r": + number[index] = -kit.Time(line[key]) + } + }) + + // 排序数据 + for i := 0; i < len(table)-1; i++ { + for j := i + 1; j < len(table); j++ { + result := false + switch cmp { + case "", "str": + if table[i][key] > table[j][key] { + result = true + } + case "str_r": + if table[i][key] < table[j][key] { + result = true + } + default: + if number[i] > number[j] { + result = true + } + } + + if result { + table[i], table[j] = table[j], table[i] + number[i], number[j] = number[j], number[i] + } + } + } + + // 输出数据 + for _, k := range m.meta[MSG_APPEND] { + delete(m.meta, k) + } + for _, v := range table { + for _, k := range m.meta[MSG_APPEND] { + m.Add(MSG_APPEND, k, v[k]) + } + } + return m +} +func (m *Message) Table(cbs ...interface{}) *Message { + if len(cbs) > 0 { + switch cb := cbs[0].(type) { + case func(int, map[string]string, []string): + nrow := 0 + for _, k := range m.meta[MSG_APPEND] { + if len(m.meta[k]) > nrow { + nrow = len(m.meta[k]) + } + } + + for i := 0; i < nrow; i++ { + line := map[string]string{} + for _, k := range m.meta[MSG_APPEND] { + line[k] = kit.Select("", m.meta[k], i) + } + // 依次回调 + cb(i, line, m.meta[MSG_APPEND]) + } + } + return m + } + + //计算列宽 + space := kit.Select(" ", m.Option("table.space")) + depth, width := 0, map[string]int{} + for _, k := range m.meta[MSG_APPEND] { + if len(m.meta[k]) > depth { + depth = len(m.meta[k]) + } + width[k] = kit.Width(k, len(space)) + for _, v := range m.meta[k] { + if kit.Width(v, len(space)) > width[k] { + width[k] = kit.Width(v, len(space)) + } + } + } + + // 回调函数 + rows := kit.Select("\n", m.Option("table.row_sep")) + cols := kit.Select(" ", m.Option("table.col_sep")) + compact := m.Option("table.compact") == "true" + cb := func(value map[string]string, field []string, index int) bool { + for i, v := range field { + if k := m.meta[MSG_APPEND][i]; compact { + v = value[k] + } + + if m.Echo(v); i < len(field)-1 { + m.Echo(cols) + } + } + m.Echo(rows) + return true + } + + // 输出表头 + row := map[string]string{} + wor := []string{} + for _, k := range m.meta[MSG_APPEND] { + row[k], wor = k, append(wor, k+strings.Repeat(space, width[k]-kit.Width(k, len(space)))) + } + if !cb(row, wor, -1) { + return m + } + + // 输出数据 + for i := 0; i < depth; i++ { + row := map[string]string{} + wor := []string{} + for _, k := range m.meta[MSG_APPEND] { + data := "" + if i < len(m.meta[k]) { + data = m.meta[k][i] + } + + row[k], wor = data, append(wor, data+strings.Repeat(space, width[k]-kit.Width(data, len(space)))) + } + // 依次回调 + if !cb(row, wor, i) { + break + } + } + return m +} +func (m *Message) Render(cmd string, args ...interface{}) *Message { + m.Log(LOG_EXPORT, "%s: %v", cmd, args) + m.Optionv(MSG_OUTPUT, cmd) + m.Optionv(MSG_ARGS, args) + + switch cmd { + case RENDER_TEMPLATE: + if len(args) == 1 { + args = append(args, m) + } + if res, err := kit.Render(args[0].(string), args[1]); m.Assert(err) { + m.Echo(string(res)) + } + } + return m +} +func (m *Message) Parse(meta string, key string, arg ...string) *Message { + list := []string{} + for _, line := range kit.Split(strings.Join(arg, " "), "\n") { + ls := kit.Split(line) + for i := 0; i < len(ls); i++ { + if strings.HasPrefix(ls[i], "#") { + ls = ls[:i] + break + } + } + list = append(list, ls...) + } + + switch data := kit.Parse(nil, "", list...); meta { + case MSG_OPTION: + m.Option(key, data) + case MSG_APPEND: + m.Append(key, data) + } + return m +} +func (m *Message) Split(str string, field string, space string, enter string) *Message { + indexs := []int{} + fields := kit.Split(field, space, "{}") + for i, l := range kit.Split(str, enter, "{}") { + if i == 0 && (field == "" || field == "index") { + // 表头行 + fields = kit.Split(l, space) + if field == "index" { + for _, v := range fields { + indexs = append(indexs, strings.Index(l, v)) + } + } + continue + } + + if len(indexs) > 0 { + // 数据行 + for i, v := range indexs { + if i == len(indexs)-1 { + m.Push(kit.Select("some", fields, i), l[v:]) + } else { + m.Push(kit.Select("some", fields, i), l[v:indexs[i+1]]) + } + } + continue + } + + for i, v := range kit.Split(l, space) { + m.Push(kit.Select("some", fields, i), v) + } + } + return m +} +func (m *Message) CSV(text string) *Message { + bio := bytes.NewBufferString(text) + r := csv.NewReader(bio) + heads, _ := r.Read() + for { + lines, e := r.Read() + if e != nil { + break + } + for i, k := range heads { + m.Push(k, kit.Select("", lines, i)) + } + } + return m +} + +func (m *Message) Detail(arg ...interface{}) string { + return kit.Select("", m.meta[MSG_DETAIL], 0) +} +func (m *Message) Detailv(arg ...interface{}) []string { + return m.meta[MSG_DETAIL] +} +func (m *Message) Optionv(key string, arg ...interface{}) interface{} { + if len(arg) > 0 { + // 写数据 + if kit.IndexOf(m.meta[MSG_OPTION], key) == -1 { + m.meta[MSG_OPTION] = append(m.meta[MSG_OPTION], key) + } + + switch str := arg[0].(type) { + case nil: + delete(m.meta, key) + case string: + m.meta[key] = kit.Simple(arg) + case []string: + m.meta[key] = str + default: + m.data[key] = str + } + } + + for msg := m; msg != nil; msg = msg.message { + if list, ok := msg.data[key]; ok { + // 读数据 + return list + } + if list, ok := msg.meta[key]; ok { + // 读选项 + return list + } + } + return nil +} +func (m *Message) Options(key string, arg ...interface{}) bool { + return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) != "" +} +func (m *Message) Option(key string, arg ...interface{}) string { + return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) +} +func (m *Message) Append(key string, arg ...interface{}) string { + return kit.Select("", m.Appendv(key, arg...), 0) +} +func (m *Message) Appendv(key string, arg ...interface{}) []string { + if key == "_index" { + max := 0 + for _, k := range m.meta[MSG_APPEND] { + if len(m.meta[k]) > max { + max = len(m.meta[k]) + } + } + index := []string{} + for i := 0; i < max; i++ { + index = append(index, kit.Format(i)) + } + return index + } + if len(arg) > 0 { + m.meta[key] = kit.Simple(arg...) + } + return m.meta[key] +} +func (m *Message) Resultv(arg ...interface{}) []string { + if len(arg) > 0 { + m.meta[MSG_RESULT] = kit.Simple(arg...) + } + return m.meta[MSG_RESULT] +} +func (m *Message) Result(arg ...interface{}) string { + if len(arg) > 0 { + switch v := arg[0].(type) { + case int: + return kit.Select("", m.meta[MSG_RESULT], v) + } + } + return strings.Join(m.Resultv(arg...), "") +} diff --git a/type.go b/type.go index 547afb04..a841c502 100644 --- a/type.go +++ b/type.go @@ -6,20 +6,13 @@ package ice import ( kit "github.com/shylinux/toolkits" - "bytes" - "encoding/csv" "encoding/json" "errors" "fmt" "io" - "io/ioutil" - "math/rand" "net/http" - "os" - "path" "runtime" "sort" - "strconv" "strings" "sync" "sync/atomic" @@ -362,538 +355,6 @@ func (m *Message) Spawn(arg ...interface{}) *Message { return msg } -func (m *Message) Add(key string, arg ...string) *Message { - switch key { - case MSG_DETAIL, MSG_RESULT: - m.meta[key] = append(m.meta[key], arg...) - - case MSG_OPTION, MSG_APPEND: - if len(arg) > 0 { - if kit.IndexOf(m.meta[key], arg[0]) == -1 { - m.meta[key] = append(m.meta[key], arg[0]) - } - m.meta[arg[0]] = append(m.meta[arg[0]], arg[1:]...) - } - } - return m -} -func (m *Message) Set(key string, arg ...string) *Message { - switch key { - case MSG_DETAIL, MSG_RESULT: - delete(m.meta, key) - case MSG_OPTION, MSG_APPEND: - if len(arg) > 0 { - if delete(m.meta, arg[0]); len(arg) == 1 { - return m - } - } else { - for _, k := range m.meta[key] { - delete(m.meta, k) - } - delete(m.meta, key) - return m - } - default: - delete(m.meta, key) - } - return m.Add(key, arg...) -} -func (m *Message) Push(key string, value interface{}, arg ...interface{}) *Message { - switch value := value.(type) { - case map[string]string: - case map[string]interface{}: - if key == "detail" { - // 格式转换 - value = kit.KeyValue(map[string]interface{}{}, "", value) - } - - // 键值排序 - list := []string{} - if len(arg) > 0 { - list = kit.Simple(arg[0]) - } else { - for k := range value { - list = append(list, k) - } - sort.Strings(list) - } - - // 追加数据 - for _, k := range list { - switch key { - case "detail": - m.Add(MSG_APPEND, "key", k) - m.Add(MSG_APPEND, "value", kit.Format(value[k])) - default: - if k == "key" { - m.Add(MSG_APPEND, k, key) - } else { - m.Add(MSG_APPEND, k, kit.Format(kit.Value(value, k))) - } - } - } - return m - } - - for _, v := range kit.Simple(value) { - m.Add(MSG_APPEND, key, v) - } - return m -} -func (m *Message) Echo(str string, arg ...interface{}) *Message { - if len(arg) > 0 { - str = fmt.Sprintf(str, arg...) - } - m.meta[MSG_RESULT] = append(m.meta[MSG_RESULT], str) - return m -} -func (m *Message) Copy(msg *Message, arg ...string) *Message { - if m == msg { - return m - } - if m == nil { - return m - } - if len(arg) > 0 { - // 精确复制 - for _, k := range arg[1:] { - if kit.IndexOf(m.meta[arg[0]], k) == -1 { - m.meta[arg[0]] = append(m.meta[arg[0]], k) - } - m.meta[k] = append(m.meta[k], msg.meta[k]...) - } - return m - } - - // 复制选项 - for _, k := range msg.meta[MSG_OPTION] { - if kit.IndexOf(m.meta[MSG_OPTION], k) == -1 { - m.meta[MSG_OPTION] = append(m.meta[MSG_OPTION], k) - } - if _, ok := msg.meta[k]; ok { - m.meta[k] = msg.meta[k] - } else { - m.data[k] = msg.data[k] - } - } - - // 复制数据 - for _, k := range msg.meta[MSG_APPEND] { - if kit.IndexOf(m.meta[MSG_OPTION], k) > -1 && len(m.meta[k]) > 0 { - m.meta[k] = m.meta[k][:0] - } - if kit.IndexOf(m.meta[MSG_APPEND], k) == -1 { - m.meta[MSG_APPEND] = append(m.meta[MSG_APPEND], k) - } - m.meta[k] = append(m.meta[k], msg.meta[k]...) - } - - // 复制文本 - m.meta[MSG_RESULT] = append(m.meta[MSG_RESULT], msg.meta[MSG_RESULT]...) - return m -} -func (m *Message) Sort(key string, arg ...string) *Message { - // 排序方法 - cmp := "str" - if len(arg) > 0 && arg[0] != "" { - cmp = arg[0] - } else { - cmp = "int" - for _, v := range m.meta[key] { - if _, e := strconv.Atoi(v); e != nil { - cmp = "str" - } - } - } - - // 排序因子 - number := map[int]int{} - table := []map[string]string{} - m.Table(func(index int, line map[string]string, head []string) { - table = append(table, line) - switch cmp { - case "int": - number[index] = kit.Int(line[key]) - case "int_r": - number[index] = -kit.Int(line[key]) - case "time": - number[index] = kit.Time(line[key]) - case "time_r": - number[index] = -kit.Time(line[key]) - } - }) - - // 排序数据 - for i := 0; i < len(table)-1; i++ { - for j := i + 1; j < len(table); j++ { - result := false - switch cmp { - case "", "str": - if table[i][key] > table[j][key] { - result = true - } - case "str_r": - if table[i][key] < table[j][key] { - result = true - } - default: - if number[i] > number[j] { - result = true - } - } - - if result { - table[i], table[j] = table[j], table[i] - number[i], number[j] = number[j], number[i] - } - } - } - - // 输出数据 - for _, k := range m.meta[MSG_APPEND] { - delete(m.meta, k) - } - for _, v := range table { - for _, k := range m.meta[MSG_APPEND] { - m.Add(MSG_APPEND, k, v[k]) - } - } - return m -} -func (m *Message) Table(cbs ...interface{}) *Message { - if len(cbs) > 0 { - switch cb := cbs[0].(type) { - case func(int, map[string]string, []string): - nrow := 0 - for _, k := range m.meta[MSG_APPEND] { - if len(m.meta[k]) > nrow { - nrow = len(m.meta[k]) - } - } - - for i := 0; i < nrow; i++ { - line := map[string]string{} - for _, k := range m.meta[MSG_APPEND] { - line[k] = kit.Select("", m.meta[k], i) - } - // 依次回调 - cb(i, line, m.meta[MSG_APPEND]) - } - } - return m - } - - //计算列宽 - space := kit.Select(" ", m.Option("table.space")) - depth, width := 0, map[string]int{} - for _, k := range m.meta[MSG_APPEND] { - if len(m.meta[k]) > depth { - depth = len(m.meta[k]) - } - width[k] = kit.Width(k, len(space)) - for _, v := range m.meta[k] { - if kit.Width(v, len(space)) > width[k] { - width[k] = kit.Width(v, len(space)) - } - } - } - - // 回调函数 - rows := kit.Select("\n", m.Option("table.row_sep")) - cols := kit.Select(" ", m.Option("table.col_sep")) - compact := m.Option("table.compact") == "true" - cb := func(maps map[string]string, lists []string, line int) bool { - for i, v := range lists { - if k := m.meta[MSG_APPEND][i]; compact { - v = maps[k] - } - - if m.Echo(v); i < len(lists)-1 { - m.Echo(cols) - } - } - m.Echo(rows) - return true - } - - // 输出表头 - row := map[string]string{} - wor := []string{} - for _, k := range m.meta[MSG_APPEND] { - row[k], wor = k, append(wor, k+strings.Repeat(space, width[k]-kit.Width(k, len(space)))) - } - if !cb(row, wor, -1) { - return m - } - - // 输出数据 - for i := 0; i < depth; i++ { - row := map[string]string{} - wor := []string{} - for _, k := range m.meta[MSG_APPEND] { - data := "" - if i < len(m.meta[k]) { - data = m.meta[k][i] - } - - row[k], wor = data, append(wor, data+strings.Repeat(space, width[k]-kit.Width(data, len(space)))) - } - // 依次回调 - if !cb(row, wor, i) { - break - } - } - return m -} -func (m *Message) Render(cmd string, args ...interface{}) *Message { - m.Log(LOG_EXPORT, "%s: %v", cmd, args) - m.Optionv(MSG_OUTPUT, cmd) - m.Optionv(MSG_ARGS, args) - - switch cmd { - case RENDER_TEMPLATE: - if len(args) == 1 { - args = append(args, m) - } - if res, err := kit.Render(args[0].(string), args[1]); m.Assert(err) { - m.Echo(string(res)) - } - } - return m -} -func (m *Message) Parse(meta string, key string, arg ...string) *Message { - list := []string{} - for _, line := range kit.Split(strings.Join(arg, " "), "\n") { - ls := kit.Split(line) - for i := 0; i < len(ls); i++ { - if strings.HasPrefix(ls[i], "#") { - ls = ls[:i] - break - } - } - list = append(list, ls...) - } - - switch data := kit.Parse(nil, "", list...); meta { - case MSG_OPTION: - m.Option(key, data) - case MSG_APPEND: - m.Append(key, data) - } - return m -} -func (m *Message) Split(str string, field string, space string, enter string) *Message { - indexs := []int{} - fields := kit.Split(field, space, "{}") - for i, l := range kit.Split(str, enter, "{}") { - if i == 0 && (field == "" || field == "index") { - // 表头行 - fields = kit.Split(l, space) - if field == "index" { - for _, v := range fields { - indexs = append(indexs, strings.Index(l, v)) - } - } - continue - } - - if len(indexs) > 0 { - // 数据行 - for i, v := range indexs { - if i == len(indexs)-1 { - m.Push(kit.Select("some", fields, i), l[v:]) - } else { - m.Push(kit.Select("some", fields, i), l[v:indexs[i+1]]) - } - } - continue - } - - for i, v := range kit.Split(l, space) { - m.Push(kit.Select("some", fields, i), v) - } - } - return m -} -func (m *Message) CSV(text string) *Message { - bio := bytes.NewBufferString(text) - r := csv.NewReader(bio) - heads, _ := r.Read() - for { - lines, e := r.Read() - if e != nil { - break - } - for i, k := range heads { - m.Push(k, kit.Select("", lines, i)) - } - } - return m -} - -func (m *Message) Detail(arg ...interface{}) string { - return kit.Select("", m.meta[MSG_DETAIL], 0) -} -func (m *Message) Detailv(arg ...interface{}) []string { - return m.meta[MSG_DETAIL] -} -func (m *Message) Optionv(key string, arg ...interface{}) interface{} { - if len(arg) > 0 { - // 写数据 - if kit.IndexOf(m.meta[MSG_OPTION], key) == -1 { - m.meta[MSG_OPTION] = append(m.meta[MSG_OPTION], key) - } - - switch str := arg[0].(type) { - case nil: - delete(m.meta, key) - case string: - m.meta[key] = kit.Simple(arg) - case []string: - m.meta[key] = str - default: - m.data[key] = str - } - } - - for msg := m; msg != nil; msg = msg.message { - if list, ok := msg.data[key]; ok { - // 读数据 - return list - } - if list, ok := msg.meta[key]; ok { - // 读选项 - return list - } - } - return nil -} -func (m *Message) Options(key string, arg ...interface{}) bool { - return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) != "" -} -func (m *Message) Option(key string, arg ...interface{}) string { - return kit.Select("", kit.Simple(m.Optionv(key, arg...)), 0) -} -func (m *Message) Append(key string, arg ...interface{}) string { - return kit.Select("", m.Appendv(key, arg...), 0) -} -func (m *Message) Appendv(key string, arg ...interface{}) []string { - if key == "_index" { - max := 0 - for _, k := range m.meta[MSG_APPEND] { - if len(m.meta[k]) > max { - max = len(m.meta[k]) - } - } - index := []string{} - for i := 0; i < max; i++ { - index = append(index, kit.Format(i)) - } - return index - } - if len(arg) > 0 { - m.meta[key] = kit.Simple(arg...) - } - return m.meta[key] -} -func (m *Message) Resultv(arg ...interface{}) []string { - if len(arg) > 0 { - m.meta[MSG_RESULT] = kit.Simple(arg...) - } - return m.meta[MSG_RESULT] -} -func (m *Message) Result(arg ...interface{}) string { - if len(arg) > 0 { - switch v := arg[0].(type) { - case int: - return kit.Select("", m.meta[MSG_RESULT], v) - } - } - return strings.Join(m.Resultv(arg...), "") -} - -func (m *Message) Logs(level string, arg ...interface{}) *Message { - list := []string{} - for i := 0; i < len(arg)-1; i += 2 { - list = append(list, fmt.Sprintf("%v: %v", arg[i], arg[i+1])) - } - m.Log(level, strings.Join(list, " ")) - return m -} -func (m *Message) Log(level string, str string, arg ...interface{}) *Message { - return m.log(level, str, arg...) -} -func (m *Message) log(level string, str string, arg ...interface{}) *Message { - if str = strings.TrimSpace(fmt.Sprintf(str, arg...)); Log != nil { - // 日志模块 - Log(m, level, str) - } - - // 日志颜色 - prefix, suffix := "", "" - switch level { - case LOG_ENABLE, LOG_IMPORT, LOG_CREATE, LOG_INSERT, LOG_EXPORT: - prefix, suffix = "\033[36;44m", "\033[0m" - - case LOG_LISTEN, LOG_SIGNAL, LOG_TIMERS, LOG_EVENTS: - prefix, suffix = "\033[33m", "\033[0m" - - case LOG_CMDS, LOG_START, LOG_SERVE: - prefix, suffix = "\033[32m", "\033[0m" - case LOG_COST: - prefix, suffix = "\033[33m", "\033[0m" - case LOG_WARN, LOG_ERROR, LOG_CLOSE: - prefix, suffix = "\033[31m", "\033[0m" - } - - _, file, line, _ := runtime.Caller(2) - ls := strings.Split(file, "/") - if len(ls) > 2 { - ls = ls[len(ls)-2:] - } - - if os.Getenv("ctx_mod") != "" && m != nil { - // 输出日志 - fmt.Fprintf(os.Stderr, "%s %02d %9s %s%s %s%s %s\n", - m.time.Format(ICE_TIME), m.code, fmt.Sprintf("%4s->%-4s", m.source.Name, m.target.Name), - prefix, level, str, suffix, - fmt.Sprintf("%s:%d", strings.Join(ls, "/"), line), - ) - } - return m -} -func (m *Message) Cost(str string, arg ...interface{}) *Message { - return m.log(LOG_COST, "%s: %s", m.Format("cost"), kit.Format(str, arg...)) -} -func (m *Message) Info(str string, arg ...interface{}) *Message { - return m.log(LOG_INFO, str, arg...) -} -func (m *Message) Warn(err bool, str string, arg ...interface{}) bool { - if err { - _, file, line, _ := runtime.Caller(1) - m.Echo("warn: ").Echo(str, arg...) - return m.log(LOG_WARN, "%s:%d %s", file, line, fmt.Sprintf(str, arg...)) != nil - } - return false -} -func (m *Message) Error(err bool, str string, arg ...interface{}) bool { - if err { - m.Echo("error: ").Echo(str, arg...) - m.log(LOG_ERROR, m.Format("stack")) - m.log(LOG_ERROR, str, arg...) - m.log(LOG_ERROR, m.Format("chain")) - return true - } - return false -} -func (m *Message) Trace(key string, str string, arg ...interface{}) *Message { - if m.Options(key) { - m.Echo("trace: ").Echo(str, arg...) - return m.log(LOG_TRACE, str, arg...) - } - return m -} - func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) *Message { defer func() { switch e := recover(); e { @@ -1161,453 +622,6 @@ func (m *Message) Preview(arg string) (res string) { return res } -func (m *Message) Prefile(favor string, id string) map[string]string { - res := map[string]string{} - m.Option("render", "") - m.Option("_action", "") - m.Cmd(WEB_FAVOR, kit.Select(m.Option("favor"), favor), id).Table(func(index int, value map[string]string, head []string) { - res[value["key"]] = value["value"] - }) - - res["content"] = m.Cmdx(CLI_SYSTEM, "sed", "-n", kit.Format("%d,%dp", kit.Int(res["extra.row"]), kit.Int(res["extra.row"])+3), res["extra.buf"]) - return res -} -func (m *Message) Prefix(arg ...string) string { - return kit.Keys(m.Cap(CTX_FOLLOW), arg) -} -func (m *Message) Save(arg ...string) *Message { - list := []string{} - for _, k := range arg { - list = append(list, kit.Keys(m.Cap(CTX_FOLLOW), k)) - } - m.Cmd(CTX_CONFIG, "save", kit.Keys(m.Cap(CTX_FOLLOW), "json"), list) - return m -} -func (m *Message) Load(arg ...string) *Message { - list := []string{} - for _, k := range arg { - list = append(list, kit.Keys(m.Cap(CTX_FOLLOW), k)) - } - m.Cmd(CTX_CONFIG, "load", kit.Keys(m.Cap(CTX_FOLLOW), "json"), list) - return m -} - -func (m *Message) Richs(key string, chain interface{}, raw interface{}, cb interface{}) (res map[string]interface{}) { - // 数据结构 - cache := m.Confm(key, chain) - if cache == nil { - return nil - } - meta, ok := cache[kit.MDB_META].(map[string]interface{}) - hash, ok := cache[kit.MDB_HASH].(map[string]interface{}) - if !ok { - return nil - } - - h := kit.Format(raw) - switch h { - case "*": - // 全部遍历 - switch cb := cb.(type) { - case func(string, string): - for k, v := range hash { - cb(k, kit.Format(v)) - } - case func(string, map[string]interface{}): - for k, v := range hash { - res = v.(map[string]interface{}) - cb(k, res) - } - } - return res - case "%": - // 随机选取 - if len(hash) > 0 { - list := []string{} - for k := range hash { - list = append(list, k) - } - h = list[rand.Intn(len(list))] - res, _ = hash[h].(map[string]interface{}) - } - default: - // 单个查询 - if res, ok = hash[h].(map[string]interface{}); !ok { - switch kit.Format(kit.Value(meta, kit.MDB_SHORT)) { - case "", "uniq": - default: - hh := kit.Hashs(h) - if res, ok = hash[hh].(map[string]interface{}); ok { - h = hh - break - } - - prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) - for _, k := range []string{h, hh} { - if f, e := os.Open(path.Join(prefix, kit.Keys(k, "json"))); e == nil { - defer f.Close() - if b, e := ioutil.ReadAll(f); e == nil { - if json.Unmarshal(b, &res) == e { - h = k - m.Log(LOG_IMPORT, "%s/%s.json", prefix, k) - break - } - } - } - } - } - } - } - - // 返回数据 - if res != nil { - switch cb := cb.(type) { - case func(map[string]interface{}): - cb(res) - case func(string, map[string]interface{}): - cb(h, res) - } - } - return res -} -func (m *Message) Rich(key string, chain interface{}, data interface{}) string { - // 数据结构 - cache := m.Confm(key, chain) - if cache == nil { - cache = map[string]interface{}{} - m.Confv(key, chain, cache) - } - meta, ok := cache[kit.MDB_META].(map[string]interface{}) - if !ok { - meta = map[string]interface{}{} - cache[kit.MDB_META] = meta - } - hash, ok := cache[kit.MDB_HASH].(map[string]interface{}) - if !ok { - hash = map[string]interface{}{} - cache[kit.MDB_HASH] = hash - } - - // 通用数据 - prefix := kit.Select("", "meta.", kit.Value(data, "meta") != nil) - if kit.Value(data, prefix+kit.MDB_TIME) == nil { - kit.Value(data, prefix+kit.MDB_TIME, m.Time()) - } - - // 生成键值 - h := "" - switch short := kit.Format(kit.Value(meta, kit.MDB_SHORT)); short { - case "": - h = kit.ShortKey(hash, 6) - case "uniq": - h = kit.Hashs("uniq") - case "data": - h = kit.Hashs(kit.Format(data)) - default: - if kit.Value(data, "meta") != nil { - h = kit.Hashs(kit.Format(kit.Value(data, "meta."+short))) - } else { - h = kit.Hashs(kit.Format(kit.Value(data, short))) - } - } - - // 添加数据 - if hash[h] = data; len(hash) >= kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.limit"), kit.Format(meta["limit"]))) { - least := kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.least"), kit.Format(meta["least"]))) - - // 时间淘汰 - list := []int{} - for _, v := range hash { - list = append(list, kit.Time(kit.Format(kit.Value(v, "time")))) - } - sort.Ints(list) - dead := list[len(list)-1-least] - - prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) - for k, v := range hash { - if kit.Time(kit.Format(kit.Value(v, "time"))) > dead { - break - } - - name := path.Join(prefix, kit.Keys(k, "json")) - if f, p, e := kit.Create(name); m.Assert(e) { - defer f.Close() - // 保存数据 - if n, e := f.WriteString(kit.Format(v)); m.Assert(e) { - m.Log(LOG_EXPORT, "%s: %d", p, n) - delete(hash, k) - } - } - } - } - - return h -} -func (m *Message) Grow(key string, chain interface{}, data interface{}) int { - // 数据结构 - cache := m.Confm(key, chain) - if cache == nil { - cache = map[string]interface{}{} - m.Confv(key, chain, cache) - } - meta, ok := cache[kit.MDB_META].(map[string]interface{}) - if !ok { - meta = map[string]interface{}{} - cache[kit.MDB_META] = meta - } - list, _ := cache[kit.MDB_LIST].([]interface{}) - - // 通用数据 - id := kit.Int(meta["count"]) + 1 - prefix := kit.Select("", "meta.", kit.Value(data, "meta") != nil) - if kit.Value(data, prefix+kit.MDB_ID, id); kit.Value(data, prefix+kit.MDB_TIME) == nil { - kit.Value(data, prefix+kit.MDB_TIME, kit.Select(m.Time(), m.Option("time"))) - } - - // 添加数据 - list = append(list, data) - cache[kit.MDB_LIST] = list - meta["count"] = id - - // 保存数据 - if len(list) >= kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.limit"), kit.Format(meta["limit"]))) { - least := kit.Int(kit.Select(m.Conf(WEB_CACHE, "meta.least"), kit.Format(meta["least"]))) - - record, _ := meta["record"].([]interface{}) - - // 文件命名 - prefix := path.Join(kit.Select(m.Conf(WEB_CACHE, "meta.store"), kit.Format(meta["store"])), key) - name := path.Join(prefix, kit.Keys(kit.Select("list", chain), "csv")) - if len(record) > 0 { - name = kit.Format(kit.Value(record, kit.Keys(len(record)-1, "file"))) - if s, e := os.Stat(name); e == nil { - if s.Size() > kit.Int64(kit.Select(m.Conf(WEB_CACHE, "meta.fsize"), kit.Format(meta["fsize"]))) { - name = fmt.Sprintf("%s/%s_%d.csv", prefix, kit.Select("list", chain), kit.Int(meta["offset"])) - } - } - } - - // 打开文件 - f, e := os.OpenFile(name, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) - if e != nil { - f, _, e = kit.Create(name) - m.Info("%s.%v create: %s", key, chain, name) - } else { - m.Info("%s.%v append: %s", key, chain, name) - } - defer f.Close() - s, e := f.Stat() - m.Assert(e) - - // 保存表头 - keys := []string{} - w := csv.NewWriter(f) - if s.Size() == 0 { - for k := range list[0].(map[string]interface{}) { - keys = append(keys, k) - } - sort.Strings(keys) - w.Write(keys) - m.Info("write head: %v", keys) - w.Flush() - s, e = f.Stat() - } else { - r := csv.NewReader(f) - keys, e = r.Read() - m.Info("read head: %v", keys) - } - - // 创建索引 - count := len(list) - least - offset := kit.Int(meta["offset"]) - meta["record"] = append(record, map[string]interface{}{ - "time": m.Time(), "offset": offset, "count": count, - "file": name, "position": s.Size(), - }) - - // 保存数据 - for i, v := range list { - if i >= count { - break - } - - val := v.(map[string]interface{}) - - values := []string{} - for _, k := range keys { - values = append(values, kit.Format(val[k])) - } - w.Write(values) - - if i < least { - list[i] = list[count+i] - } - } - - m.Log(LOG_INFO, "%s.%v save %s offset %v+%v", key, chain, name, offset, count) - meta["offset"] = offset + count - list = list[count:] - cache[kit.MDB_LIST] = list - w.Flush() - } - return id -} -func (m *Message) Grows(key string, chain interface{}, match string, value string, cb interface{}) map[string]interface{} { - // 数据结构 - cache := m.Confm(key, chain) - if cache == nil { - return nil - } - meta, ok := cache[kit.MDB_META].(map[string]interface{}) - list, ok := cache[kit.MDB_LIST].([]interface{}) - if !ok { - return nil - } - - // 数据范围 - offend := kit.Int(kit.Select("0", m.Option("cache.offend"))) - limit := kit.Int(kit.Select("10", m.Option("cache.limit"))) - current := kit.Int(meta["offset"]) - end := current + len(list) - offend - begin := end - limit - switch limit { - case -1: - begin = current - case -2: - begin = 0 - } - - if match == kit.MDB_ID { - begin, end = kit.Int(value)-1, kit.Int(value) - match, value = "", "" - } - - order := 0 - if begin < current { - // 读取文件 - m.Log(LOG_INFO, "%s.%v read %v-%v from %v-%v", key, chain, begin, end, current, current+len(list)) - store, _ := meta["record"].([]interface{}) - for s := len(store) - 1; s > -1; s-- { - item, _ := store[s].(map[string]interface{}) - line := kit.Int(item["offset"]) - m.Logs(LOG_INFO, "action", "check", "record", s, "offset", line, "count", item["count"]) - if begin < line && s > 0 { - if kit.Int(item["count"]) != 0 { - s -= (line - begin) / kit.Int(item["count"]) - } - // 向后查找 - continue - } - - for ; begin < end && s < len(store); s++ { - item, _ := store[s].(map[string]interface{}) - name := kit.Format(item["file"]) - pos := kit.Int(item["position"]) - offset := kit.Int(item["offset"]) - if offset+kit.Int(item["count"]) <= begin { - m.Logs(LOG_INFO, "action", "check", "record", s, "offset", line, "count", item["count"]) - // 向前查找 - continue - } - - if f, e := os.Open(name); m.Assert(e) { - defer f.Close() - // 打开文件 - r := csv.NewReader(f) - heads, _ := r.Read() - m.Logs(LOG_IMPORT, "head", heads) - - f.Seek(int64(pos), os.SEEK_SET) - r = csv.NewReader(f) - for i := offset; i < end; i++ { - lines, e := r.Read() - if e != nil { - m.Log(LOG_IMPORT, "load head %v", e) - break - } - if i < begin { - m.Logs(LOG_INFO, "action", "skip", "offset", i) - continue - } - - // 读取数据 - item := map[string]interface{}{} - for i := range heads { - if heads[i] == "extra" { - item[heads[i]] = kit.UnMarshal(lines[i]) - } else { - item[heads[i]] = lines[i] - } - } - m.Logs(LOG_IMPORT, "offset", i, "type", item["type"], "name", item["name"], "text", item["text"]) - - if match == "" || strings.Contains(kit.Format(item[match]), value) { - // 匹配成功 - switch cb := cb.(type) { - case func(int, map[string]interface{}): - cb(order, item) - case func(int, map[string]interface{}) bool: - if cb(order, item) { - return meta - } - } - order++ - } - begin = i + 1 - } - } - } - break - } - } - - if begin < current { - begin = current - } - for i := begin - current; i < end-current; i++ { - // 读取缓存 - if match == "" || strings.Contains(kit.Format(kit.Value(list[i], match)), value) { - switch cb := cb.(type) { - case func(int, map[string]interface{}): - cb(order, list[i].(map[string]interface{})) - case func(int, map[string]interface{}) bool: - if cb(order, list[i].(map[string]interface{})) { - return meta - } - } - order++ - } - } - return meta -} -func (m *Message) Show(cmd string, arg ...string) bool { - if len(arg) == 0 { - // 日志分类 - m.Richs(cmd, nil, "*", func(key string, value map[string]interface{}) { - m.Push(key, value["meta"]) - }) - return true - } - if len(arg) < 3 { - if m.Richs(cmd, nil, arg[0], func(key string, val map[string]interface{}) { - if len(arg) == 1 { - // 日志列表 - m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) { - m.Push(key, value) - }) - return - } - // 日志详情 - m.Grows(cmd, kit.Keys(kit.MDB_HASH, key), "id", arg[1], func(index int, value map[string]interface{}) { - m.Push("detail", value) - }) - }) != nil { - return true - } - } - return false -} - var count = int32(0) func (m *Message) AddCmd(cmd *Command) string {