From c511a2e6b32b75d9afedfa4a9c4e5fae9f5f1d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=9F=E5=B8=85?= <133814250@qq.com> Date: Mon, 15 May 2023 18:37:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9C=8D=E5=8A=A1=E9=80=80?= =?UTF-8?q?=E5=87=BA=E6=B5=81=E7=A8=8B=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=B8=AD?= =?UTF-8?q?=E9=97=B4=E4=BB=B6=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/guide-zh-CN/README.md | 3 +- .../images/sys-middleware-error-log.png | Bin 0 -> 58649 bytes docs/guide-zh-CN/sys-console.md | 7 +- docs/guide-zh-CN/sys-middleware.md | 204 ++++++++++++++++++ docs/guide-zh-CN/sys-webhook.md | 3 + server/internal/cmd/auth.go | 12 +- server/internal/cmd/cmd.go | 8 +- server/internal/cmd/cron.go | 17 +- server/internal/cmd/handler_shutdown.go | 7 +- server/internal/cmd/http.go | 23 +- server/internal/cmd/queue.go | 4 +- server/internal/consts/config.go | 4 +- server/internal/library/addons/build.go | 1 + server/internal/library/network/tcp/client.go | 9 +- server/internal/library/network/tcp/server.go | 5 + server/internal/library/token/token.go | 7 +- server/internal/logic/admin/member.go | 31 +-- server/internal/logic/admin/site.go | 11 - server/internal/logic/hook/access_log.go | 4 +- server/internal/logic/hook/last_active.go | 1 - server/internal/logic/middleware/response.go | 6 +- server/internal/logic/sys/log.go | 2 +- server/internal/logic/tcpclient/auth.go | 2 +- server/internal/logic/tcpclient/cron.go | 2 +- server/internal/logic/tcpserver/server.go | 8 +- server/internal/model/input/adminin/site.go | 13 +- .../generate/default/addon/README.MD.template | 2 +- server/utility/simple/simple.go | 24 ++- web/src/components/Editor/editor.vue | 10 +- 30 files changed, 335 insertions(+), 97 deletions(-) create mode 100644 docs/guide-zh-CN/images/sys-middleware-error-log.png create mode 100644 docs/guide-zh-CN/sys-middleware.md create mode 100644 docs/guide-zh-CN/sys-webhook.md diff --git a/README.md b/README.md index b493358..921e412 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ 16. 插件应用:支持一键生成插件模板,每个插件之间开发隔离,拥有独立多应用入口、独立配置。完美支持多人协同开发、插件插拔不会对原系统产生影响等。 17. 服务监控:监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。 18. 附件管理:文件图片上传,支持本地、阿里云oss、腾讯云cos、ucloud对象存储、七牛云对象存储等多种上传驱动,后台一键切换配置。 -19. TCP服务:基于gtcp的应用化实例,支持长连接、断线重连、自动维护心跳、服务登录、服务授权等。主要用于网络服务进程之间的消息通讯。 +19. TCP服务:基于gtcp的应用实例,支持长连接、断线重连、自动维护心跳、签名、服务登录、服务授权等。主要用于C/S服务器和服务进程之间的数据通讯。 20. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列,一键配置切换到场景适用的MQ。 21. 通知公告:采用websocket实时推送在线用户最新通知、公告、私信消息。 22. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项。 diff --git a/docs/guide-zh-CN/README.md b/docs/guide-zh-CN/README.md index 3998cdf..b9291e9 100644 --- a/docs/guide-zh-CN/README.md +++ b/docs/guide-zh-CN/README.md @@ -16,7 +16,8 @@ - [目录结构](sys-catalog.md) - [开发规范](sys-exploit.md) - [控制台](sys-console.md) -- 请求中间件和WebHook +- [中间件/拦截器](sys-middleware.md) +- [WebHook](sys-webhook.md) - [权限控制](sys-auth.md) - [支付网关](sys-payment.md) - [数据库](sys-db.md) diff --git a/docs/guide-zh-CN/images/sys-middleware-error-log.png b/docs/guide-zh-CN/images/sys-middleware-error-log.png new file mode 100644 index 0000000000000000000000000000000000000000..224dfb57f03e6fdd74154bba38997d07a074ac33 GIT binary patch literal 58649 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

