From dc698ec6a86f4f30328a1cad796dc429663680e5 Mon Sep 17 00:00:00 2001 From: hailong <605739610@qq.com> Date: Wed, 26 May 2021 19:15:25 +0800 Subject: [PATCH] add api --- src/api/Makefile | 26 +++++ src/api/auth/user_register.go | 90 +++++++++++++++++ src/api/auth/user_token.go | 66 +++++++++++++ src/api/chat/newest_seq.go | 59 ++++++++++++ src/api/chat/pull_msg.go | 75 +++++++++++++++ src/api/chat/send_msg.go | 96 +++++++++++++++++++ src/api/open_im_api.go | 75 +++++++++++++++ .../third/tencent_cloud_storage_credential.go | 70 ++++++++++++++ 8 files changed, 557 insertions(+) create mode 100644 src/api/Makefile create mode 100644 src/api/auth/user_register.go create mode 100644 src/api/auth/user_token.go create mode 100644 src/api/chat/newest_seq.go create mode 100644 src/api/chat/pull_msg.go create mode 100644 src/api/chat/send_msg.go create mode 100644 src/api/open_im_api.go create mode 100644 src/api/third/tencent_cloud_storage_credential.go diff --git a/src/api/Makefile b/src/api/Makefile new file mode 100644 index 000000000..5ea7e5742 --- /dev/null +++ b/src/api/Makefile @@ -0,0 +1,26 @@ +.PHONY: all build run gotool install clean help + +BINARY_NAME=open_im_api +BIN_DIR=../../bin/ +LAN_FILE=.go +GO_FILE:=${BINARY_NAME}${LAN_FILE} + +all: gotool build + +build: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_NAME} ${GO_FILE} + +run: + @go run ./ + +gotool: + go fmt ./ + go vet ./ + +install: + make build + mv ${BINARY_NAME} ${BIN_DIR} + +clean: + @if [ -f ${BINARY_NAME} ] ; then rm ${BINARY_NAME} ; fi + diff --git a/src/api/auth/user_register.go b/src/api/auth/user_register.go new file mode 100644 index 000000000..a66858160 --- /dev/null +++ b/src/api/auth/user_register.go @@ -0,0 +1,90 @@ +package apiAuth + +import ( + "Open_IM/src/common/config" + "Open_IM/src/common/log" + pbAuth "Open_IM/src/proto/auth" + "context" + "github.com/gin-gonic/gin" + "github.com/skiffer-git/grpc-etcdv3/getcdv3" + "net/http" + "strings" +) + +type paramsUserRegister struct { + Secret string `json:"secret" binding:"required,max=32"` + Platform int32 `json:"platform" binding:"required,min=1,max=7"` + UID string `json:"uid" binding:"required,min=1,max=64"` + Name string `json:"name" binding:"required,min=1,max=64"` + Icon string `json:"icon" binding:"omitempty,max=1024"` + Gender int32 `json:"gender" binding:"omitempty,oneof=0 1 2"` + Mobile string `json:"mobile" binding:"omitempty,max=32"` + Birth string `json:"birth" binding:"omitempty,max=16"` + Email string `json:"email" binding:"omitempty,max=64"` + Ex string `json:"ex" binding:"omitempty,max=1024"` +} + +func newUserRegisterReq(params *paramsUserRegister) *pbAuth.UserRegisterReq { + pbData := pbAuth.UserRegisterReq{ + UID: params.UID, + Name: params.Name, + Icon: params.Icon, + Gender: params.Gender, + Mobile: params.Mobile, + Birth: params.Birth, + Email: params.Email, + Ex: params.Ex, + } + return &pbData +} + +func UserRegister(c *gin.Context) { + log.Info("", "", "api user_register init ....") + etcdConn := getcdv3.GetConn(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.RpcGetTokenName) + client := pbAuth.NewAuthClient(etcdConn) + + params := paramsUserRegister{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": err.Error()}) + return + } + pbData := newUserRegisterReq(¶ms) + + log.Info("", "", "api user_register is server, [data: %s]", pbData.String()) + reply, err := client.UserRegister(context.Background(), pbData) + if err != nil || !reply.Success { + log.Error("", "", "api user_register call rpc fail, [data: %s] [err: %s]", pbData.String(), err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{"errCode": 500, "errMsg": err.Error()}) + return + } + log.Info("", "", "api user_register call rpc success, [data: %s] [reply: %s]", pbData.String(), reply.String()) + + pbDataToken := &pbAuth.UserTokenReq{ + Platform: params.Platform, + UID: params.UID, + } + replyToken, err := client.UserToken(context.Background(), pbDataToken) + if err != nil { + log.Error("", "", "api user_register call rpc fail, [data: %s] [err: %s]", pbData.String(), err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{"errCode": 500, "errMsg": err.Error()}) + return + } + log.Info("", "", "api user_register call success, [data: %s] [reply: %s]", pbData.String(), reply.String()) + + if replyToken.ErrCode == 0 { + c.JSON(http.StatusOK, gin.H{ + "errCode": replyToken.ErrCode, + "errMsg": replyToken.ErrMsg, + "data": gin.H{ + "uid": pbData.UID, + "token": replyToken.Token, + "expiredTime": replyToken.ExpiredTime, + }, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "errCode": replyToken.ErrCode, + "errMsg": replyToken.ErrMsg, + }) + } +} diff --git a/src/api/auth/user_token.go b/src/api/auth/user_token.go new file mode 100644 index 000000000..2e18145f7 --- /dev/null +++ b/src/api/auth/user_token.go @@ -0,0 +1,66 @@ +package apiAuth + +import ( + "Open_IM/src/common/config" + "Open_IM/src/common/log" + pbAuth "Open_IM/src/proto/auth" + "context" + "github.com/gin-gonic/gin" + "github.com/skiffer-git/grpc-etcdv3/getcdv3" + "net/http" + "strings" +) + +type paramsUserToken struct { + Secret string `json:"secret" binding:"required,max=32"` + Platform int32 `json:"platform" binding:"required,min=1,max=7"` + UID string `json:"uid" binding:"required,min=1,max=64"` +} + +func newUserTokenReq(params *paramsUserToken) *pbAuth.UserTokenReq { + pbData := pbAuth.UserTokenReq{ + Platform: params.Platform, + UID: params.UID, + } + return &pbData +} + +func UserToken(c *gin.Context) { + log.Info("", "", "api user_token init ....") + etcdConn := getcdv3.GetConn(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.RpcGetTokenName) + client := pbAuth.NewAuthClient(etcdConn) + + params := paramsUserToken{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": err.Error()}) + return + } + pbData := newUserTokenReq(¶ms) + + log.Info("", "", "api user_token is server, [data: %s]", pbData.String()) + reply, err := client.UserToken(context.Background(), pbData) + if err != nil { + log.Error("", "", "api user_token call rpc fail, [data: %s] [err: %s]", pbData.String(), err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{"errCode": 500, "errMsg": err.Error()}) + return + } + log.Info("", "", "api user_token call rpc success, [data: %s] [reply: %s]", pbData.String(), reply.String()) + + if reply.ErrCode == 0 { + c.JSON(http.StatusOK, gin.H{ + "errCode": reply.ErrCode, + "errMsg": reply.ErrMsg, + "data": gin.H{ + "uid": pbData.UID, + "token": reply.Token, + "expiredTime": reply.ExpiredTime, + }, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "errCode": reply.ErrCode, + "errMsg": reply.ErrMsg, + }) + } + +} diff --git a/src/api/chat/newest_seq.go b/src/api/chat/newest_seq.go new file mode 100644 index 000000000..be6298c36 --- /dev/null +++ b/src/api/chat/newest_seq.go @@ -0,0 +1,59 @@ +package apiChat + +import ( + "Open_IM/src/common/config" + "Open_IM/src/common/log" + pbMsg "Open_IM/src/proto/chat" + "Open_IM/src/utils" + "context" + "github.com/gin-gonic/gin" + "github.com/skiffer-git/grpc-etcdv3/getcdv3" + "net/http" + "strings" +) + +type paramsUserNewestSeq struct { + ReqIdentifier int `json:"reqIdentifier" binding:"required"` + SendID string `json:"sendID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + MsgIncr int `json:"msgIncr" binding:"required"` +} + +func UserNewestSeq(c *gin.Context) { + params := paramsUserNewestSeq{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": err.Error()}) + return + } + + token := c.Request.Header.Get("token") + if !utils.VerifyToken(token, params.SendID) { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": "token validate err"}) + return + } + + pbData := pbMsg.GetNewSeqReq{} + pbData.UserID = params.SendID + pbData.OperationID = params.OperationID + grpcConn := getcdv3.GetConn(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.OpenImOfflineMessageName) + if grpcConn == nil { + log.ErrorByKv("get grpcConn err", pbData.OperationID, "args", params) + } + msgClient := pbMsg.NewChatClient(grpcConn) + reply, err := msgClient.GetNewSeq(context.Background(), &pbData) + if err != nil { + log.ErrorByKv("rpc call failed to getNewSeq", pbData.OperationID, "err", err, "pbData", pbData.String()) + return + } + + c.JSON(http.StatusOK, gin.H{ + "errCode": reply.ErrCode, + "errMsg": reply.ErrMsg, + "msgIncr": params.MsgIncr, + "reqIdentifier": params.ReqIdentifier, + "data": gin.H{ + "seq": reply.Seq, + }, + }) + +} diff --git a/src/api/chat/pull_msg.go b/src/api/chat/pull_msg.go new file mode 100644 index 000000000..cbbc84b57 --- /dev/null +++ b/src/api/chat/pull_msg.go @@ -0,0 +1,75 @@ +package apiChat + +import ( + "Open_IM/src/common/config" + "Open_IM/src/common/log" + "Open_IM/src/proto/chat" + "Open_IM/src/utils" + "context" + "github.com/gin-gonic/gin" + "github.com/skiffer-git/grpc-etcdv3/getcdv3" + "net/http" + "strings" +) + +type paramsUserPullMsg struct { + ReqIdentifier int `json:"reqIdentifier" binding:"required"` + SendID string `json:"sendID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + MsgIncr int `json:"msgIncr" binding:"required"` + Data struct { + SeqBegin int64 `json:"seqBegin" binding:"required"` + SeqEnd int64 `json:"seqEnd" binding:"required"` + } +} + +func UserPullMsg(c *gin.Context) { + params := paramsUserPullMsg{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": err.Error()}) + return + } + + token := c.Request.Header.Get("token") + if !utils.VerifyToken(token, params.SendID) { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": "token validate err"}) + return + } + + pbData := pbChat.PullMessageReq{} + pbData.UserID = params.SendID + pbData.OperationID = params.OperationID + pbData.SeqBegin = params.Data.SeqBegin + pbData.SeqEnd = params.Data.SeqEnd + grpcConn := getcdv3.GetConn(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.OpenImOfflineMessageName) + msgClient := pbChat.NewChatClient(grpcConn) + reply, err := msgClient.PullMessage(context.Background(), &pbData) + if err != nil { + log.ErrorByKv("PullMessage error", pbData.OperationID, "err", err.Error()) + return + } + log.InfoByKv("rpc call success to pullMsgRep", pbData.OperationID, "ReplyArgs", reply.String(), "maxSeq", reply.GetMaxSeq(), + "MinSeq", reply.GetMinSeq(), "singLen", len(reply.GetSingleUserMsg()), "groupLen", len(reply.GetGroupUserMsg())) + + msg := make(map[string]interface{}) + if v := reply.GetSingleUserMsg(); v != nil { + msg["single"] = v + } else { + msg["single"] = []pbChat.GatherFormat{} + } + if v := reply.GetGroupUserMsg(); v != nil { + msg["group"] = v + } else { + msg["group"] = []pbChat.GatherFormat{} + } + msg["maxSeq"] = reply.GetMaxSeq() + msg["minSeq"] = reply.GetMinSeq() + c.JSON(http.StatusOK, gin.H{ + "errCode": reply.ErrCode, + "errMsg": reply.ErrMsg, + "msgIncr": params.MsgIncr, + "reqIdentifier": params.ReqIdentifier, + "data": msg, + }) + +} diff --git a/src/api/chat/send_msg.go b/src/api/chat/send_msg.go new file mode 100644 index 000000000..30751eefd --- /dev/null +++ b/src/api/chat/send_msg.go @@ -0,0 +1,96 @@ +package apiChat + +import ( + "Open_IM/src/common/config" + "Open_IM/src/common/log" + pbChat "Open_IM/src/proto/chat" + "Open_IM/src/utils" + "context" + + "github.com/gin-gonic/gin" + "github.com/skiffer-git/grpc-etcdv3/getcdv3" + "net/http" + "strings" +) + +type paramsUserSendMsg struct { + ReqIdentifier int32 `json:"reqIdentifier" binding:"required"` + PlatformID int32 `json:"platformID" binding:"required"` + SendID string `json:"sendID" binding:"required"` + OperationID string `json:"operationID" binding:"required"` + MsgIncr int32 `json:"msgIncr" binding:"required"` + Data struct { + SessionType int32 `json:"sessionType" binding:"required"` + MsgFrom int32 `json:"msgFrom" binding:"required"` + ContentType int32 `json:"contentType" binding:"required"` + RecvID string `json:"recvID" binding:"required"` + ForceList []string `json:"forceList" binding:"required"` + Content string `json:"content" binding:"required"` + Options map[string]interface{} `json:"options" binding:"required"` + ClientMsgID string `json:"clientMsgID" binding:"required"` + OffLineInfo map[string]interface{} `json:"offlineInfo" binding:"required"` + Ex map[string]interface{} `json:"ext"` + } +} + +func newUserSendMsgReq(token string, params *paramsUserSendMsg) *pbChat.UserSendMsgReq { + pbData := pbChat.UserSendMsgReq{ + ReqIdentifier: params.ReqIdentifier, + Token: token, + SendID: params.SendID, + OperationID: params.OperationID, + MsgIncr: params.MsgIncr, + PlatformID: params.PlatformID, + SessionType: params.Data.SessionType, + MsgFrom: params.Data.MsgFrom, + ContentType: params.Data.ContentType, + RecvID: params.Data.RecvID, + ForceList: params.Data.ForceList, + Content: params.Data.Content, + Options: utils.MapToJsonString(params.Data.Options), + ClientMsgID: params.Data.ClientMsgID, + OffLineInfo: utils.MapToJsonString(params.Data.OffLineInfo), + Ex: utils.MapToJsonString(params.Data.Ex), + } + return &pbData +} + +func UserSendMsg(c *gin.Context) { + params := paramsUserSendMsg{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": err.Error()}) + log.ErrorByKv("json unmarshal err", "", "err", err.Error(), "data", c.PostForm("data")) + return + } + + token := c.Request.Header.Get("token") + if !utils.VerifyToken(token, params.SendID) { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": "token validate err"}) + return + } + + log.InfoByKv("Ws call success to sendMsgReq", params.OperationID, "Parameters", params) + + pbData := newUserSendMsgReq(token, ¶ms) + log.Info("", "", "api UserSendMsg call start..., [data: %s]", pbData.String()) + + etcdConn := getcdv3.GetConn(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.OpenImOfflineMessageName) + client := pbChat.NewChatClient(etcdConn) + + log.Info("", "", "api UserSendMsg call, api call rpc...") + + reply, _ := client.UserSendMsg(context.Background(), pbData) + log.Info("", "", "api UserSendMsg call end..., [data: %s] [reply: %s]", pbData.String(), reply.String()) + + c.JSON(http.StatusOK, gin.H{ + "errCode": 0, + "errMsg": "", + "msgIncr": reply.MsgIncr, + "reqIdentifier": reply.ReqIdentifier, + "data": gin.H{ + "clientMsgID": reply.ClientMsgID, + "serverMsgID": reply.ServerMsgID, + }, + }) + +} diff --git a/src/api/open_im_api.go b/src/api/open_im_api.go new file mode 100644 index 000000000..a2b6f45ae --- /dev/null +++ b/src/api/open_im_api.go @@ -0,0 +1,75 @@ +package main + +import ( + apiAuth "Open_IM/src/api/auth" + apiChat "Open_IM/src/api/chat" + "Open_IM/src/api/friend" + apiThird "Open_IM/src/api/third" + "Open_IM/src/api/user" + "Open_IM/src/common/log" + "Open_IM/src/utils" + "flag" + "github.com/gin-gonic/gin" + "strconv" +) + +func main() { + log.Info("", "", "api server running...") + r := gin.Default() + r.Use(utils.CorsHandler()) + // user routing group, which handles user registration and login services + userRouterGroup := r.Group("/user") + { + userRouterGroup.POST("/update_user_info", user.UpdateUserInfo) + userRouterGroup.POST("/get_user_info", user.GetUserInfo) + } + //friend routing group + friendRouterGroup := r.Group("/friend") + { + friendRouterGroup.POST("/search_friend", friend.SearchFriend) + friendRouterGroup.POST("/add_friend", friend.AddFriend) + friendRouterGroup.POST("/get_friend_apply_list", friend.GetFriendApplyList) + friendRouterGroup.POST("/get_friend_list", friend.GetFriendList) + friendRouterGroup.POST("/add_blacklist", friend.AddBlacklist) + friendRouterGroup.POST("/get_blacklist", friend.GetBlacklist) + friendRouterGroup.POST("/remove_blacklist", friend.RemoveBlacklist) + friendRouterGroup.POST("/delete_friend", friend.DeleteFriend) + friendRouterGroup.POST("/add_friend_response", friend.AddFriendResponse) + friendRouterGroup.POST("/set_friend_comment", friend.SetFriendComment) + } + //group related routing group + /*groupRouterGroup := r.Group("/group") + { + groupRouterGroup.POST("/create_group", group.CreateGroup) + groupRouterGroup.POST("/get_group_list", group.GetGroupList) + groupRouterGroup.POST("/get_group_info", group.GetGroupInfo) + groupRouterGroup.POST("/delete_group_member", group.DeleteGroupMember) + groupRouterGroup.POST("/set_group_name", group.SetGroupName) + groupRouterGroup.POST("/set_group_bulletin", group.SetGroupBulletin) + groupRouterGroup.POST("/set_owner_group_nickname", group.SetOwnerGroupNickname) + groupRouterGroup.POST("/set_group_head_image", group.SetGroupHeadImage) + groupRouterGroup.POST("/member_exit_group", group.MemberExitGroup) + }*/ + //certificate + authRouterGroup := r.Group("/auth") + { + authRouterGroup.POST("/user_register", apiAuth.UserRegister) + authRouterGroup.POST("/user_token", apiAuth.UserToken) + } + //Third service + thirdGroup := r.Group("/third") + { + thirdGroup.POST("/tencent_cloud_storage_credential", apiThird.TencentCloudStorageCredential) + } + //Message + chatGroup := r.Group("/chat") + { + chatGroup.POST("/newest_seq", apiChat.UserNewestSeq) + chatGroup.POST("/pull_msg", apiChat.UserPullMsg) + chatGroup.POST("/send_msg", apiChat.UserSendMsg) + } + + ginPort := flag.Int("port", 10000, "get ginServerPort from cmd,default 10000 as port") + flag.Parse() + r.Run(utils.ServerIP + ":" + strconv.Itoa(*ginPort)) +} diff --git a/src/api/third/tencent_cloud_storage_credential.go b/src/api/third/tencent_cloud_storage_credential.go new file mode 100644 index 000000000..e7d299842 --- /dev/null +++ b/src/api/third/tencent_cloud_storage_credential.go @@ -0,0 +1,70 @@ +package apiThird + +import ( + "Open_IM/src/common/config" + log2 "Open_IM/src/common/log" + "github.com/gin-gonic/gin" + sts "github.com/tencentyun/qcloud-cos-sts-sdk/go" + "net/http" + "time" +) + +type paramsTencentCloudStorageCredential struct { + Token string `json:"token"` + OperationID string `json:"operationID"` +} + +func TencentCloudStorageCredential(c *gin.Context) { + params := paramsTencentCloudStorageCredential{} + if err := c.BindJSON(¶ms); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"errCode": 400, "errMsg": "Parameter parsing error,please check the parameters and request service again"}) + return + } + + log2.Info(params.Token, params.OperationID, "api TencentUpLoadCredential call start...") + + cli := sts.NewClient( + config.Config.Credential.Tencent.SecretID, + config.Config.Credential.Tencent.SecretKey, + nil, + ) + log2.Info(c.Request.Header.Get("token"), c.PostForm("optionID"), "api TencentUpLoadCredential sts.NewClient cli = %v", cli) + + opt := &sts.CredentialOptions{ + DurationSeconds: int64(time.Hour.Seconds()), + Region: config.Config.Credential.Tencent.Region, + Policy: &sts.CredentialPolicy{ + Statement: []sts.CredentialPolicyStatement{ + { + Action: []string{ + "name/cos:PostObject", + "name/cos:PutObject", + }, + Effect: "allow", + Resource: []string{ + "qcs::cos:" + config.Config.Credential.Tencent.Region + ":uid/" + config.Config.Credential.Tencent.AppID + ":" + config.Config.Credential.Tencent.Bucket + "/*", + }, + }, + }, + }, + } + log2.Info(c.Request.Header.Get("token"), c.PostForm("optionID"), "api TencentUpLoadCredential sts.CredentialOptions opt = %v", opt) + + res, err := cli.GetCredential(opt) + if err != nil { + log2.Error(c.Request.Header.Get("token"), c.PostForm("optionID"), "api TencentUpLoadCredential cli.GetCredential err = %s", err.Error()) + c.JSON(http.StatusOK, gin.H{ + "errCode": config.ErrTencentCredential.ErrCode, + "errMsg": err.Error(), + "data": res, + }) + return + } + log2.Info(c.Request.Header.Get("token"), c.PostForm("optionID"), "api TencentUpLoadCredential cli.GetCredential success res = %v, res.Credentials = %v", res, res.Credentials) + + c.JSON(http.StatusOK, gin.H{ + "errCode": 0, + "errMsg": "", + "data": res, + }) +}