This commit is contained in:
孟帅
2023-07-20 18:01:10 +08:00
parent 9113fc5297
commit 373d9627fb
492 changed files with 12170 additions and 6982 deletions

View File

@@ -1,76 +1,80 @@
// Package tcpserver
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/api/servmsg"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/network/tcp"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/msgin"
"hotgo/internal/model/input/servmsgin"
"hotgo/internal/service"
)
// OnAuthSummary 获取授权信息
func (s *sTCPServer) OnAuthSummary(ctx context.Context, args ...interface{}) {
func (s *sTCPServer) OnAuthSummary(ctx context.Context, req *servmsg.AuthSummaryReq) {
var (
in *msgin.AuthSummary
user = tcp.GetCtx(ctx)
res = new(msgin.ResponseAuthSummary)
conn = tcp.ConnFromCtx(ctx)
models *entity.SysServeLicense
res = new(servmsg.AuthSummaryRes)
)
if err := gconv.Scan(args, &in); err != nil {
res.Code = 1
res.Message = err.Error()
_ = s.serv.Reply(ctx, res)
if conn == nil {
g.Log().Warningf(ctx, "conn is nil.")
return
}
if user.Auth == nil {
res.Code = 2
res.Message = "登录信息获取失败,请重新登录"
_ = s.serv.Reply(ctx, res)
if conn.Auth == nil {
res.SetError(gerror.New("登录信息获取失败,请重新登录"))
conn.Send(ctx, res)
return
}
if err := dao.SysServeLicense.Ctx(ctx).Where("appid = ?", user.Auth.AppId).Scan(&models); err != nil {
res.Code = 3
res.Message = err.Error()
_ = s.serv.Reply(ctx, res)
if err := dao.SysServeLicense.Ctx(ctx).Where("appid = ?", conn.Auth.AppId).Scan(&models); err != nil {
res.SetError(err)
conn.Send(ctx, res)
return
}
if models == nil {
res.Code = 4
res.Message = "授权信息不存在"
_ = s.serv.Reply(ctx, res)
res.SetError(gerror.New("授权信息不存在"))
conn.Send(ctx, res)
return
}
if models.Status != consts.StatusEnabled {
res.Code = 5
res.Message = "授权已禁用,请联系管理员"
_ = s.serv.Reply(ctx, res)
res.SetError(gerror.New("授权已禁用,请联系管理员"))
conn.Send(ctx, res)
return
}
if models.Group != user.Auth.Group {
res.Code = 6
res.Message = "你登录的授权分组未得到授权,请联系管理员"
_ = s.serv.Reply(ctx, res)
if models.Group != conn.Auth.Group {
res.SetError(gerror.New("你登录的授权分组未得到授权,请联系管理员"))
conn.Send(ctx, res)
return
}
if models.EndAt.Before(gtime.Now()) {
res.Code = 7
res.Message = "授权已过期,请联系管理员"
_ = s.serv.Reply(ctx, res)
res.SetError(gerror.New("授权已过期,请联系管理员"))
conn.Send(ctx, res)
return
}
res.Data = new(msgin.AuthSummaryData)
res.Data.EndAt = models.EndAt
res.Data.Online = models.Online
_ = s.serv.Reply(ctx, res)
data := new(servmsgin.AuthSummaryModel)
data.EndAt = models.EndAt
data.Online = service.TCPServer().Instance().GetAppIdOnline(models.Appid)
// 请填充你的授权数据
// ...
res.Data = data
conn.Send(ctx, res)
}

View File

@@ -1,117 +1,97 @@
// Package tcpserver
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/api/servmsg"
"hotgo/internal/consts"
"hotgo/internal/model/input/msgin"
)
// CronDelete 删除任务
func (s *sTCPServer) CronDelete(ctx context.Context, in *msgin.CronDelete) (err error) {
clients := s.serv.GetGroupClients(consts.TCPClientGroupCron)
func (s *sTCPServer) CronDelete(ctx context.Context, in *servmsg.CronDeleteReq) (err error) {
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
if len(clients) == 0 {
err = gerror.New("没有在线的定时任务服务")
return
}
for _, client := range clients {
res, err := s.serv.RpcRequest(ctx, client, in)
if err != nil {
return err
var res servmsg.CronDeleteRes
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
return
}
var resp = new(msgin.ResponseCronDelete)
if err = gconv.Scan(res, &resp); err != nil {
return err
}
if err = resp.GetError(); err != nil {
return err
if err = res.GetError(); err != nil {
return
}
}
return
}
// CronEdit 编辑任务
func (s *sTCPServer) CronEdit(ctx context.Context, in *msgin.CronEdit) (err error) {
clients := s.serv.GetGroupClients(consts.TCPClientGroupCron)
func (s *sTCPServer) CronEdit(ctx context.Context, in *servmsg.CronEditReq) (err error) {
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
if len(clients) == 0 {
err = gerror.New("没有在线的定时任务服务")
return
}
for _, client := range clients {
res, err := s.serv.RpcRequest(ctx, client, in)
if err != nil {
return err
var res servmsg.CronEditRes
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
return
}
var resp = new(msgin.ResponseCronEdit)
if err = gconv.Scan(res, &resp); err != nil {
return err
}
if err = resp.GetError(); err != nil {
return err
if err = res.GetError(); err != nil {
return
}
}
return
}
// CronStatus 修改任务状态
func (s *sTCPServer) CronStatus(ctx context.Context, in *msgin.CronStatus) (err error) {
clients := s.serv.GetGroupClients(consts.TCPClientGroupCron)
func (s *sTCPServer) CronStatus(ctx context.Context, in *servmsg.CronStatusReq) (err error) {
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
if len(clients) == 0 {
err = gerror.New("没有在线的定时任务服务")
return
}
for _, client := range clients {
res, err := s.serv.RpcRequest(ctx, client, in)
if err != nil {
return err
var res servmsg.CronStatusRes
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
return
}
var resp = new(msgin.ResponseCronStatus)
if err = gconv.Scan(res, &resp); err != nil {
return err
}
if err = resp.GetError(); err != nil {
return err
if err = res.GetError(); err != nil {
return
}
}
return
}
// CronOnlineExec 执行一次任务
func (s *sTCPServer) CronOnlineExec(ctx context.Context, in *msgin.CronOnlineExec) (err error) {
clients := s.serv.GetGroupClients(consts.TCPClientGroupCron)
func (s *sTCPServer) CronOnlineExec(ctx context.Context, in *servmsg.CronOnlineExecReq) (err error) {
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
if len(clients) == 0 {
err = gerror.New("没有在线的定时任务服务")
return
}
for _, client := range clients {
res, err := s.serv.RpcRequest(ctx, client, in)
if err != nil {
return err
var res servmsg.CronOnlineExecRes
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
return
}
var resp = new(msgin.ResponseCronOnlineExec)
if err = gconv.Scan(res, &resp); err != nil {
return err
}
if err = resp.GetError(); err != nil {
return err
if err = res.GetError(); err != nil {
return
}
}
return
}

View File

@@ -0,0 +1,49 @@
package tcpserver
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/api/servmsg"
"hotgo/internal/consts"
"hotgo/internal/library/network/tcp"
"hotgo/internal/model/input/servmsgin"
)
// OnExampleHello 一个tcp请求例子
func (s *sTCPServer) OnExampleHello(ctx context.Context, req *servmsg.ExampleHelloReq) {
var (
conn = tcp.ConnFromCtx(ctx)
res = new(servmsg.ExampleHelloRes)
data = new(servmsgin.ExampleHelloModel)
)
if conn == nil {
g.Log().Warningf(ctx, "conn is nil.")
return
}
if conn.Auth == nil {
res.SetError(gerror.New("连接未认证,请确认已登录成功!"))
conn.Send(ctx, res)
return
}
data.Desc = fmt.Sprintf("Hello %v, 你的APPID%v当前HotGo版本%v你成功请求了`servmsg.ExampleHelloReq`接口!", req.Name, conn.Auth.AppId, consts.VersionApp)
data.Timestamp = gtime.Now()
res.Data = data
conn.Send(ctx, res)
}
// OnExampleRPCHello 一个rpc请求例子
func (s *sTCPServer) OnExampleRPCHello(ctx context.Context, req *servmsg.ExampleRPCHelloReq) (res *servmsg.ExampleRPCHelloRes, err error) {
var data = new(servmsgin.ExampleHelloModel)
data.Desc = fmt.Sprintf("Hello %v, 当前HotGo版本%v你成功请求了`servmsg.ExampleRPCHelloReq`接口!", req.Name, consts.VersionApp)
data.Timestamp = gtime.Now()
res = new(servmsg.ExampleRPCHelloRes)
res.Data = data
return
}

View File

@@ -1,3 +1,8 @@
// Package tcpserver
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver
import (
@@ -20,40 +25,39 @@ func newTCPServer() *sTCPServer {
return &sTCPServer{}
}
// Instance 获取实例
func (s *sTCPServer) Instance() *tcp.Server {
return s.serv
}
// Start 启动服务
func (s *sTCPServer) Start(ctx context.Context) {
simple.SafeGo(ctx, func(ctx context.Context) {
g.Log().Debug(ctx, "TCPServer start..")
server, err := tcp.NewServer(&tcp.ServerConfig{
s.serv = tcp.NewServer(&tcp.ServerConfig{
Name: simple.AppName(ctx),
Addr: g.Cfg().MustGet(ctx, "tcp.server.address").String(),
})
if err != nil {
g.Log().Warningf(ctx, "TCPServer start fail%+v", err)
return
}
// 注册路由
s.serv.RegisterRouter(
s.onServerLogin, // 服务登录
s.onServerHeartbeat, // 心跳
s.OnAuthSummary, // 获取授权信息
s.OnExampleHello, // 一个tcp请求例子
)
s.serv = server
// 注册RPC路由
s.serv.RegisterRPCRouter(
s.OnExampleRPCHello, // 一个rpc请求例子
)
// 消息队列路由
s.serv.RegisterQueueRouter(map[string]tcp.RouterHandler{
// ...
})
// 定时任务路由
s.serv.RegisterCronRouter(map[string]tcp.RouterHandler{
// ...
})
// 授权服务路由
s.serv.RegisterAuthRouter(map[string]tcp.RouterHandler{
"AuthSummary": s.OnAuthSummary, // 获取授权信息
})
// 注册拦截器
s.serv.RegisterInterceptor(s.DefaultInterceptor, s.PreFilterInterceptor)
// 服务监听
if err = s.serv.Listen(); err != nil {
if err := s.serv.Listen(); err != nil {
if !s.serv.IsClose() {
g.Log().Warningf(ctx, "TCPServer Listen err:%v", err)
}

View File

@@ -0,0 +1,167 @@
// Package tcpserver
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/network/tcp"
"hotgo/internal/model/entity"
"hotgo/utility/convert"
"hotgo/utility/encrypt"
)
// onServerLogin 处理客户端登录
func (s *sTCPServer) onServerLogin(ctx context.Context, req *tcp.ServerLoginReq) {
var (
conn = tcp.ConnFromCtx(ctx)
models *entity.SysServeLicense
res = new(tcp.ServerLoginRes)
cols = dao.SysServeLicense.Columns()
)
if conn == nil {
g.Log().Warningf(ctx, "conn is nil.")
return
}
if err := dao.SysServeLicense.Ctx(ctx).Where(cols.Appid, req.AppId).Scan(&models); err != nil {
return
}
if models == nil {
res.SetError(gerror.New("授权信息不存在"))
conn.Send(ctx, res)
return
}
// 验证签名
sign := encrypt.Md5ToString(fmt.Sprintf("%v%v%v", models.Appid, req.Timestamp, models.SecretKey))
if sign != req.Sign {
res.SetError(gerror.New("签名错误,请检查!"))
conn.Send(ctx, res)
return
}
if models.Status != consts.StatusEnabled {
res.SetError(gerror.New("授权已禁用,请联系管理员"))
conn.Send(ctx, res)
return
}
if models.Group != req.Group {
res.SetError(gerror.New("你登录的授权分组未得到授权,请联系管理员"))
conn.Send(ctx, res)
return
}
if models.EndAt.Before(gtime.Now()) {
res.SetError(gerror.New("授权已过期,请联系管理员"))
conn.Send(ctx, res)
return
}
ip := gstr.StrTillEx(conn.RemoteAddr().String(), ":")
if !convert.MatchIpStrategy(models.AllowedIps, ip) {
res.SetError(gerror.New("IP(" + ip + ")未授权,请联系管理员"))
conn.Send(ctx, res)
return
}
var routes []string
if err := models.Routes.Scan(&routes); err != nil {
res.SetError(gerror.New("授权路由解析失败,请联系管理员"))
conn.Send(ctx, res)
return
}
// 拿出当前登录应用的所有客户端
clients := s.serv.GetAppIdClients(models.Appid)
// 检查多地登录,如果连接超过上限,则断开当前许可证下的所有连接
if len(clients)+1 > models.OnlineLimit {
for _, client := range clients {
client.Close()
}
res.SetError(gerror.New("授权登录端超出上限,请勿多地登录"))
conn.Send(ctx, res)
return
}
for _, client := range clients {
if client.Auth.Name == req.Name {
res.SetError(gerror.Newf("应用名称[%v]已存在登录用户,当前连接已被拒绝。", req.Name))
conn.Send(ctx, res)
return
}
}
auth := &tcp.AuthMeta{
Name: req.Name,
Extra: req.Extra,
Group: models.Group,
AppId: models.Appid,
SecretKey: models.SecretKey,
EndAt: models.EndAt,
Routes: routes,
}
s.serv.AuthClient(conn, auth)
update := g.Map{
cols.LoginTimes: models.LoginTimes + 1,
cols.LastLoginAt: gtime.Now(),
cols.LastActiveAt: gtime.Now(),
cols.RemoteAddr: conn.RemoteAddr().String(),
}
if _, err := dao.SysServeLicense.Ctx(ctx).Where(cols.Id, models.Id).Data(update).Update(); err != nil {
res.SetError(err)
conn.Send(ctx, res)
return
}
g.Log().Debugf(ctx, "onServerLogin succeed. appid:%v, group:%v, name:%v", auth.AppId, auth.Group, auth.Name)
conn.Send(ctx, res)
}
// onServerHeartbeat 处理客户端心跳
func (s *sTCPServer) onServerHeartbeat(ctx context.Context, req *tcp.ServerHeartbeatReq) {
var (
conn = tcp.ConnFromCtx(ctx)
res = new(tcp.ServerHeartbeatRes)
)
if conn == nil {
g.Log().Warningf(ctx, "conn is nil.")
return
}
client := s.serv.GetClient(conn.Conn)
if client == nil {
res.SetError(gerror.New("登录异常,请重新登录"))
conn.Send(ctx, res)
return
}
// 更新心跳
client.Heartbeat = gtime.Timestamp()
// 更新活跃时间
update := g.Map{
dao.SysServeLicense.Columns().LastActiveAt: gtime.Now(),
}
if _, err := dao.SysServeLicense.Ctx(ctx).Where(dao.SysServeLicense.Columns().Appid, client.Auth.AppId).Data(update).Update(); err != nil {
res.SetError(err)
conn.Send(ctx, res)
return
}
conn.Send(ctx, res)
}

View File

@@ -0,0 +1,75 @@
// Package tcpserver
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcpserver
import (
"context"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/library/network/tcp"
"hotgo/utility/validate"
)
// 免登录路由
var noLoginRouter = map[string]struct{}{
"ServerLoginReq": {}, // 服务登录
}
// 免验证路由
var noVerifyRouter = map[string]struct{}{
"ServerHeartbeatReq": {}, // 心跳
}
func (s *sTCPServer) isNoLoginRouter(router string) bool {
_, ok := noLoginRouter[router]
return ok
}
func (s *sTCPServer) isNoVerifyRouter(router string) bool {
_, ok := noVerifyRouter[router]
return ok
}
// DefaultInterceptor 默认拦截器
func (s *sTCPServer) DefaultInterceptor(ctx context.Context, msg *tcp.Message) (err error) {
conn := tcp.ConnFromCtx(ctx)
//g.Log().Debugf(ctx, "DefaultInterceptor msg:%+v, conn:%+v", msg, gjson.New(conn).String())
// 免登录
if s.isNoLoginRouter(msg.Router) {
return
}
if conn.Auth == nil {
err = gerror.NewCode(gcode.CodeNotAuthorized, "未进行登录认证,请先登录")
return
}
// 检查授权有效期
if conn.Auth.EndAt.Before(gtime.Now()) {
err = gerror.NewCode(gcode.CodeNotAuthorized, "授权已过期")
return
}
// 免验证
if s.isNoVerifyRouter(msg.Router) {
return
}
// 验证路由权限
if len(conn.Auth.Routes) > 0 && !validate.InSlice(conn.Auth.Routes, msg.Router) {
err = gerror.NewCodef(gcode.CodeNotAuthorized, "没有授权路由访问权限:%v", msg.Router)
return
}
return
}
// PreFilterInterceptor 预处理
func (s *sTCPServer) PreFilterInterceptor(ctx context.Context, msg *tcp.Message) (err error) {
//g.Log().Debug(ctx, "PreFilterInterceptor...")
return
}