oxhqiXVrL3H0vEp+4Wep8`M^G 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 // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package cmd import ( @@ -11,6 +10,12 @@ import ( "github.com/gogf/gf/v2/os/gproc" "hotgo/utility/simple" "os" + "sync" +) + +var ( + serverCloseSignal = make(chan struct{}, 1) + serverWg = sync.WaitGroup{} ) func signalHandlerForOverall(sig os.Signal) { diff --git a/server/internal/cmd/http.go b/server/internal/cmd/http.go index dbad3e5..b85025e 100644 --- a/server/internal/cmd/http.go +++ b/server/internal/cmd/http.go @@ -10,13 +10,11 @@ import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gcmd" - "hotgo/internal/crons" "hotgo/internal/library/addons" "hotgo/internal/library/casbin" "hotgo/internal/router" "hotgo/internal/service" "hotgo/internal/websocket" - "os" ) var ( @@ -77,13 +75,20 @@ var ( // https setSSL(ctx, s) - // 退出信号监听 - signalListen(ctx, func(sig os.Signal) { - s.Shutdown() - crons.StopALL() - websocket.Stop() - service.TCPServer().Stop(ctx) - }) + serverWg.Add(1) + + // 信号监听 + signalListen(ctx, signalHandlerForOverall) + go func() { + select { + case <-serverCloseSignal: + websocket.Stop() + service.TCPServer().Stop(ctx) + s.Shutdown() // 主服务建议放在最后一个关闭 + g.Log().Debug(ctx, "http successfully closed ..") + serverWg.Done() + } + }() // Just run the server. s.Run() diff --git a/server/internal/cmd/queue.go b/server/internal/cmd/queue.go index 8972d1a..b8c9c12 100644 --- a/server/internal/cmd/queue.go +++ b/server/internal/cmd/queue.go @@ -25,12 +25,14 @@ var ( g.Log().Debug(ctx, "start queue consumer success..") }) + serverWg.Add(1) + // 信号监听 signalListen(ctx, signalHandlerForOverall) select { case <-serverCloseSignal: - // ... + serverWg.Done() } g.Log().Debug(ctx, "queue successfully closed ..") diff --git a/server/internal/consts/config.go b/server/internal/consts/config.go index 4039745..7c9c80d 100644 --- a/server/internal/consts/config.go +++ b/server/internal/consts/config.go @@ -7,7 +7,9 @@ package consts import "github.com/gogf/gf/v2/util/gconv" -// RequestEncryptKey 请求加密密钥用于敏感数据加密,16位字符,前后端需保持一致。安全起见请修改此值 +// RequestEncryptKey +// 请求加密密钥用于敏感数据加密,16位字符,前后端需保持一致 +// 安全起见,生产环境运行时请注意修改 var RequestEncryptKey = []byte("f080a463654b2279") // 配置数据类型 diff --git a/server/internal/library/addons/build.go b/server/internal/library/addons/build.go index 974a1ba..2abc077 100644 --- a/server/internal/library/addons/build.go +++ b/server/internal/library/addons/build.go @@ -27,6 +27,7 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err "@{.description}": sk.Description, "@{.author}": sk.Author, "@{.version}": sk.Version, + "@{.hgVersion}": consts.VersionApp, // HG 版本 } ) diff --git a/server/internal/library/network/tcp/client.go b/server/internal/library/network/tcp/client.go index c55dd37..aa5fe7d 100644 --- a/server/internal/library/network/tcp/client.go +++ b/server/internal/library/network/tcp/client.go @@ -195,7 +195,9 @@ func (client *Client) connect() { reconnect: conn := client.dial() if conn == nil { - client.Logger.Debugf(client.Ctx, "client dial failed") + if !client.stopFlag { + client.Logger.Debugf(client.Ctx, "client dial failed") + } return } @@ -311,6 +313,11 @@ func (client *Client) Stop() { client.Close() } +// IsStop 是否已停止 +func (client *Client) IsStop() bool { + return client.stopFlag +} + // Destroy 销毁当前连接 func (client *Client) Destroy() { client.stopCron() diff --git a/server/internal/library/network/tcp/server.go b/server/internal/library/network/tcp/server.go index 47fa078..a0df114 100644 --- a/server/internal/library/network/tcp/server.go +++ b/server/internal/library/network/tcp/server.go @@ -294,6 +294,11 @@ func (server *Server) Close() { server.wgLn.Wait() } +// IsClose 服务是否关闭 +func (server *Server) IsClose() bool { + return server.closeFlag +} + // Write 向指定客户端发送消息 func (server *Server) Write(conn *gtcp.Conn, data interface{}) (err error) { if server.closeFlag { diff --git a/server/internal/library/token/token.go b/server/internal/library/token/token.go index e8e513f..1869629 100644 --- a/server/internal/library/token/token.go +++ b/server/internal/library/token/token.go @@ -99,6 +99,8 @@ func Logout(r *ghttp.Request) (err error) { claims, err := parseToken(ctx, header) if err != nil { + g.Log().Debugf(ctx, "logout parseToken err:%+v", err) + err = errorLogin return } @@ -138,10 +140,13 @@ func ParseLoginUser(r *ghttp.Request) (user *model.Identity, err error) { claims, err := parseToken(ctx, header) if err != nil { + g.Log().Debugf(ctx, "parseToken err:%+v", err) + err = errorLogin return } var ( + // 认证key authKey = GetAuthKey(header) // 登录token tokenKey = GetTokenKey(claims.App, authKey) @@ -285,7 +290,7 @@ func GetAuthorization(r *ghttp.Request) string { // GetAuthKey 认证key func GetAuthKey(token string) string { - return gmd5.MustEncryptString(token) + return gmd5.MustEncryptString("hotgo" + token) } // GetTokenKey 令牌缓存key diff --git a/server/internal/logic/admin/member.go b/server/internal/logic/admin/member.go index e1efa79..d73d408 100644 --- a/server/internal/logic/admin/member.go +++ b/server/internal/logic/admin/member.go @@ -227,18 +227,18 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate return err } - var memberInfo *entity.AdminMember - if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&memberInfo); err != nil { + var mb *entity.AdminMember + if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&mb); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err } - if memberInfo == nil { + if mb == nil { err = gerror.New("用户信息不存在") return err } - if memberInfo.Mobile == in.Mobile { + if mb.Mobile == in.Mobile { err = gerror.New("新旧手机号不能一样") return } @@ -249,10 +249,10 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate } // 存在原绑定号码,需要进行验证 - if memberInfo.Mobile != "" { + if mb.Mobile != "" { err = service.SysSmsLog().VerifyCode(ctx, sysin.VerifyCodeInp{ Event: consts.SmsTemplateBind, - Mobile: memberInfo.Mobile, + Mobile: mb.Mobile, Code: in.Code, }) if err != nil { @@ -264,8 +264,7 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate dao.AdminMember.Columns().Mobile: in.Mobile, } - _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update() - if err != nil { + if _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err } @@ -281,13 +280,13 @@ func (s *sAdminMember) UpdateProfile(ctx context.Context, in adminin.MemberUpdat return err } - var memberInfo *entity.AdminMember - if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&memberInfo); err != nil { + var mb *entity.AdminMember + if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&mb); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err } - if memberInfo == nil { + if mb == nil { err = gerror.New("用户信息不存在") return err } @@ -302,8 +301,7 @@ func (s *sAdminMember) UpdateProfile(ctx context.Context, in adminin.MemberUpdat dao.AdminMember.Columns().Address: in.Address, } - _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update() - if err != nil { + if _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err } @@ -718,7 +716,9 @@ func (s *sAdminMember) MemberLoginStat(ctx context.Context, in adminin.MemberLog // GetIdByCode 通过邀请码获取用户ID func (s *sAdminMember) GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) { - err = dao.AdminMember.Ctx(ctx).Fields(adminin.GetIdByCodeModel{}).Where("invite_code", in.Code).Scan(&res) + if err = dao.AdminMember.Ctx(ctx).Fields(adminin.GetIdByCodeModel{}).Where("invite_code", in.Code).Scan(&res); err != nil { + err = gerror.Wrap(err, consts.ErrorORM) + } return } @@ -728,6 +728,9 @@ func (s *sAdminMember) Select(ctx context.Context, in adminin.MemberSelectInp) ( Fields("id as value,real_name as label,username,avatar"). Handler(handler.FilterAuthWithField("id")). Scan(&res) + if err != nil { + err = gerror.Wrap(err, consts.ErrorORM) + } return } diff --git a/server/internal/logic/admin/site.go b/server/internal/logic/admin/site.go index 082c734..3172c2e 100644 --- a/server/internal/logic/admin/site.go +++ b/server/internal/logic/admin/site.go @@ -12,7 +12,6 @@ import ( "hotgo/internal/dao" "hotgo/internal/library/token" "hotgo/internal/model" - "hotgo/internal/model/do" "hotgo/internal/model/entity" "hotgo/internal/model/input/adminin" "hotgo/internal/model/input/sysin" @@ -255,16 +254,6 @@ func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (r return nil, err } - update := do.AdminMember{ - LastActiveAt: user.LoginAt, - } - - // 更新登录信息 - if _, err = dao.AdminMember.Ctx(ctx).Data(update).Where(do.AdminMember{Id: mb.Id}).Update(); err != nil { - err = gerror.Wrap(err, consts.ErrorORM) - return - } - res = &adminin.LoginModel{ Username: user.Username, Id: user.Id, diff --git a/server/internal/logic/hook/access_log.go b/server/internal/logic/hook/access_log.go index 314d86b..0ea56db 100644 --- a/server/internal/logic/hook/access_log.go +++ b/server/internal/logic/hook/access_log.go @@ -20,9 +20,9 @@ func (s *sHook) accessLog(r *ghttp.Request) { if r.IsFileRequest() { return } + var ctx = contexts.Detach(r.Context()) - modelCtx := contexts.Get(ctx) - if modelCtx == nil { + if contexts.Get(ctx) == nil { return } diff --git a/server/internal/logic/hook/last_active.go b/server/internal/logic/hook/last_active.go index e05b275..84ea7d0 100644 --- a/server/internal/logic/hook/last_active.go +++ b/server/internal/logic/hook/last_active.go @@ -79,7 +79,6 @@ func (s *sHook) lastAdminActive(r *ghttp.Request) { _, err := g.Model("admin_member"). Ctx(ctx). Where("id", member.Id). - WhereLT("last_active_at", gtime.Now()). Data(g.Map{"last_active_at": gtime.Now()}). Update() if err != nil { diff --git a/server/internal/logic/middleware/response.go b/server/internal/logic/middleware/response.go index ede181f..149030a 100644 --- a/server/internal/logic/middleware/response.go +++ b/server/internal/logic/middleware/response.go @@ -101,16 +101,16 @@ func responseJson(r *ghttp.Request) { if err = r.GetError(); err != nil { // 记录到自定义错误日志文件 - g.Log().Warningf(ctx, "exception:%v", err) + g.Log().Stdout(false).Printf(ctx, "exception:%v", err) code = gerror.Code(err).Code() // 是否输出错误到页面 if g.Cfg().MustGet(ctx, "hotgo.debug", true).Bool() { - message = err.Error() + message = gerror.Current(err).Error() data = charset.ParseErrStack(err) } else { - message = consts.ErrorMessage(err) + message = consts.ErrorMessage(gerror.Current(err)) } } else { data = r.GetHandlerResponse() diff --git a/server/internal/logic/sys/log.go b/server/internal/logic/sys/log.go index 08e346d..0dec417 100644 --- a/server/internal/logic/sys/log.go +++ b/server/internal/logic/sys/log.go @@ -105,7 +105,7 @@ func (s *sSysLog) AutoLog(ctx context.Context) error { var err error defer func() { if err != nil { - panic(err) + g.Log().Error(ctx, "autoLog err:%+v", err) } }() diff --git a/server/internal/logic/tcpclient/auth.go b/server/internal/logic/tcpclient/auth.go index 768dea4..84317c8 100644 --- a/server/internal/logic/tcpclient/auth.go +++ b/server/internal/logic/tcpclient/auth.go @@ -75,7 +75,7 @@ func (s *sAuthClient) Start(ctx context.Context) { // Stop 停止服务 func (s *sAuthClient) Stop(ctx context.Context) { - if s.client != nil { + if s.client != nil && !s.client.IsStop() { s.client.Stop() g.Log().Debug(ctx, "AuthClient stop..") } diff --git a/server/internal/logic/tcpclient/cron.go b/server/internal/logic/tcpclient/cron.go index 55dcb73..eff8ae5 100644 --- a/server/internal/logic/tcpclient/cron.go +++ b/server/internal/logic/tcpclient/cron.go @@ -76,7 +76,7 @@ func (s *sCronClient) Start(ctx context.Context) { // Stop 停止服务 func (s *sCronClient) Stop(ctx context.Context) { - if s.client != nil { + if s.client != nil && !s.client.IsStop() { s.client.Stop() g.Log().Debug(ctx, "CronClient stop..") } diff --git a/server/internal/logic/tcpserver/server.go b/server/internal/logic/tcpserver/server.go index 3b2462c..a3cff17 100644 --- a/server/internal/logic/tcpserver/server.go +++ b/server/internal/logic/tcpserver/server.go @@ -53,15 +53,17 @@ func (s *sTCPServer) Start(ctx context.Context) { }) // 服务监听 - if err := s.serv.Listen(); err != nil { - g.Log().Warningf(ctx, "TCPServer Listen err:%v", err) + if err = s.serv.Listen(); err != nil { + if !s.serv.IsClose() { + g.Log().Warningf(ctx, "TCPServer Listen err:%v", err) + } } }) } // Stop 关闭服务 func (s *sTCPServer) Stop(ctx context.Context) { - if s.serv != nil { + if s.serv != nil && !s.serv.IsClose() { s.serv.Close() g.Log().Debug(ctx, "TCPServer stop..") } diff --git a/server/internal/model/input/adminin/site.go b/server/internal/model/input/adminin/site.go index c061442..c21e87a 100644 --- a/server/internal/model/input/adminin/site.go +++ b/server/internal/model/input/adminin/site.go @@ -2,11 +2,9 @@ package adminin import ( "context" - "github.com/gogf/gf/v2/encoding/gbase64" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" - "hotgo/internal/consts" - "hotgo/utility/encrypt" + "hotgo/utility/simple" ) // RegisterInp 账号注册 @@ -20,18 +18,11 @@ type RegisterInp struct { func (in *RegisterInp) Filter(ctx context.Context) (err error) { // 解密密码 - str, err := gbase64.Decode([]byte(in.Password)) + password, err := simple.DecryptText(in.Password) if err != nil { return err } - str, err = encrypt.AesECBDecrypt(str, consts.RequestEncryptKey) - if err != nil { - return err - } - - password := string(str) - if err = g.Validator().Data(password).Rules("password").Messages("密码长度在6~18之间").Run(ctx); err != nil { return } diff --git a/server/resource/generate/default/addon/README.MD.template b/server/resource/generate/default/addon/README.MD.template index b054efe..6d9080b 100644 --- a/server/resource/generate/default/addon/README.MD.template +++ b/server/resource/generate/default/addon/README.MD.template @@ -12,7 +12,7 @@ ### 迁移或安装 -1、安装 HotGo (2.1.4及以上) +1、安装 HotGo (@{.hgVersion}及以上) 项目介绍:https://github.com/bufanyun/hotgo diff --git a/server/utility/simple/simple.go b/server/utility/simple/simple.go index 4bc2f5d..7551ad5 100644 --- a/server/utility/simple/simple.go +++ b/server/utility/simple/simple.go @@ -18,20 +18,30 @@ import ( "hotgo/utility/encrypt" ) +// DecryptText 解密文本 +func DecryptText(text string) (string, error) { + str, err := gbase64.Decode([]byte(text)) + if err != nil { + return "", err + } + + str, err = encrypt.AesECBDecrypt(str, consts.RequestEncryptKey) + if err != nil { + return "", err + } + + return string(str), nil +} + // CheckPassword 检查密码 func CheckPassword(input, salt, hash string) (err error) { // 解密密码 - password, err := gbase64.Decode([]byte(input)) + password, err := DecryptText(input) if err != nil { return err } - password, err = encrypt.AesECBDecrypt(password, consts.RequestEncryptKey) - if err != nil { - return err - } - - if hash != gmd5.MustEncryptString(string(password)+salt) { + if hash != gmd5.MustEncryptString(password+salt) { err = gerror.New("用户密码不正确") return } diff --git a/web/src/components/Editor/editor.vue b/web/src/components/Editor/editor.vue index c68ba75..062db48 100644 --- a/web/src/components/Editor/editor.vue +++ b/web/src/components/Editor/editor.vue @@ -1,6 +1,6 @@