diff --git a/cmd/main.go b/cmd/main.go index ba981e9..b2c0774 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -36,7 +36,7 @@ func (s *subServer) Start(ctx context.Context) (err error) { []string{"SSS", "aaa"}, }) }) - + kos.Command().Handle("/users", "test command", func(ctx *cli.Context) (err error) { return ctx.Success([]*users{ {Name: "Zhan", Age: 10, Tags: []string{"a", "b"}}, diff --git a/entry/cli/serialize.go b/entry/cli/serialize.go index 6842ad3..2b75877 100644 --- a/entry/cli/serialize.go +++ b/entry/cli/serialize.go @@ -191,8 +191,12 @@ func serializeArray(val []any) (buf []byte, err error) { row := make([]any, 0, rv.Type().NumField()) for j := 0; j < rv.Type().NumField(); j++ { st := rv.Type().Field(j).Tag - if columnName, ok = st.Lookup("name"); !ok { + if columnName, ok = st.Lookup("kos"); !ok { columnName = strings.ToUpper(rv.Type().Field(j).Name) + } else { + if columnName == "-" { + continue + } } row = append(row, columnName) } diff --git a/entry/cli/server.go b/entry/cli/server.go index 61859eb..18ed174 100644 --- a/entry/cli/server.go +++ b/entry/cli/server.go @@ -10,6 +10,7 @@ import ( "runtime" "strings" "sync" + "sync/atomic" "time" "git.nspix.com/golang/kos/util/env" @@ -29,6 +30,7 @@ type Server struct { middleware []Middleware router *Router l net.Listener + exitFlag int32 } func (svr *Server) applyContext() *Context { @@ -200,10 +202,14 @@ func (svr *Server) Serve(l net.Listener) (err error) { return ctx.Success(svr.router.String()) }) svr.serve() + atomic.StoreInt32(&svr.exitFlag, 0) return } func (svr *Server) Shutdown() (err error) { + if !atomic.CompareAndSwapInt32(&svr.exitFlag, 0, 1) { + return + } err = svr.l.Close() svr.ctxMap.Range(func(key, value any) bool { if ctx, ok := value.(*Context); ok { diff --git a/entry/http/server.go b/entry/http/server.go index 37b596c..cebcfd7 100644 --- a/entry/http/server.go +++ b/entry/http/server.go @@ -9,6 +9,7 @@ import ( "path" "strings" "sync" + "sync/atomic" "time" ) @@ -26,6 +27,7 @@ type Server struct { fileSystem http.FileSystem beforeRequests []HandleFunc anyRequests map[string]http.Handler + exitFlag int32 } func (svr *Server) applyContext() *Context { @@ -216,10 +218,14 @@ func (svr *Server) Serve(l net.Listener) (err error) { } svr.router.NotFound = NotFound{} svr.router.MethodNotAllowed = NotAllowed{} + atomic.StoreInt32(&svr.exitFlag, 0) return svr.serve.Serve(l) } func (svr *Server) Shutdown() (err error) { + if !atomic.CompareAndSwapInt32(&svr.exitFlag, 0, 1) { + return + } if svr.serve != nil { err = svr.serve.Shutdown(svr.ctx) } diff --git a/instance.go b/instance.go index d6de4b9..f0c3b86 100644 --- a/instance.go +++ b/instance.go @@ -19,53 +19,59 @@ var ( std *application ) -func initApplication(cbs ...Option) { +func initialization(cbs ...Option) { once.Do(func() { std = New(cbs...) }) } -func Init(cbs ...Option) *application { - initApplication(cbs...) +func Init(cbs ...Option) Application { + initialization(cbs...) return std } +func Name() string { + initialization() + return std.opts.Name +} + +func ShortName() string { + initialization() + return std.opts.ShortName() +} + +func Version() string { + initialization() + return std.opts.Version +} + +func Debug(args ...any) bool { + initialization() + if len(args) <= 0 { + return std.opts.EnableDebug + } + if b, ok := args[0].(bool); ok { + std.opts.EnableDebug = b + } + return std.opts.EnableDebug +} + func Node() *Info { - initApplication() + initialization() return std.Info() } func Http() *http.Server { - initApplication() + initialization() return std.Http() } -func Name() string { - initApplication() - return std.opts.Name -} - -func Version() string { - initApplication() - return std.opts.Version -} - -func Debug() bool { - initApplication() - return std.opts.EnableDebug -} - func Command() *cli.Server { - initApplication() + initialization() return std.Command() } -func ShortName() string { - initApplication() - return std.opts.ShortName() -} - func Handle(method string, cb HandleFunc) { - initApplication() + initialization() std.Handle(method, cb) } diff --git a/options.go b/options.go index 1f2bc7d..e8fbe88 100644 --- a/options.go +++ b/options.go @@ -18,10 +18,11 @@ type ( Address string //绑定地址 Port int //端口 EnableDebug bool //开启调试模式 + DisableGateway bool //禁用HTTP和COMMAND入口 DisableHttp bool //禁用HTTP入口 EnableDirectHttp bool //启用HTTP直连模式 - DisableCommand bool //禁用命令行入口 - EnableDirectCommand bool //启用命令行直连模式 + DisableCommand bool //禁用COMMAND入口 + EnableDirectCommand bool //启用COMMAND直连模式 DisableStateApi bool //禁用系统状态接口 Metadata map[string]string //原数据 Context context.Context @@ -64,6 +65,12 @@ func WithName(name string, version string) Option { } } +func WithoutGateway() Option { + return func(o *Options) { + o.DisableGateway = true + } +} + func WithPort(port int) Option { return func(o *Options) { o.Port = port diff --git a/service.go b/service.go index e8ffcff..29cc903 100644 --- a/service.go +++ b/service.go @@ -94,7 +94,6 @@ func (app *application) httpServe() (err error) { var ( l net.Listener ) - app.http = http.New(app.ctx) if l, err = app.gateway.Apply( entry.Feature(http.MethodGet), entry.Feature(http.MethodHead), @@ -141,7 +140,6 @@ func (app *application) commandServe() (err error) { var ( l net.Listener ) - app.command = cli.New(app.ctx) if l, err = app.gateway.Apply( cli.Feature, ); err != nil { @@ -213,23 +211,26 @@ func (app *application) preStart() (err error) { app.Log().Infof("server starting") env.Set(EnvAppName, app.opts.ShortName()) env.Set(EnvAppVersion, app.opts.Version) - addr = net.JoinHostPort(app.opts.Address, strconv.Itoa(app.opts.Port)) - app.Log().Infof("server listen on: %s", addr) - app.gateway = entry.New(addr) - if err = app.gateway.Start(app.ctx); err != nil { - return - } - if !app.opts.DisableHttp { - if err = app.httpServe(); err != nil { + app.http = http.New(app.ctx) + app.command = cli.New(app.ctx) + if !app.opts.DisableGateway { + addr = net.JoinHostPort(app.opts.Address, strconv.Itoa(app.opts.Port)) + app.Log().Infof("server listen on: %s", addr) + app.gateway = entry.New(addr) + if err = app.gateway.Start(app.ctx); err != nil { return } - } - if !app.opts.DisableCommand { - if err = app.commandServe(); err != nil { - return + if !app.opts.DisableHttp { + if err = app.httpServe(); err != nil { + return + } + } + if !app.opts.DisableCommand { + if err = app.commandServe(); err != nil { + return + } } } - app.plugins.Range(func(key, value any) bool { if plugin, ok := value.(Plugin); ok { if err = plugin.BeforeStart(); err != nil { @@ -289,18 +290,20 @@ func (app *application) preStop() (err error) { } return true }) - if app.http != nil { - if err = app.http.Shutdown(); err != nil { - app.Log().Warnf("server http shutdown error: %s", err.Error()) + if !app.opts.DisableGateway { + if app.http != nil { + if err = app.http.Shutdown(); err != nil { + app.Log().Warnf("server http shutdown error: %s", err.Error()) + } } - } - if app.command != nil { - if err = app.command.Shutdown(); err != nil { - app.Log().Warnf("server command shutdown error: %s", err.Error()) + if app.command != nil { + if err = app.command.Shutdown(); err != nil { + app.Log().Warnf("server command shutdown error: %s", err.Error()) + } + } + if err = app.gateway.Stop(); err != nil { + app.Log().Warnf("server gateway shutdown error: %s", err.Error()) } - } - if err = app.gateway.Stop(); err != nil { - app.Log().Warnf("server gateway shutdown error: %s", err.Error()) } app.plugins.Range(func(key, value any) bool { if plugin, ok := value.(Plugin); ok { @@ -345,7 +348,7 @@ func (app *application) Run() (err error) { return app.preStop() } -func New(cbs ...Option) *application { +func New(cbs ...Option) Application { opts := NewOptions(cbs...) app := &application{ opts: opts, diff --git a/types.go b/types.go index 15323cc..bfb4cfa 100644 --- a/types.go +++ b/types.go @@ -23,6 +23,7 @@ type ( Http() *http.Server Command() *cli.Server Handle(method string, cb HandleFunc, opts ...HandleOption) + Run() (err error) } // Info application information diff --git a/util/fs/dir.go b/util/fs/dir.go index ffff1c1..6bd668a 100644 --- a/util/fs/dir.go +++ b/util/fs/dir.go @@ -1,7 +1,6 @@ package fs import ( - "errors" "os" ) @@ -11,22 +10,26 @@ func IsDir(filename string) (bool, error) { if err != nil { return false, err } - fm := fd.Mode() - return fm.IsDir(), nil + return fd.Mode().IsDir(), nil } -// DirectoryOrCreate checking directory, is not exists will create -func DirectoryOrCreate(dirname string) error { +// IsFile Tells whether the filename is a file +func IsFile(filename string) (bool, error) { + fd, err := os.Stat(filename) + if err != nil { + return false, err + } + return !fd.Mode().IsDir(), nil +} + +// Mkdir checking directory, is not exists will create +func Mkdir(dirname string, perm os.FileMode) error { if fi, err := os.Stat(dirname); err != nil { - if errors.Is(err, os.ErrNotExist) { - return os.MkdirAll(dirname, 0755) - } else { - return err - } + return os.MkdirAll(dirname, perm) } else { if fi.IsDir() { return nil } - return errors.New("file not directory") + return os.ErrPermission } } diff --git a/util/fs/file.go b/util/fs/file.go deleted file mode 100644 index ee66687..0000000 --- a/util/fs/file.go +++ /dev/null @@ -1 +0,0 @@ -package fs diff --git a/util/sys/homedir.go b/util/sys/homedir.go index e1636b5..e4de586 100644 --- a/util/sys/homedir.go +++ b/util/sys/homedir.go @@ -2,19 +2,15 @@ package sys import ( "os" - "path/filepath" "runtime" ) // HomeDir return user home directory func HomeDir() string { - if runtime.GOOS == "windows" { - return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if dirname, err := os.UserHomeDir(); err == nil { + return dirname } - if h := os.Getenv("HOME"); h != "" { - return h - } - return "/" + return os.TempDir() } // HiddenFile get hidden file prefix @@ -29,20 +25,12 @@ func HiddenFile(name string) string { // CacheDir return user cache directory func CacheDir() string { - switch runtime.GOOS { - case "darwin": - return filepath.Join(HomeDir(), "Library", "Caches") - case "windows": - for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} { - if v := os.Getenv(ev); v != "" { - return v - } - } - // Worst case: - return HomeDir() + if dirname, err := os.UserCacheDir(); err == nil { + return dirname } - if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { - return xdg - } - return filepath.Join(HomeDir(), ".cache") + return os.TempDir() +} + +func TempFile() (*os.File, error) { + return os.CreateTemp(os.TempDir(), "kos_*") }