mirror of
https://shylinux.com/x/icebergs
synced 2025-04-26 01:24:05 +08:00
opt ssh.connect
This commit is contained in:
parent
fbe99a51bd
commit
122c3295ff
@ -85,7 +85,7 @@ func init() {
|
||||
m.Push("code", _totp_get(value[SECRET], kit.Int(value[NUMBER]), period))
|
||||
|
||||
if len(arg) > 0 {
|
||||
m.PushQRCode("show", kit.Format(m.Conf(TOTP, kit.Keym(kit.MDB_LINK)), value[kit.MDB_NAME], value[SECRET]))
|
||||
m.PushQRCode("scan", kit.Format(m.Conf(TOTP, kit.Keym(kit.MDB_LINK)), value[kit.MDB_NAME], value[SECRET]))
|
||||
m.Echo(_totp_get(value[SECRET], kit.Int(value[NUMBER]), kit.Int64(value[PERIOD])))
|
||||
}
|
||||
})
|
||||
|
@ -13,12 +13,33 @@ import (
|
||||
|
||||
func NodeInfo(m *ice.Message, kind, name string) {
|
||||
name = strings.ReplaceAll(name, ".", "_")
|
||||
m.Conf(RUNTIME, "node.type", kind)
|
||||
m.Conf(RUNTIME, "node.name", name)
|
||||
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_TYPE), kind)
|
||||
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_NAME), name)
|
||||
ice.Info.NodeName = name
|
||||
ice.Info.NodeType = kind
|
||||
}
|
||||
|
||||
const (
|
||||
MAKE = "make"
|
||||
CONF = "conf"
|
||||
HOST = "host"
|
||||
BOOT = "boot"
|
||||
NODE = "node"
|
||||
)
|
||||
const (
|
||||
HOSTNAME = "hostname"
|
||||
PATHNAME = "pathname"
|
||||
USERNAME = "username"
|
||||
)
|
||||
const (
|
||||
CTX_SELF = "ctx_self"
|
||||
CTX_DEV = "ctx_dev"
|
||||
CTX_SHY = "ctx_shy"
|
||||
CTX_PID = "ctx_pid"
|
||||
CTX_USER = "ctx_user"
|
||||
CTX_SHARE = "ctx_share"
|
||||
CTX_RIVER = "ctx_river"
|
||||
)
|
||||
const CLI = "cli"
|
||||
|
||||
var Index = &ice.Context{Name: CLI, Help: "命令模块",
|
||||
@ -27,18 +48,18 @@ var Index = &ice.Context{Name: CLI, Help: "命令模块",
|
||||
m.Load()
|
||||
|
||||
// 启动配置
|
||||
for _, k := range []string{"ctx_self", "ctx_dev", "ctx_shy", "ctx_pid", "ctx_user", "ctx_share", "ctx_river"} {
|
||||
m.Conf(RUNTIME, kit.Keys("conf", k), os.Getenv(k))
|
||||
for _, k := range []string{CTX_SELF, CTX_DEV, CTX_SHY, CTX_PID, CTX_USER, CTX_SHARE, CTX_RIVER} {
|
||||
m.Conf(RUNTIME, kit.Keys(CONF, k), os.Getenv(k))
|
||||
}
|
||||
|
||||
// 主机信息
|
||||
m.Conf(RUNTIME, "host.GOARCH", runtime.GOARCH)
|
||||
m.Conf(RUNTIME, "host.GOOS", runtime.GOOS)
|
||||
m.Conf(RUNTIME, "host.pid", os.Getpid())
|
||||
m.Conf(RUNTIME, kit.Keys(HOST, "GOARCH"), runtime.GOARCH)
|
||||
m.Conf(RUNTIME, kit.Keys(HOST, "GOOS"), runtime.GOOS)
|
||||
m.Conf(RUNTIME, kit.Keys(HOST, "pid"), os.Getpid())
|
||||
|
||||
// 启动信息
|
||||
if name, e := os.Hostname(); e == nil {
|
||||
m.Conf(RUNTIME, "boot.hostname", kit.Select(name, os.Getenv("HOSTNAME")))
|
||||
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), kit.Select(name, os.Getenv("HOSTNAME")))
|
||||
}
|
||||
if name, e := os.Getwd(); e == nil {
|
||||
name = path.Base(kit.Select(name, os.Getenv("PWD")))
|
||||
@ -46,36 +67,36 @@ var Index = &ice.Context{Name: CLI, Help: "命令模块",
|
||||
name = ls[len(ls)-1]
|
||||
ls = strings.Split(name, "\\")
|
||||
name = ls[len(ls)-1]
|
||||
m.Conf(RUNTIME, "boot.pathname", name)
|
||||
m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME), name)
|
||||
}
|
||||
if m.Conf(RUNTIME, "boot.username", kit.Select(os.Getenv("USER"), os.Getenv("ctx_user"))) == "" {
|
||||
if m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME), kit.Select(os.Getenv(USER), os.Getenv(CTX_USER))) == "" {
|
||||
if user, e := user.Current(); e == nil && user.Name != "" {
|
||||
m.Conf(RUNTIME, "boot.username", kit.Select(user.Name, os.Getenv("ctx_user")))
|
||||
m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME), kit.Select(user.Name, os.Getenv(CTX_USER)))
|
||||
}
|
||||
}
|
||||
ice.Info.HostName = m.Conf(RUNTIME, "boot.hostname")
|
||||
ice.Info.PathName = m.Conf(RUNTIME, "boot.pathname")
|
||||
ice.Info.UserName = m.Conf(RUNTIME, "boot.username")
|
||||
ice.Info.HostName = m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME))
|
||||
ice.Info.PathName = m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME))
|
||||
ice.Info.UserName = m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME))
|
||||
|
||||
ice.Info.CtxShare = m.Conf(RUNTIME, "conf.ctx_share")
|
||||
ice.Info.CtxRiver = m.Conf(RUNTIME, "conf.ctx_river")
|
||||
ice.Info.CtxShare = m.Conf(RUNTIME, kit.Keys(CONF, CTX_SHARE))
|
||||
ice.Info.CtxRiver = m.Conf(RUNTIME, kit.Keys(CONF, CTX_RIVER))
|
||||
|
||||
// 启动次数
|
||||
count := kit.Int(m.Conf(RUNTIME, "boot.count")) + 1
|
||||
m.Conf(RUNTIME, "boot.count", count)
|
||||
count := kit.Int(m.Conf(RUNTIME, kit.Keys(BOOT, kit.MDB_COUNT))) + 1
|
||||
m.Conf(RUNTIME, kit.Keys(BOOT, kit.MDB_COUNT), count)
|
||||
|
||||
// 节点信息
|
||||
m.Conf(RUNTIME, "node.time", m.Time())
|
||||
NodeInfo(m, "worker", m.Conf(RUNTIME, "boot.pathname"))
|
||||
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_TIME), m.Time())
|
||||
NodeInfo(m, "worker", m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME)))
|
||||
m.Info("runtime %v", kit.Formats(m.Confv(RUNTIME)))
|
||||
|
||||
n := kit.Int(kit.Select("1", m.Conf(RUNTIME, "host.GOMAXPROCS")))
|
||||
m.Logs("host", "gomaxprocs", n)
|
||||
n := kit.Int(kit.Select("1", m.Conf(RUNTIME, kit.Keys(HOST, "GOMAXPROCS"))))
|
||||
m.Logs(HOST, "GOMAXPROCS", n)
|
||||
runtime.GOMAXPROCS(n)
|
||||
|
||||
// 版本信息
|
||||
kit.Fetch(kit.UnMarshal(kit.Format(ice.Info.Build)), func(key string, value interface{}) {
|
||||
m.Conf(RUNTIME, kit.Keys("make", strings.ToLower(key)), value)
|
||||
m.Conf(RUNTIME, kit.Keys(MAKE, strings.ToLower(key)), value)
|
||||
})
|
||||
}},
|
||||
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
|
@ -37,7 +37,7 @@ func _daemon_show(m *ice.Message, cmd *exec.Cmd, out, err string) {
|
||||
|
||||
m.Go(func() {
|
||||
h := m.Cmdx(mdb.INSERT, DAEMON, "", mdb.HASH,
|
||||
kit.MDB_STATUS, Status.Start, kit.SSH_PID, cmd.Process.Pid,
|
||||
kit.MDB_STATUS, START, kit.SSH_PID, cmd.Process.Pid,
|
||||
kit.SSH_CMD, strings.Join(cmd.Args, " "),
|
||||
kit.SSH_DIR, cmd.Dir, kit.SSH_ENV, kit.Select("", cmd.Env),
|
||||
mdb.CACHE_CLEAR_ON_EXIT, m.Option(mdb.CACHE_CLEAR_ON_EXIT),
|
||||
@ -46,11 +46,11 @@ func _daemon_show(m *ice.Message, cmd *exec.Cmd, out, err string) {
|
||||
|
||||
if e := cmd.Wait(); m.Warn(e != nil, cmd.Args, " ", e) {
|
||||
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, h,
|
||||
kit.MDB_STATUS, Status.Error, kit.MDB_ERROR, e)
|
||||
kit.MDB_STATUS, ERROR, kit.MDB_ERROR, e)
|
||||
} else {
|
||||
m.Cost("args", cmd.Args, "code", cmd.ProcessState.ExitCode())
|
||||
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, h,
|
||||
kit.MDB_STATUS, Status.Stop)
|
||||
kit.MDB_STATUS, STOP)
|
||||
}
|
||||
|
||||
if w, ok := m.Optionv(CMD_OUTPUT).(io.Closer); ok {
|
||||
@ -62,12 +62,6 @@ func _daemon_show(m *ice.Message, cmd *exec.Cmd, out, err string) {
|
||||
})
|
||||
}
|
||||
|
||||
var Status = struct{ Error, Start, Stop string }{
|
||||
Error: "error",
|
||||
Start: "start",
|
||||
Stop: "stop",
|
||||
}
|
||||
|
||||
const (
|
||||
DIR = "dir"
|
||||
ENV = "env"
|
||||
@ -80,6 +74,7 @@ const (
|
||||
const (
|
||||
RESTART = "restart"
|
||||
START = "start"
|
||||
ERROR = "error"
|
||||
STOP = "stop"
|
||||
)
|
||||
|
||||
@ -120,7 +115,7 @@ func init() {
|
||||
STOP: {Name: "stop", Help: "停止", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Option(mdb.FIELDS, "time,hash,status,pid,cmd,dir,env")
|
||||
m.Cmd(mdb.SELECT, DAEMON, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH)).Table(func(index int, value map[string]string, head []string) {
|
||||
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH), kit.MDB_STATUS, Status.Stop)
|
||||
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH), kit.MDB_STATUS, STOP)
|
||||
m.Cmdy(SYSTEM, "kill", "-9", value[kit.SSH_PID])
|
||||
})
|
||||
}},
|
||||
@ -129,8 +124,8 @@ func init() {
|
||||
}},
|
||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Option(mdb.FIELDS, "time,hash,status,pid,cmd,dir,env")
|
||||
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, Status.Error)
|
||||
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, Status.Stop)
|
||||
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, ERROR)
|
||||
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, STOP)
|
||||
}},
|
||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
if len(arg) == 0 { // 进程列表
|
||||
@ -138,7 +133,7 @@ func init() {
|
||||
m.Cmdy(mdb.SELECT, DAEMON, "", mdb.HASH)
|
||||
m.Table(func(index int, value map[string]string, head []string) {
|
||||
switch value[kit.MDB_STATUS] {
|
||||
case Status.Start:
|
||||
case START:
|
||||
m.PushButton(RESTART, STOP)
|
||||
default:
|
||||
m.PushButton(mdb.REMOVE)
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
const (
|
||||
DISKINFO = "diskinfo"
|
||||
IFCONFIG = "ifconfig"
|
||||
HOSTNAME = "hostname"
|
||||
HOSTINFO = "hostinfo"
|
||||
USERINFO = "userinfo"
|
||||
PROCINFO = "procinfo"
|
||||
@ -41,8 +40,8 @@ func init() {
|
||||
}},
|
||||
HOSTNAME: {Name: "hostname", Help: "主机域名", Hand: func(m *ice.Message, arg ...string) {
|
||||
if len(arg) > 0 {
|
||||
m.Conf(RUNTIME, "boot.hostname", arg[0])
|
||||
m.Conf(RUNTIME, "node.name", arg[0])
|
||||
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_NAME), arg[0])
|
||||
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), arg[0])
|
||||
ice.Info.HostName = arg[0]
|
||||
}
|
||||
m.Echo(ice.Info.HostName)
|
||||
|
@ -20,7 +20,7 @@ func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server
|
||||
return &Frame{}
|
||||
}
|
||||
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
|
||||
f.t = time.Tick(kit.Duration(m.Cap(ice.CTX_STREAM, ice.MOD_TICK)))
|
||||
f.t = time.Tick(kit.Duration(m.Conf(TIMER, kit.Keym("tick"))))
|
||||
f.s = make(chan os.Signal, ice.MOD_CHAN)
|
||||
f.e = make(chan bool, 1)
|
||||
return f
|
||||
@ -65,7 +65,8 @@ const GDB = "gdb"
|
||||
var Index = &ice.Context{Name: GDB, Help: "事件模块",
|
||||
Commands: map[string]*ice.Command{
|
||||
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
m.Cmd(nfs.SAVE, kit.Select(m.Conf(SIGNAL, kit.META_PATH), m.Conf(cli.RUNTIME, "conf.ctx_pid")), m.Conf(cli.RUNTIME, "host.pid"))
|
||||
m.Cmd(nfs.SAVE, kit.Select(m.Conf(SIGNAL, kit.META_PATH), m.Conf(cli.RUNTIME, kit.Keys(cli.CONF, cli.CTX_PID))),
|
||||
m.Conf(cli.RUNTIME, kit.Keys(cli.HOST, "pid")))
|
||||
|
||||
m.Cmd(SIGNAL, LISTEN, SIGNAL, "3", kit.MDB_NAME, "退出", kit.SSH_CMD, "exit 0")
|
||||
m.Cmd(SIGNAL, LISTEN, SIGNAL, "2", kit.MDB_NAME, "重启", kit.SSH_CMD, "exit 1")
|
||||
|
@ -1,7 +1,9 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"syscall"
|
||||
|
||||
ice "github.com/shylinux/icebergs"
|
||||
@ -23,6 +25,18 @@ func _signal_action(m *ice.Message, s int) {
|
||||
})
|
||||
}
|
||||
|
||||
func SignalNotify(m *ice.Message, sig int, cb func()) {
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.Signal(sig))
|
||||
m.Go(func() {
|
||||
for {
|
||||
if _, ok := <-ch; ok {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
LISTEN = "listen"
|
||||
ACTION = "action"
|
||||
@ -33,7 +47,7 @@ func init() {
|
||||
Index.Merge(&ice.Context{
|
||||
Configs: map[string]*ice.Config{
|
||||
SIGNAL: {Name: SIGNAL, Help: "信号器", Value: kit.Data(
|
||||
kit.MDB_PATH, "var/run/ice.pid", kit.MDB_SHORT, SIGNAL,
|
||||
kit.MDB_PATH, path.Join(ice.VAR_RUN, "ice.pid"), kit.MDB_SHORT, SIGNAL,
|
||||
)},
|
||||
},
|
||||
Commands: map[string]*ice.Command{
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func _timer_create(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.INSERT, TIMER, "", mdb.HASH, "delay", "10ms", "interval", "10m", "order", 1, "next", m.Time(m.Option("delay")), arg)
|
||||
m.Cmdy(mdb.INSERT, TIMER, "", mdb.HASH, DELAY, "10ms", INTERVAL, "10m", ORDER, 1, NEXT, m.Time(m.Option(DELAY)), arg)
|
||||
}
|
||||
func _timer_action(m *ice.Message, arg ...string) {
|
||||
now := time.Now().UnixNano()
|
||||
@ -20,19 +20,25 @@ func _timer_action(m *ice.Message, arg ...string) {
|
||||
return
|
||||
}
|
||||
|
||||
order := kit.Int(value["order"])
|
||||
if n := kit.Time(kit.Format(value["next"])); now > n && order > 0 {
|
||||
m.Logs(TIMER, "key", key, "order", order)
|
||||
order := kit.Int(value[ORDER])
|
||||
if n := kit.Time(kit.Format(value[NEXT])); now > n && order > 0 {
|
||||
m.Logs(TIMER, kit.MDB_KEY, key, ORDER, order)
|
||||
|
||||
msg := m.Cmd(value[kit.SSH_CMD])
|
||||
m.Grow(TIMER, kit.Keys(kit.MDB_HASH, key), kit.Dict("res", msg.Result()))
|
||||
if value["order"] = kit.Format(order - 1); order > 1 {
|
||||
value["next"] = msg.Time(value["interval"])
|
||||
if value[ORDER] = kit.Format(order - 1); order > 1 {
|
||||
value[NEXT] = msg.Time(value[INTERVAL])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
DELAY = "delay"
|
||||
INTERVAL = "interval"
|
||||
ORDER = "order"
|
||||
NEXT = "next"
|
||||
)
|
||||
const TIMER = "timer"
|
||||
|
||||
func init() {
|
||||
@ -53,7 +59,7 @@ func init() {
|
||||
}},
|
||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Option(mdb.FIELDS, "time,hash,delay,interval,order,next,cmd")
|
||||
m.Cmdy(mdb.PRUNES, TIMER, "", mdb.HASH, "order", 0)
|
||||
m.Cmdy(mdb.PRUNES, TIMER, "", mdb.HASH, ORDER, 0)
|
||||
}},
|
||||
|
||||
ACTION: {Name: "action", Help: "执行", Hand: func(m *ice.Message, arg ...string) {
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func _file_name(m *ice.Message, arg ...string) string {
|
||||
return kit.Select(path.Join(m.Option(ice.MSG_LOCAL), "usr/local/export", path.Join(arg[:2]...), arg[2]), arg, 3)
|
||||
return kit.Select(path.Join(m.Option(ice.MSG_LOCAL), ice.USR_LOCAL, EXPORT, path.Join(arg[:2]...), arg[2]), arg, 3)
|
||||
}
|
||||
func _domain_chain(m *ice.Message, chain string) string {
|
||||
return kit.Keys(m.Option(ice.MSG_DOMAIN), chain)
|
||||
@ -157,7 +157,7 @@ func _list_select(m *ice.Message, prefix, chain, field, value string) {
|
||||
}
|
||||
fields := _list_fields(m)
|
||||
cb := m.Optionv(kit.Keycb(SELECT))
|
||||
m.Grows(prefix, chain, kit.Select(m.Option("cache.field"), field), kit.Select(m.Option(CACHE_VALUE), value), func(index int, val map[string]interface{}) {
|
||||
m.Grows(prefix, chain, kit.Select(m.Option(CACHE_FIELD), field), kit.Select(m.Option(CACHE_VALUE), value), func(index int, val map[string]interface{}) {
|
||||
val = kit.GetMeta(val)
|
||||
switch cb := cb.(type) {
|
||||
case func(fields []string, value map[string]interface{}):
|
||||
@ -263,7 +263,7 @@ func _list_inputs(m *ice.Message, prefix, chain string, field, value string) {
|
||||
m.Push(field, k)
|
||||
m.Push(kit.MDB_COUNT, i)
|
||||
}
|
||||
m.Sort(kit.MDB_COUNT, "int_r")
|
||||
m.SortIntR(kit.MDB_COUNT)
|
||||
}
|
||||
|
||||
func _zone_fields(m *ice.Message) []string {
|
||||
@ -404,6 +404,7 @@ const (
|
||||
SELECT = "select"
|
||||
DELETE = "delete"
|
||||
REMOVE = "remove"
|
||||
REVERT = "revert"
|
||||
|
||||
EXPORT = "export"
|
||||
IMPORT = "import"
|
||||
@ -412,14 +413,9 @@ const (
|
||||
)
|
||||
const (
|
||||
CACHE_LIMIT = "cache.limit"
|
||||
CACHE_FILED = "cache.field"
|
||||
CACHE_FIELD = "cache.field"
|
||||
CACHE_VALUE = "cache.value"
|
||||
|
||||
CACHE_STATUS = "cache.status"
|
||||
CACHE_ACTION = "cache.action"
|
||||
CACHE_BEGIN = "cache.begin"
|
||||
CACHE_HASH = "cache.hash"
|
||||
|
||||
CACHE_CLEAR_ON_EXIT = "cache.clear.on.exit"
|
||||
)
|
||||
|
||||
|
@ -178,7 +178,7 @@ func init() {
|
||||
_dir_search(m, arg[0], arg[1])
|
||||
}},
|
||||
mdb.RENDER: {Name: "render type name text", Help: "渲染", Hand: func(m *ice.Message, arg ...string) {
|
||||
_dir_show(m, arg[2], arg[1], 0, m.Option(DIR_DEEP) == "true", kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)),
|
||||
_dir_show(m, arg[2], arg[1], 0, m.Option(DIR_DEEP) == ice.TRUE, kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)),
|
||||
nil, kit.Split("time,size,type,path"))
|
||||
}},
|
||||
|
||||
|
@ -28,7 +28,7 @@ func _tail_create(m *ice.Message, arg ...string) {
|
||||
|
||||
m.Option(cli.CMD_OUTPUT, w)
|
||||
m.Option(cli.CMD_ERRPUT, w)
|
||||
m.Option(mdb.CACHE_CLEAR_ON_EXIT, "true")
|
||||
m.Option(mdb.CACHE_CLEAR_ON_EXIT, ice.TRUE)
|
||||
m.Cmd(cli.DAEMON, TAIL, "-n", "0", "-f", file)
|
||||
})
|
||||
}
|
||||
@ -55,7 +55,6 @@ func init() {
|
||||
offend = total - kit.Int(kit.Select("10", arg, 2))
|
||||
m.Toast("已经是最后一页啦!")
|
||||
}
|
||||
|
||||
m.ProcessRewrite("offend", offend)
|
||||
}},
|
||||
"next": {Name: "next", Help: "下一页", Hand: func(m *ice.Message, arg ...string) {
|
||||
@ -68,7 +67,7 @@ func init() {
|
||||
}},
|
||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
m.Option(mdb.FIELDS, kit.Select("time,hash,count,name,file", kit.Select("time,id,file,text", mdb.DETAIL, len(arg) > 1 && arg[1] != ""), len(arg) > 0))
|
||||
m.Option("cache.limit", kit.Select("10", arg, 2))
|
||||
m.Option(mdb.CACHE_LIMIT, kit.Select("10", arg, 2))
|
||||
m.Option("cache.offend", kit.Select("0", arg, 3))
|
||||
|
||||
if m.Cmdy(mdb.SELECT, TAIL, "", mdb.ZONE, arg); len(arg) == 0 {
|
||||
|
@ -44,7 +44,7 @@ func init() {
|
||||
},
|
||||
Commands: map[string]*ice.Command{
|
||||
TRASH: {Name: "trash file auto prunes", Help: "回收站", Action: map[string]*ice.Action{
|
||||
"recover": {Name: "recover", Help: "恢复", Hand: func(m *ice.Message, arg ...string) {
|
||||
mdb.REVERT: {Name: "revert", Help: "恢复", Hand: func(m *ice.Message, arg ...string) {
|
||||
os.Rename(m.Option(kit.MDB_FILE), m.Option(kit.MDB_FROM))
|
||||
m.Cmd(mdb.DELETE, TRASH, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH))
|
||||
}},
|
||||
@ -59,7 +59,7 @@ func init() {
|
||||
if len(arg) == 0 {
|
||||
m.Option(mdb.FIELDS, "time,hash,file,from")
|
||||
m.Cmdy(mdb.SELECT, TRASH, "", mdb.HASH)
|
||||
m.PushAction("recover", mdb.REMOVE)
|
||||
m.PushAction(mdb.REVERT, mdb.REMOVE)
|
||||
return
|
||||
}
|
||||
_trash_create(m, arg[0])
|
||||
|
@ -8,11 +8,11 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ice "github.com/shylinux/icebergs"
|
||||
"github.com/shylinux/icebergs/base/aaa"
|
||||
"github.com/shylinux/icebergs/base/cli"
|
||||
"github.com/shylinux/icebergs/base/gdb"
|
||||
"github.com/shylinux/icebergs/base/mdb"
|
||||
"github.com/shylinux/icebergs/base/nfs"
|
||||
"github.com/shylinux/icebergs/base/tcp"
|
||||
@ -21,72 +21,103 @@ import (
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func _ssh_tick(m *ice.Message, pw io.Writer) {
|
||||
if m.Option("tick") == "" {
|
||||
return
|
||||
}
|
||||
m.Go(func() {
|
||||
for {
|
||||
m.Sleep(m.Option("tick"))
|
||||
pw.Write([]byte("# " + time.Now().Format(ice.MOD_TIME) + "\n"))
|
||||
}
|
||||
})
|
||||
}
|
||||
func _ssh_password(m *ice.Message, file string) {
|
||||
if f, e := os.Open(file); e == nil {
|
||||
func _ssh_open(m *ice.Message, arg ...string) {
|
||||
// 加载配置
|
||||
if f, e := os.Open(m.Option("authfile")); e == nil {
|
||||
defer f.Close()
|
||||
|
||||
var data interface{}
|
||||
json.NewDecoder(f).Decode(&data)
|
||||
|
||||
kit.Fetch(data, func(key string, value string) { m.Option(key, value) })
|
||||
kit.Fetch(data, func(key string, value interface{}) { m.Option(key, kit.Simple(value)) })
|
||||
}
|
||||
|
||||
_ssh_dial(m, func(c net.Conn) {
|
||||
// 保存界面
|
||||
fd := int(os.Stdin.Fd())
|
||||
if oldState, err := terminal.MakeRaw(fd); err == nil {
|
||||
defer terminal.Restore(fd, oldState)
|
||||
}
|
||||
|
||||
// 设置宽高
|
||||
w, h, _ := terminal.GetSize(fd)
|
||||
c.Write([]byte(fmt.Sprintf("height:%d,width:%d\n", h, w)))
|
||||
|
||||
// 初始命令
|
||||
for _, item := range kit.Simple(m.Optionv("list")) {
|
||||
m.Sleep("10ms")
|
||||
c.Write([]byte(item + "\n"))
|
||||
}
|
||||
|
||||
m.Go(func() { io.Copy(os.Stdout, c) })
|
||||
io.Copy(c, os.Stdin)
|
||||
}, arg...)
|
||||
}
|
||||
func _ssh_stream(m *ice.Message, stdin *os.File) (io.Reader, io.Writer) {
|
||||
pr, pw := io.Pipe()
|
||||
m.Go(func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
if n, e := stdin.Read(buf); m.Assert(e) {
|
||||
pw.Write(buf[:n])
|
||||
func _ssh_dial(m *ice.Message, cb func(net.Conn), arg ...string) {
|
||||
p := path.Join(os.Getenv(cli.HOME), ".ssh/", fmt.Sprintf("%s@%s:%s", m.Option(aaa.USERNAME), m.Option(tcp.HOST), m.Option(tcp.PORT)))
|
||||
if _, e := os.Stat(p); e == nil {
|
||||
if c, e := net.Dial("unix", p); e == nil {
|
||||
cb(c) // 会话连接
|
||||
return
|
||||
}
|
||||
os.Remove(p)
|
||||
}
|
||||
|
||||
var client *ssh.Client
|
||||
if l, e := net.Listen("unix", p); m.Assert(e) {
|
||||
defer func() { os.Remove(p) }()
|
||||
defer l.Close()
|
||||
|
||||
m.Go(func() {
|
||||
for {
|
||||
c, e := l.Accept()
|
||||
m.Assert(e)
|
||||
|
||||
func(c net.Conn) {
|
||||
w, h, _ := terminal.GetSize(int(os.Stdin.Fd()))
|
||||
buf := make([]byte, ice.MOD_BUFS)
|
||||
if n, e := c.Read(buf); m.Assert(e) {
|
||||
fmt.Sscanf(string(buf[:n]), "height:%d,width:%d", &h, &w)
|
||||
}
|
||||
|
||||
m.Go(func() {
|
||||
defer c.Close()
|
||||
|
||||
session, e := client.NewSession()
|
||||
m.Assert(e)
|
||||
|
||||
session.Stdin = c
|
||||
session.Stdout = c
|
||||
session.Stderr = c
|
||||
|
||||
session.RequestPty(os.Getenv("TERM"), h, w, ssh.TerminalModes{
|
||||
ssh.ECHO: 1,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
})
|
||||
|
||||
gdb.SignalNotify(m, 28, func() {
|
||||
w, h, _ := terminal.GetSize(int(os.Stdin.Fd()))
|
||||
session.WindowChange(h, w)
|
||||
})
|
||||
|
||||
session.Shell()
|
||||
session.Wait()
|
||||
})
|
||||
}(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
m.Option(kit.Keycb(tcp.DIAL), func(c net.Conn) {
|
||||
client = _ssh_conn(m, c, m.Option(aaa.USERNAME), m.Option(tcp.HOST)+":"+m.Option(tcp.PORT))
|
||||
|
||||
if c, e := net.Dial("unix", p); e == nil {
|
||||
cb(c) // 会话连接
|
||||
}
|
||||
})
|
||||
return pr, pw
|
||||
}
|
||||
func _ssh_store(stdio *os.File) func() {
|
||||
fd := int(stdio.Fd())
|
||||
oldState, err := terminal.MakeRaw(fd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return func() { terminal.Restore(fd, oldState) }
|
||||
}
|
||||
|
||||
func _ssh_session(m *ice.Message, client *ssh.Client, w, h int, stdin io.Reader, stdout, stderr io.Writer) *ssh.Session {
|
||||
session, e := client.NewSession()
|
||||
m.Assert(e)
|
||||
|
||||
session.Stdin = stdin
|
||||
session.Stdout = stdout
|
||||
session.Stderr = stderr
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
|
||||
session.RequestPty(os.Getenv("TERM"), h, w, modes)
|
||||
session.Shell()
|
||||
return session
|
||||
}
|
||||
func _ssh_init(m *ice.Message, pw io.Writer) {
|
||||
for _, k := range []string{"one", "two"} {
|
||||
if m.Sleep("100ms"); m.Option(k) != "" {
|
||||
pw.Write([]byte(m.Option(k) + "\n"))
|
||||
}
|
||||
}
|
||||
m.Cmdy(tcp.CLIENT, tcp.DIAL, kit.MDB_TYPE, SSH, kit.MDB_NAME, m.Option(tcp.HOST),
|
||||
tcp.PORT, m.Option(tcp.PORT), tcp.HOST, m.Option(tcp.HOST), arg)
|
||||
}
|
||||
func _ssh_conn(m *ice.Message, conn net.Conn, username, hostport string) *ssh.Client {
|
||||
methods := []ssh.AuthMethod{}
|
||||
@ -128,76 +159,6 @@ func _ssh_conn(m *ice.Message, conn net.Conn, username, hostport string) *ssh.Cl
|
||||
return ssh.NewClient(c, chans, reqs)
|
||||
}
|
||||
|
||||
func _ssh_open(m *ice.Message, arg ...string) {
|
||||
var client *ssh.Client
|
||||
w, h, _ := terminal.GetSize(int(os.Stdin.Fd()))
|
||||
|
||||
_ssh_password(m, m.Option("authfile"))
|
||||
p := path.Join(os.Getenv("HOME"), ".ssh/", fmt.Sprintf("%s@%s", m.Option("username"), m.Option("host")))
|
||||
if _, e := os.Stat(p); e == nil {
|
||||
if c, e := net.Dial("unix", p); e == nil {
|
||||
|
||||
pr, pw := _ssh_stream(m, os.Stdin)
|
||||
defer _ssh_store(os.Stdout)()
|
||||
defer _ssh_store(os.Stdin)()
|
||||
|
||||
c.Write([]byte(fmt.Sprintf("height:%d,width:%d\n", h, w)))
|
||||
|
||||
m.Go(func() { io.Copy(c, pr) })
|
||||
_ssh_init(m, pw)
|
||||
m.Echo("logout\n")
|
||||
io.Copy(os.Stdout, c)
|
||||
return
|
||||
} else {
|
||||
os.Remove(p)
|
||||
}
|
||||
}
|
||||
|
||||
if l, e := net.Listen("unix", p); m.Assert(e) {
|
||||
defer func() { os.Remove(p) }()
|
||||
defer l.Close()
|
||||
|
||||
m.Go(func() {
|
||||
for {
|
||||
if c, e := l.Accept(); e == nil {
|
||||
buf := make([]byte, 1024)
|
||||
if n, e := c.Read(buf); m.Assert(e) {
|
||||
fmt.Sscanf(string(buf[:n]), "height:%d,width:%d", &h, &w)
|
||||
}
|
||||
|
||||
session := _ssh_session(m, client, w, h, c, c, c)
|
||||
func(session *ssh.Session) {
|
||||
m.Go(func() {
|
||||
defer c.Close()
|
||||
session.Wait()
|
||||
})
|
||||
}(session)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
m.Option(kit.Keycb(tcp.DIAL), func(c net.Conn) {
|
||||
client = _ssh_conn(m, c, m.Option(aaa.USERNAME), m.Option(tcp.HOST)+":"+m.Option(tcp.PORT))
|
||||
|
||||
pr, pw := _ssh_stream(m, os.Stdin)
|
||||
defer _ssh_store(os.Stdout)()
|
||||
defer _ssh_store(os.Stdin)()
|
||||
|
||||
session := _ssh_session(m, client, w, h, pr, os.Stdout, os.Stderr)
|
||||
_ssh_init(m, pw)
|
||||
_ssh_tick(m, pw)
|
||||
session.Wait()
|
||||
})
|
||||
|
||||
m.Cmdy(tcp.CLIENT, tcp.DIAL, kit.MDB_TYPE, "ssh", kit.MDB_NAME, m.Option(tcp.HOST),
|
||||
tcp.PORT, m.Option(tcp.PORT), tcp.HOST, m.Option(tcp.HOST), arg)
|
||||
|
||||
m.Echo("exit %s\n", m.Option(tcp.HOST))
|
||||
}
|
||||
|
||||
const CONNECT = "connect"
|
||||
|
||||
func init() {
|
||||
@ -207,6 +168,9 @@ func init() {
|
||||
},
|
||||
Commands: map[string]*ice.Command{
|
||||
CONNECT: {Name: "connect hash auto dial prunes", Help: "连接", Action: map[string]*ice.Action{
|
||||
tcp.OPEN: {Name: "open authfile= username=shy password= verfiy= host=shylinux.com port=22 private=.ssh/id_rsa", Help: "终端", Hand: func(m *ice.Message, arg ...string) {
|
||||
_ssh_open(m, arg...)
|
||||
}},
|
||||
tcp.DIAL: {Name: "dial username=shy host=shylinux.com port=22 private=.ssh/id_rsa", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Option(kit.Keycb(tcp.DIAL), func(c net.Conn) {
|
||||
client := _ssh_conn(m, c, kit.Select("shy", m.Option(aaa.USERNAME)),
|
||||
@ -225,9 +189,6 @@ func init() {
|
||||
m.Cmds(tcp.CLIENT, tcp.DIAL, kit.MDB_TYPE, SSH, kit.MDB_NAME, m.Option(aaa.USERNAME),
|
||||
tcp.PORT, m.Option(tcp.PORT), tcp.HOST, m.Option(tcp.HOST))
|
||||
}},
|
||||
tcp.OPEN: {Name: "open authfile= username=shy password= verfiy= host=shylinux.com port=22 private=.ssh/id_rsa tick=", Help: "终端", Hand: func(m *ice.Message, arg ...string) {
|
||||
_ssh_open(m, arg...)
|
||||
}},
|
||||
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.DELETE, CONNECT, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH))
|
||||
}},
|
||||
@ -243,7 +204,7 @@ func init() {
|
||||
|
||||
h := m.Rich(SESSION, "", kit.Data(kit.MDB_STATUS, tcp.OPEN, CONNECT, key))
|
||||
|
||||
if session, e := _ssh_sess(m, h, client); m.Assert(e) {
|
||||
if session, e := _ssh_session(m, h, client); m.Assert(e) {
|
||||
session.Shell()
|
||||
session.Wait()
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package ssh
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
ice "github.com/shylinux/icebergs"
|
||||
"github.com/shylinux/icebergs/base/ctx"
|
||||
@ -12,7 +11,7 @@ import (
|
||||
kit "github.com/shylinux/toolkits"
|
||||
)
|
||||
|
||||
func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, error) {
|
||||
func _ssh_session(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, error) {
|
||||
session, e := client.NewSession()
|
||||
m.Assert(e)
|
||||
|
||||
@ -23,14 +22,13 @@ func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, erro
|
||||
m.Assert(e)
|
||||
|
||||
m.Go(func() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
buf := make([]byte, 4096)
|
||||
n, e := out.Read(buf)
|
||||
if e != nil {
|
||||
break
|
||||
}
|
||||
|
||||
m.Debug(string(buf[:n]))
|
||||
m.Grow(SESSION, kit.Keys(kit.MDB_HASH, h), kit.Dict(
|
||||
kit.MDB_TYPE, RES, kit.MDB_TEXT, string(buf[:n]),
|
||||
))
|
||||
@ -38,35 +36,24 @@ func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, erro
|
||||
})
|
||||
|
||||
m.Richs(SESSION, "", h, func(key string, value map[string]interface{}) {
|
||||
kit.Value(value, "meta.output", out)
|
||||
kit.Value(value, "meta.input", in)
|
||||
kit.Value(value, kit.Keym(OUTPUT), out)
|
||||
kit.Value(value, kit.Keym(INPUT), in)
|
||||
})
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func _watch(m *ice.Message, from io.Reader, to io.Writer, cb func([]byte)) {
|
||||
m.Go(func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, e := from.Read(buf)
|
||||
if e != nil {
|
||||
cb(nil)
|
||||
break
|
||||
}
|
||||
cb(buf[:n])
|
||||
to.Write(buf[:n])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
TTY = "tty"
|
||||
ENV = "env"
|
||||
ARG = "arg"
|
||||
CMD = "cmd"
|
||||
ARG = "arg"
|
||||
RES = "res"
|
||||
)
|
||||
const (
|
||||
INPUT = "input"
|
||||
OUTPUT = "output"
|
||||
)
|
||||
|
||||
const SESSION = "session"
|
||||
|
||||
@ -77,9 +64,16 @@ func init() {
|
||||
},
|
||||
Commands: map[string]*ice.Command{
|
||||
SESSION: {Name: "session hash id auto command prunes", Help: "会话", Action: map[string]*ice.Action{
|
||||
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.DELETE, SESSION, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH))
|
||||
}},
|
||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.PRUNES, SESSION, "", mdb.HASH, kit.MDB_STATUS, tcp.ERROR)
|
||||
m.Cmdy(mdb.PRUNES, SESSION, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE)
|
||||
}},
|
||||
ctx.COMMAND: {Name: "command cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Richs(SESSION, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) {
|
||||
if w, ok := kit.Value(value, "meta.input").(io.Writer); ok {
|
||||
if w, ok := kit.Value(value, kit.Keym(INPUT)).(io.Writer); ok {
|
||||
m.Grow(SESSION, kit.Keys(kit.MDB_HASH, key), kit.Dict(kit.MDB_TYPE, CMD, kit.MDB_TEXT, m.Option(CMD)))
|
||||
n, e := w.Write([]byte(m.Option(CMD) + "\n"))
|
||||
m.Debug("%v %v", n, e)
|
||||
@ -87,42 +81,19 @@ func init() {
|
||||
})
|
||||
m.ProcessRefresh("300ms")
|
||||
}},
|
||||
"bind": {Name: "bind", Help: "绑定", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Richs(SESSION, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) {
|
||||
value = kit.GetMeta(value)
|
||||
|
||||
input := value["input"].(io.Writer)
|
||||
_watch(m, os.Stdin, input, func(buf []byte) {
|
||||
m.Debug("input %v", string(buf))
|
||||
})
|
||||
|
||||
output := value["output"].(io.Reader)
|
||||
_watch(m, output, os.Stdout, func(buf []byte) {
|
||||
m.Debug("output %v", string(buf))
|
||||
})
|
||||
})
|
||||
}},
|
||||
|
||||
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.DELETE, SESSION, "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH))
|
||||
}},
|
||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||
m.Cmdy(mdb.PRUNES, SESSION, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE)
|
||||
}},
|
||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
if len(arg) == 0 {
|
||||
m.Option(mdb.FIELDS, "time,hash,status,count,connect")
|
||||
if m.Cmdy(mdb.SELECT, SESSION, "", mdb.HASH, kit.MDB_HASH, arg); len(arg) == 0 {
|
||||
m.Table(func(index int, value map[string]string, head []string) {
|
||||
m.PushButton(kit.Select("bind", mdb.REMOVE, value[kit.MDB_STATUS] == tcp.CLOSE))
|
||||
m.PushButton(kit.Select("", mdb.REMOVE, value[kit.MDB_STATUS] == tcp.CLOSE))
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
m.Option(mdb.FIELDS, kit.Select("time,id,type,text", mdb.DETAIL, len(arg) > 1))
|
||||
m.Fields(len(arg) == 1, "time,id,type,text")
|
||||
m.Cmdy(mdb.SELECT, SESSION, kit.Keys(kit.MDB_HASH, arg[0]), mdb.LIST, kit.MDB_ID, arg[1:])
|
||||
m.Sort(kit.MDB_ID)
|
||||
}},
|
||||
},
|
||||
})
|
||||
|
1
conf.go
1
conf.go
@ -40,6 +40,7 @@ const ( // DIR
|
||||
INDEX_SH = "index.sh"
|
||||
|
||||
VAR_TMP = "var/tmp"
|
||||
VAR_RUN = "var/run"
|
||||
VAR_LOG = "var/log"
|
||||
VAR_CONF = "var/conf"
|
||||
VAR_DATA = "var/data"
|
||||
|
6
exec.go
6
exec.go
@ -1,13 +1,13 @@
|
||||
package ice
|
||||
|
||||
import (
|
||||
kit "github.com/shylinux/toolkits"
|
||||
"github.com/shylinux/toolkits/task"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
kit "github.com/shylinux/toolkits"
|
||||
"github.com/shylinux/toolkits/task"
|
||||
)
|
||||
|
||||
func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) *Message {
|
||||
|
Loading…
x
Reference in New Issue
Block a user