Merge remote-tracking branch 'origin/main'

This commit is contained in:
Gordon 2021-05-26 19:38:57 +08:00
commit 6c2beab9e5
27 changed files with 1454 additions and 2 deletions

View File

@ -2,7 +2,7 @@
Open-IM-Server is open source instant messaging Server.Backend in Go.
![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/Open-IM.png)
[![LICENSE](https://img.shields.io/github/license/pingcap/tidb.svg)](https://github.com/pingcap/tidb/blob/master/LICENSE)
[![LICENSE](https://img.shields.io/github/license/pingcap/tidb.svg)](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/LICENSE)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
## Open-IM-Server: Open source Instant Messaging Server
@ -334,7 +334,7 @@ All images are available at https://hub.docker.com/r/lyt1123/open_im_server
- **Authentication Clow Chart**
![avatar](https://github.com/Open-IM-IM/opim_admin/blob/main/docs/open-im-server.png)
![avatar](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/open-im-server.png)
## Architecture

View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
source ./style_info.cfg
source ./path_info.cfg
source ./function.sh
#begin path
begin_path=$PWD
for ((i = 0; i < ${#service_source_root[*]}; i++)); do
cd $begin_path
service_path=${service_source_root[$i]}
cd $service_path && echo -e "${SKY_BLUE_PREFIX}Current directory: $PWD $COLOR_SUFFIX"
make install && echo -e "${SKY_BLUE_PREFIX}build ${service_names[$i]} success,moving binary file to the bin directory${COLOR_SUFFIX}" &&
echo -e "${SKY_BLUE_PREFIX}Successful moved ${service_names[$i]} to the bin directory${COLOR_SUFFIX}\n"
done

19
script/start_all.sh Normal file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
#fixme This script is the total startup script
#fixme The full name of the shell script that needs to be started is placed in the need_to_start_server_shell array
#fixme Put the shell script name here
need_to_start_server_shell=(
auto_start_service.sh
msg_gateway_start.sh
push_start.sh
msg_transfer_start.sh
)
for i in ${need_to_start_server_shell[*]}; do
chmod +x $i
./$i
done
while [ true ]; do
sleep 60
done

View File

@ -0,0 +1,59 @@
#!/usr/bin/env bash
source ./style_info.cfg
source ./path_info.cfg
source ./function.sh
#service filename
service_filename=(
#api
open_im_api
#rpc
open_im_user
open_im_friend
open_im_group
open_im_auth
${msg_name}
)
#service config port name
service_port_name=(
#api port name
openImApiPort
#rpc port name
openImUserPort
openImFriendPort
openImGroupPort
openImAuthPort
openImOfflineMessagePort
)
for ((i = 0; i < ${#service_filename[*]}; i++)); do
#Check whether the service exists
service_name="ps -aux |grep -w ${service_filename[$i]} |grep -v grep"
count="${service_name}| wc -l"
if [ $(eval ${count}) -gt 0 ]; then
pid="${service_name}| awk '{print \$2}'"
echo -e "${SKY_BLUE_PREFIX}${service_filename[$i]} service has been started,pid:$(eval $pid)$COLOR_SUFFIX"
echo -e "${SKY_BLUE_PREFIX}Killing the service ${service_filename[$i]} pid:$(eval $pid)${COLOR_SUFFIX}"
#kill the service that existed
kill -9 $(eval $pid)
sleep 0.5
fi
cd ../bin && echo -e "${SKY_BLUE_PREFIX}${service_filename[$i]} service is starting${COLOR_SUFFIX}"
#Get the rpc port in the configuration file
portList=$(cat $config_path | grep ${service_port_name[$i]} | awk -F '[:]' '{print $NF}')
list_to_string ${portList}
#Start related rpc services based on the number of ports
for j in ${ports_array}; do
echo -e "${SKY_BLUE_PREFIX}${service_filename[$i]} Service is starting,port number:$j $COLOR_SUFFIX"
#Start the service in the background
./${service_filename[$i]} -port $j &
# nohup ./${service_filename[$i]} -port $j >/dev/null 2>&1 &
sleep 1
pid="netstat -ntlp|grep $j |awk '{printf \$7}'|cut -d/ -f1"
echo -e "${RED_PREFIX}${service_filename[$i]} Service is started,port number:$j pid:$(eval $pid)$COLOR_SUFFIX"
done
done

19
script/stop_all.sh Normal file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
#fixme This script is to stop the service
source ./style_info.cfg
source ./path_info.cfg
for i in ${service_names[*]}; do
#Check whether the service exists
name="ps -aux |grep -w $i |grep -v grep"
count="${name}| wc -l"
if [ $(eval ${count}) -gt 0 ]; then
pid="${name}| awk '{print \$2}'"
echo -e "${SKY_BLUE_PREFIX}Killing service:$i pid:$(eval $pid)${COLOR_SUFFIX}"
#kill the service that existed
kill -9 $(eval $pid)
echo -e "${SKY_BLUE_PREFIX}service:$i was killed ${COLOR_SUFFIX}"
fi
done

View File

@ -0,0 +1,70 @@
package im_mysql_model
import (
"Open_IM/src/common/db"
_ "github.com/jinzhu/gorm/dialects/mysql"
"time"
)
func InsertToFriend(ownerId, friendId string, flag int32) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
toInsertFollow := Friend{
OwnerId: ownerId,
FriendId: friendId,
FriendFlag: flag,
CreateTime: time.Now(),
}
err = dbConn.Table("friend").Create(toInsertFollow).Error
if err != nil {
return err
}
return nil
}
func FindFriendRelationshipFromFriend(ownerId, friendId string) (*Friend, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var friend Friend
err = dbConn.Table("friend").Where("owner_id=? and friend_id=?", ownerId, friendId).Find(&friend).Error
if err != nil {
return nil, err
}
return &friend, err
}
func FindUserInfoFromFriend(ownerId string) ([]Friend, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
//dbConn.LogMode(true)
var friends []Friend
err = dbConn.Table("friend").Where("owner_id=?", ownerId).Find(&friends).Error
if err != nil {
return nil, err
}
return friends, nil
}
func UpdateFriendComment(ownerId, friendId, comment string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update friend set comment=? where owner_id=? and friend_id=?", comment, ownerId, friendId).Error
return err
}
func DeleteSingleFriendInfo(ownerId, friendId string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Table("friend").Where("owner_id=? and friend_id=?", ownerId, friendId).Delete(Friend{}).Error
return err
}

View File

@ -0,0 +1,57 @@
package im_mysql_model
import (
"Open_IM/src/common/db"
"time"
)
func InsertIntoFriendReq(reqId, userId string, flag int32, reqMessage string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("insert into friend_request(req_id,user_id,flag,req_message,create_time) values(?,?,?,?,?)", reqId, userId, flag, reqMessage, time.Now()).Error
if err != nil {
return err
}
return nil
}
func FindFriendsApplyFromFriendReq(userId string) ([]FriendRequest, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var usersInfo []FriendRequest
//dbConn.LogMode(true)
err = dbConn.Table("friend_request").Where("user_id=?", userId).Find(&usersInfo).Error
if err != nil {
return nil, err
}
return usersInfo, nil
}
func FindFriendRelationshipFromFriendReq(reqId, userId string) (*FriendRequest, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var friendRequest FriendRequest
err = dbConn.Table("friend_request").Where("req_id=? and user_id=?", reqId, userId).Find(&friendRequest).Error
if err != nil {
return nil, err
}
return &friendRequest, nil
}
func UpdateFriendRelationshipToFriendReq(reqId, userId string, flag int32) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update friend_request set flag=? where req_id=? and user_id=?", flag, reqId, userId).Error
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,98 @@
package im_mysql_model
import "Open_IM/src/common/db"
func InsertIntoGroupMember(groupId, userId string, isAdmin int64) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("insert into `group_member`(group_id,user_id,is_admin) values(?,?,?)", groupId, userId, isAdmin).Error
if err != nil {
return err
}
return nil
}
func FindGroupMemberListByUserId(userId string) ([]GroupMember, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var groupMemberList []GroupMember
err = dbConn.Raw("select * from `group_member` where user_id=?", userId).Find(&groupMemberList).Error
if err != nil {
return nil, err
}
return groupMemberList, nil
}
func FindGroupMemberListByGroupId(groupId string) ([]GroupMember, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var groupMemberList []GroupMember
err = dbConn.Raw("select * from `group_member` where group_id=?", groupId).Find(&groupMemberList).Error
if err != nil {
return nil, err
}
return groupMemberList, nil
}
func FindGroupMemberInfoByGroupIdAndUserId(groupId, userId string) (*GroupMember, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var groupMember GroupMember
err = dbConn.Raw("select * from `group_member` where group_id=? and user_id=? limit 1", groupId, userId).Scan(&groupMember).Error
if err != nil {
return nil, err
}
return &groupMember, nil
}
func DeleteGroupMemberByGroupIdAndUserId(groupId, userId string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("delete from `group_member` where group_id=? and user_id=?", groupId, userId).Error
if err != nil {
return err
}
return nil
}
func UpdateOwnerGroupNickName(groupId, userId, groupNickName string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update `group_member` set nickname=? where group_id=? and user_id=?", groupNickName, groupId, userId).Error
if err != nil {
return err
}
return nil
}
func SelectGroupList(groupID string) ([]string, error) {
var groupUserID string
var groupList []string
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return groupList, err
}
rows, err := dbConn.Model(&GroupMember{}).Where("group_id = ?", groupID).Select("user_id").Rows()
if err != nil {
return groupList, err
}
defer rows.Close()
for rows.Next() {
rows.Scan(&groupUserID)
groupList = append(groupList, groupUserID)
}
return groupList, nil
}

View File

@ -0,0 +1,67 @@
package im_mysql_model
import "Open_IM/src/common/db"
func InsertIntoGroup(groupId, name, groupHeadUrl string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
//Default group name
if name == "" {
name = "groupChat"
}
err = dbConn.Exec("insert into `group`(group_id,name,head_url) values(?,?,?)", groupId, name, groupHeadUrl).Error
if err != nil {
return err
}
return nil
}
func FindGroupInfoByGroupId(groupId string) (*Group, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var groupInfo Group
err = dbConn.Raw("select * from `group` where group_id=?", groupId).Scan(&groupInfo).Error
if err != nil {
return nil, err
}
return &groupInfo, nil
}
func UpdateGroupName(groupId, groupName string) (err error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update `group` set name=? where group_id=?", groupName, groupId).Error
if err != nil {
return err
}
return nil
}
func UpdateGroupBulletin(groupId, bulletinContent string) (err error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update `group` set bulletin=? where group_id=?", bulletinContent, groupId).Error
if err != nil {
return err
}
return nil
}
func UpdateGroupHeadImage(groupId, headImageUrl string) (err error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("update `group` set head_url=? where group_id=?", headImageUrl, groupId).Error
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,48 @@
package im_mysql_model
import "time"
type User struct {
UID string `gorm:"column:uid"`
Name string `gorm:"column:name"`
Icon string `gorm:"column:icon"`
Gender int32 `gorm:"column:gender"`
Mobile string `gorm:"column:mobile"`
Birth string `gorm:"column:birth"`
Email string `gorm:"column:email"`
Ex string `gorm:"column:ex"`
CreateTime time.Time `gorm:"column:create_time"`
}
type Friend struct {
OwnerId string `gorm:"column:owner_id"`
FriendId string `gorm:"column:friend_id"`
Comment string `gorm:"column:comment"`
FriendFlag int32 `gorm:"column:friend_flag"`
CreateTime time.Time `gorm:"column:create_time"`
}
type FriendRequest struct {
ReqId string `gorm:"column:req_id"`
UserId string `gorm:"column:user_id"`
Flag int32 `gorm:"column:flag"`
ReqMessage string `gorm:"column:req_message"`
CreateTime time.Time `gorm:"column:create_time"`
}
type BlackList struct {
OwnerId string `gorm:"column:owner_id"`
BlockId string `gorm:"column:block_id"`
CreateTime time.Time `gorm:"column:create_time"`
}
type Group struct {
GroupId string `gorm:"column:group_id"`
Name string `gorm:"column:name"`
HeadURL string `gorm:"column:head_url"`
Bulletin string `gorm:"column:bulletin"`
}
type GroupMember struct {
GroupId string `gorm:"column:group_id"`
UserId string `gorm:"column:user_id"`
NickName string `gorm:"column:nickname"`
IsAdmin int32 `gorm:"column:is_admin"`
}

View File

@ -0,0 +1,48 @@
package im_mysql_model
import (
"Open_IM/src/common/db"
"time"
)
func InsertInToUserBlackList(ownerID, blockID string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
toInsertInfo := BlackList{OwnerId: ownerID, BlockId: blockID, CreateTime: time.Now()}
err = dbConn.Table("user_black_list").Create(toInsertInfo).Error
return err
}
func FindRelationshipFromBlackList(ownerID, blockID string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
var blackList BlackList
err = dbConn.Table("user_black_list").Where("owner_id=? and block_id=?", ownerID, blockID).Find(&blackList).Error
return err
}
func RemoveBlackList(ownerID, blockID string) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
err = dbConn.Exec("delete from user_black_list where owner_id=? and block_id=?", ownerID, blockID).Error
return err
}
func GetBlackListByUID(ownerID string) ([]BlackList, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var blackListUsersInfo []BlackList
err = dbConn.Table("user_black_list").Where("owner_id=?", ownerID).Find(&blackListUsersInfo).Error
if err != nil {
return nil, err
}
return blackListUsersInfo, nil
}

View File

@ -0,0 +1,104 @@
package im_mysql_model
import (
"Open_IM/src/common/db"
pbAuth "Open_IM/src/proto/auth"
_ "github.com/jinzhu/gorm/dialects/mysql"
"time"
)
func UserRegister(pb *pbAuth.UserRegisterReq) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
addUser := User{
UID: pb.UID,
Name: pb.Name,
Icon: pb.Icon,
Gender: pb.Gender,
Mobile: pb.Mobile,
Birth: pb.Birth,
Email: pb.Email,
Ex: pb.Ex,
CreateTime: time.Now(),
}
err = dbConn.Table("user").Create(&addUser).Error
if err != nil {
return err
}
return nil
}
func FindUserByUID(uid string) (*User, error) {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return nil, err
}
var user User
err = dbConn.Table("user").Where("uid=?", uid).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}
func UpDateUserInfo(uid, name, headUrl, mobilePhoneNum, birth, email, extendInfo string, gender int32) error {
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return err
}
if name != "" {
if err = dbConn.Exec("update user set name=? where uid=?", name, uid).Error; err != nil {
return err
}
}
if headUrl != "" {
if err = dbConn.Exec("update user set icon=? where uid=?", headUrl, uid).Error; err != nil {
return err
}
}
if mobilePhoneNum != "" {
if err = dbConn.Exec("update user set mobile=? where uid=?", mobilePhoneNum, uid).Error; err != nil {
return err
}
}
if birth != "" {
if err = dbConn.Exec("update user set birth=? where uid=?", birth, uid).Error; err != nil {
return err
}
}
if email != "" {
if err = dbConn.Exec("update user set email=? where uid=?", email, uid).Error; err != nil {
return err
}
}
if extendInfo != "" {
if err = dbConn.Exec("update user set ex=? where uid=?", extendInfo, uid).Error; err != nil {
return err
}
}
if gender != 0 {
if err = dbConn.Exec("update user set gender=? where uid=?", gender, uid).Error; err != nil {
return err
}
}
return nil
}
func SelectAllUID() ([]string, error) {
var uid []string
dbConn, err := db.DB.MysqlDB.DefaultGormDB()
if err != nil {
return uid, err
}
rows, _ := dbConn.Raw("select uid from user").Rows()
defer rows.Close()
var strUID string
for rows.Next() {
rows.Scan(&strUID)
uid = append(uid, strUID)
}
return uid, nil
}

View File

@ -0,0 +1,72 @@
package multi_terminal_login
import (
"Open_IM/src/common/config"
"Open_IM/src/common/constant"
"Open_IM/src/common/db"
pbChat "Open_IM/src/proto/chat"
"Open_IM/src/push/logic"
"Open_IM/src/utils"
)
func MultiTerminalLoginChecker(uid, token string, platformID int32) error {
// 1.check userid and platform class 0 not exists and 1 exists
existsInterface, err := db.DB.ExistsUserIDAndPlatform(uid, utils.PlatformNameToClass(utils.PlatformIDToName(platformID)))
if err != nil {
return err
}
exists := existsInterface.(int64)
//get config multi login policy
if config.Config.MultiLoginPolicy.OnlyOneTerminalAccess {
//OnlyOneTerminalAccess policy need to check all terminal
if utils.PlatformNameToClass(utils.PlatformIDToName(platformID)) == "PC" {
existsInterface, err = db.DB.ExistsUserIDAndPlatform(uid, "Mobile")
if err != nil {
return err
}
} else {
existsInterface, err = db.DB.ExistsUserIDAndPlatform(uid, "PC")
if err != nil {
return err
}
}
exists = existsInterface.(int64)
if exists == 1 {
err := db.DB.SetUserIDAndPlatform(uid, utils.PlatformNameToClass(utils.PlatformIDToName(platformID)), token, config.Config.TokenPolicy.AccessExpire)
if err != nil {
return err
}
PushMessageToTheTerminal(uid, platformID)
return nil
}
} else if config.Config.MultiLoginPolicy.MobileAndPCTerminalAccessButOtherTerminalKickEachOther {
// common terminal need to kick eich other
if exists == 1 {
err := db.DB.SetUserIDAndPlatform(uid, utils.PlatformNameToClass(utils.PlatformIDToName(platformID)), token, config.Config.TokenPolicy.AccessExpire)
if err != nil {
return err
}
PushMessageToTheTerminal(uid, platformID)
return nil
}
}
err = db.DB.SetUserIDAndPlatform(uid, utils.PlatformNameToClass(utils.PlatformIDToName(platformID)), token, config.Config.TokenPolicy.AccessExpire)
if err != nil {
return err
}
PushMessageToTheTerminal(uid, platformID)
return nil
}
func PushMessageToTheTerminal(uid string, platform int32) {
logic.SendMsgByWS(&pbChat.WSToMsgSvrChatMsg{
SendID: uid,
RecvID: uid,
Content: "Your account is already logged on other terminal,please confirm",
SendTime: utils.GetCurrentTimestampBySecond(),
MsgFrom: constant.SysMsgType,
ContentType: constant.KickOnlineTip,
PlatformID: platform,
})
}

26
src/rpc/auth/Makefile Normal file
View File

@ -0,0 +1,26 @@
.PHONY: all build run gotool install clean help
BINARY_NAME=open_im_auth
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

View File

@ -0,0 +1,61 @@
package rpcAuth
import (
"Open_IM/src/common/config"
log2 "Open_IM/src/common/log"
pbAuth "Open_IM/src/proto/auth"
"Open_IM/src/utils"
"github.com/skiffer-git/grpc-etcdv3/getcdv3"
"google.golang.org/grpc"
"net"
"strconv"
"strings"
)
type rpcAuth struct {
rpcPort int
rpcRegisterName string
etcdSchema string
etcdAddr []string
}
func NewRpcAuthServer(port int) *rpcAuth {
return &rpcAuth{
rpcPort: port,
rpcRegisterName: config.Config.RpcRegisterName.RpcGetTokenName,
etcdSchema: config.Config.Etcd.EtcdSchema,
etcdAddr: config.Config.Etcd.EtcdAddr,
}
}
func (rpc *rpcAuth) Run() {
log2.Info("", "", "rpc get_token init...")
address := utils.ServerIP + ":" + strconv.Itoa(rpc.rpcPort)
listener, err := net.Listen("tcp", address)
if err != nil {
log2.Error("", "", "listen network failed, err = %s, address = %s", err.Error(), address)
return
}
log2.Info("", "", "listen network success, address = %s", address)
//grpc server
srv := grpc.NewServer()
defer srv.GracefulStop()
//service registers with etcd
pbAuth.RegisterAuthServer(srv, rpc)
err = getcdv3.RegisterEtcd(rpc.etcdSchema, strings.Join(rpc.etcdAddr, ","), utils.ServerIP, rpc.rpcPort, rpc.rpcRegisterName, 10)
if err != nil {
log2.Error("", "", "register rpc get_token to etcd failed, err = %s", err.Error())
return
}
err = srv.Serve(listener)
if err != nil {
log2.Info("", "", "rpc get_token fail, err = %s", err.Error())
return
}
log2.Info("", "", "rpc get_token init success")
}

View File

@ -0,0 +1,20 @@
package rpcAuth
import (
"Open_IM/src/common/db/mysql_model/im_mysql_model"
"Open_IM/src/common/log"
pbAuth "Open_IM/src/proto/auth"
"context"
)
func (rpc *rpcAuth) UserRegister(_ context.Context, pb *pbAuth.UserRegisterReq) (*pbAuth.UserRegisterResp, error) {
log.Info("", "", "rpc user_register start, [data: %s]", pb.String())
if err := im_mysql_model.UserRegister(pb); err != nil {
log.Error("", "", "rpc user_register error, [data: %s] [err: %s]", pb.String(), err.Error())
return &pbAuth.UserRegisterResp{Success: false}, err
}
log.Info("", "", "rpc user_register success return")
return &pbAuth.UserRegisterResp{Success: true}, nil
}

View File

@ -0,0 +1,29 @@
package rpcAuth
import (
"Open_IM/src/common/db/mysql_model/im_mysql_model"
"Open_IM/src/common/log"
pbAuth "Open_IM/src/proto/auth"
"Open_IM/src/utils"
"context"
)
func (rpc *rpcAuth) UserToken(_ context.Context, pb *pbAuth.UserTokenReq) (*pbAuth.UserTokenResp, error) {
log.Info("", "", "rpc user_token call start..., [pbTokenReq: %s]", pb.String())
_, err := im_mysql_model.FindUserByUID(pb.UID)
if err != nil {
log.Error("", "", "rpc user_token call..., im_mysql_model.AppServerFindFromUserByUserID fail [uid: %s] [err: %s]", pb.UID, err.Error())
return &pbAuth.UserTokenResp{ErrCode: 500, ErrMsg: err.Error()}, err
}
log.Info("", "", "rpc user_token call..., im_mysql_model.AppServerFindFromUserByUserID")
tokens, expTime, err := utils.CreateToken(pb.UID, "", pb.Platform)
if err != nil {
log.Error("", "", "rpc user_token call..., utils.CreateToken fail [uid: %s] [err: %s]", pb.UID, err.Error())
return &pbAuth.UserTokenResp{ErrCode: 500, ErrMsg: err.Error()}, err
}
log.Info("", "", "rpc user_token success return, [uid: %s] [tokens: %s]", pb.UID, tokens)
return &pbAuth.UserTokenResp{Token: tokens, ExpiredTime: expTime}, nil
}

23
utils/cors_middleware.go Normal file
View File

@ -0,0 +1,23 @@
package utils
import (
"github.com/gin-gonic/gin"
"net/http"
)
func CorsHandler() gin.HandlerFunc {
return func(context *gin.Context) {
context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
context.Header("Access-Control-Allow-Methods", "*")
context.Header("Access-Control-Allow-Headers", "*")
context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
context.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
context.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
context.Header("content-type", "application/json") // 设置返回格式是json
//Release all option pre-requests
if context.Request.Method == http.MethodOptions {
context.JSON(http.StatusOK, "Options Request!")
}
context.Next()
}
}

22
utils/file.go Normal file
View File

@ -0,0 +1,22 @@
package utils
import "os"
// Determine whether the given path is a folder
func IsDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
}
// Determine whether the given path is a file
func IsFile(path string) bool {
return !IsDir(path)
}
// Create a directory
func MkDir(path string) error {
return os.MkdirAll(path, os.ModePerm)
}

35
utils/get_server_ip.go Normal file
View File

@ -0,0 +1,35 @@
package utils
import (
"Open_IM/src/common/config"
"net"
)
var ServerIP = ""
func init() {
//fixme In the configuration file, ip takes precedence, if not, get the valid network card ip of the machine
if config.Config.ServerIP != "" {
ServerIP = config.Config.ServerIP
return
}
//fixme Get the ip of the local network card
netInterfaces, err := net.Interfaces()
if err != nil {
panic(err)
}
for i := 0; i < len(netInterfaces); i++ {
//Exclude useless network cards by judging the net.flag Up flag
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
address, _ := netInterfaces[i].Addrs()
for _, addr := range address {
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
ServerIP = ipNet.IP.String()
return
}
}
}
}
}
}

56
utils/image.go Normal file
View File

@ -0,0 +1,56 @@
package utils
import (
"errors"
"github.com/nfnt/resize"
"golang.org/x/image/bmp"
"image"
"image/gif"
"image/jpeg"
"image/png"
"io"
"os"
)
func GenSmallImage(src, dst string) error {
fIn, _ := os.Open(src)
defer fIn.Close()
fOut, _ := os.Create(dst)
defer fOut.Close()
if err := scale(fIn, fOut, 0, 0, 0); err != nil {
return err
}
return nil
}
func scale(in io.Reader, out io.Writer, width, height, quality int) error {
origin, fm, err := image.Decode(in)
if err != nil {
return err
}
if width == 0 || height == 0 {
width = origin.Bounds().Max.X / 2
height = origin.Bounds().Max.Y / 2
}
if quality == 0 {
quality = 25
}
canvas := resize.Thumbnail(uint(width), uint(height), origin, resize.Lanczos3)
switch fm {
case "jpeg":
return jpeg.Encode(out, canvas, &jpeg.Options{quality})
case "png":
return png.Encode(out, canvas)
case "gif":
return gif.Encode(out, canvas, &gif.Options{})
case "bmp":
return bmp.Encode(out, canvas)
default:
return errors.New("ERROR FORMAT")
}
return nil
}

192
utils/jwt_token.go Normal file
View File

@ -0,0 +1,192 @@
package utils
import (
"Open_IM/src/common/config"
"Open_IM/src/common/db"
"errors"
"time"
)
var (
TokenExpired = errors.New("token is timed out, please log in again")
TokenInvalid = errors.New("token has been invalidated")
TokenNotValidYet = errors.New("token not active yet")
TokenMalformed = errors.New("that's not even a token")
TokenUnknown = errors.New("couldn't handle this token")
)
type Claims struct {
UID string
Platform string //login platform
jwt.StandardClaims
}
func BuildClaims(uid, accountAddr, platform string, ttl int64) Claims {
now := time.Now().Unix()
//if ttl=-1 Permanent token
if ttl == -1 {
return Claims{
UID: uid,
Platform: platform,
StandardClaims: jwt.StandardClaims{
ExpiresAt: -1,
IssuedAt: now,
NotBefore: now,
}}
}
return Claims{
UID: uid,
Platform: platform,
StandardClaims: jwt.StandardClaims{
ExpiresAt: now + ttl, //Expiration time
IssuedAt: now, //Issuing time
NotBefore: now, //Begin Effective time
}}
}
func CreateToken(userID, accountAddr string, platform int32) (string, int64, error) {
claims := BuildClaims(userID, accountAddr, PlatformIDToName(platform), config.Config.TokenPolicy.AccessExpire)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(config.Config.TokenPolicy.AccessSecret))
return tokenString, claims.ExpiresAt, err
}
func secret() jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
return []byte(config.Config.TokenPolicy.AccessSecret), nil
}
}
func ParseToken(tokensString string) (claims *Claims, err error) {
token, err := jwt.ParseWithClaims(tokensString, &Claims{}, secret())
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenUnknown
}
}
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
// 1.check userid and platform class 0 not exists and 1 exists
existsInterface, err := db.DB.ExistsUserIDAndPlatform(claims.UID, Platform2class[claims.Platform])
if err != nil {
return nil, err
}
exists := existsInterface.(int64)
//get config multi login policy
if config.Config.MultiLoginPolicy.OnlyOneTerminalAccess {
//OnlyOneTerminalAccess policy need to check all terminal
//When only one end is allowed to log in, there is a situation that needs to be paid attention to. After PC login,
//mobile login should check two platform times. One of them is less than the redis storage time, which is the invalid token.
if Platform2class[claims.Platform] == "PC" {
existsInterface, err = db.DB.ExistsUserIDAndPlatform(claims.UID, "Mobile")
if err != nil {
return nil, err
}
exists = existsInterface.(int64)
if exists == 1 {
res, err := MakeTheTokenInvalid(*claims, "Mobile")
if err != nil {
return nil, err
}
if res {
return nil, TokenInvalid
}
}
} else {
existsInterface, err = db.DB.ExistsUserIDAndPlatform(claims.UID, "PC")
if err != nil {
return nil, err
}
exists = existsInterface.(int64)
if exists == 1 {
res, err := MakeTheTokenInvalid(*claims, "PC")
if err != nil {
return nil, err
}
if res {
return nil, TokenInvalid
}
}
}
if exists == 1 {
res, err := MakeTheTokenInvalid(*claims, Platform2class[claims.Platform])
if err != nil {
return nil, err
}
if res {
return nil, TokenInvalid
}
}
} else if config.Config.MultiLoginPolicy.MobileAndPCTerminalAccessButOtherTerminalKickEachOther {
if exists == 1 {
res, err := MakeTheTokenInvalid(*claims, Platform2class[claims.Platform])
if err != nil {
return nil, err
}
if res {
return nil, TokenInvalid
}
}
}
return claims, nil
}
return nil, TokenUnknown
}
func MakeTheTokenInvalid(currentClaims Claims, platformClass string) (bool, error) {
storedRedisTokenInterface, err := db.DB.GetPlatformToken(currentClaims.UID, platformClass)
if err != nil {
return false, err
}
storedRedisPlatformClaims, err := ParseRedisInterfaceToken(storedRedisTokenInterface)
if err != nil {
return false, err
}
//if issue time less than redis token then make this token invalid
if currentClaims.IssuedAt < storedRedisPlatformClaims.IssuedAt {
return true, TokenInvalid
}
return false, nil
}
func ParseRedisInterfaceToken(redisToken interface{}) (*Claims, error) {
token, err := jwt.ParseWithClaims(string(redisToken.([]uint8)), &Claims{}, secret())
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenInvalid
}
}
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, err
}
//Validation token, false means failure, true means successful verification
func VerifyToken(token, uid string) bool {
claims, err := ParseToken(token)
if err != nil {
return false
} else if claims.UID != uid {
return false
} else {
return true
}
}

119
utils/map.go Normal file
View File

@ -0,0 +1,119 @@
package utils
import (
"encoding/json"
"sync"
)
type Map struct {
sync.RWMutex
m map[interface{}]interface{}
}
func (m *Map) init() {
if m.m == nil {
m.m = make(map[interface{}]interface{})
}
}
func (m *Map) UnsafeGet(key interface{}) interface{} {
if m.m == nil {
return nil
} else {
return m.m[key]
}
}
func (m *Map) Get(key interface{}) interface{} {
m.RLock()
defer m.RUnlock()
return m.UnsafeGet(key)
}
func (m *Map) UnsafeSet(key interface{}, value interface{}) {
m.init()
m.m[key] = value
}
func (m *Map) Set(key interface{}, value interface{}) {
m.Lock()
defer m.Unlock()
m.UnsafeSet(key, value)
}
func (m *Map) TestAndSet(key interface{}, value interface{}) interface{} {
m.Lock()
defer m.Unlock()
m.init()
if v, ok := m.m[key]; ok {
return v
} else {
m.m[key] = value
return nil
}
}
func (m *Map) UnsafeDel(key interface{}) {
m.init()
delete(m.m, key)
}
func (m *Map) Del(key interface{}) {
m.Lock()
defer m.Unlock()
m.UnsafeDel(key)
}
func (m *Map) UnsafeLen() int {
if m.m == nil {
return 0
} else {
return len(m.m)
}
}
func (m *Map) Len() int {
m.RLock()
defer m.RUnlock()
return m.UnsafeLen()
}
func (m *Map) UnsafeRange(f func(interface{}, interface{})) {
if m.m == nil {
return
}
for k, v := range m.m {
f(k, v)
}
}
func (m *Map) RLockRange(f func(interface{}, interface{})) {
m.RLock()
defer m.RUnlock()
m.UnsafeRange(f)
}
func (m *Map) LockRange(f func(interface{}, interface{})) {
m.Lock()
defer m.Unlock()
m.UnsafeRange(f)
}
func MapToJsonString(param map[string]interface{}) string {
dataType, _ := json.Marshal(param)
dataString := string(dataType)
return dataString
}
func JsonStringToMap(str string) map[string]interface{} {
var tempMap map[string]interface{}
_ = json.Unmarshal([]byte(str), &tempMap)
return tempMap
}
func GetSwitchFromOptions(Options map[string]interface{}, key string) (result bool) {
if flag, ok := Options[key]; !ok || flag == 1 {
return true
}
return false
}

13
utils/md5.go Normal file
View File

@ -0,0 +1,13 @@
package utils
import (
"crypto/md5"
"encoding/hex"
)
func Md5(s string) string {
h := md5.New()
h.Write([]byte(s))
cipher := h.Sum(nil)
return hex.EncodeToString(cipher)
}

View File

@ -0,0 +1,66 @@
package utils
// fixme 1<--->IOS 2<--->Android 3<--->Windows
//fixme 4<--->OSX 5<--->Web 6<--->MiniWeb 7<--->Linux
const (
//Platform ID
IOSPlatformID = 1
AndroidPlatformID = 2
WindowsPlatformID = 3
OSXPlatformID = 4
WebPlatformID = 5
MiniWebPlatformID = 6
LinuxPlatformID = 7
//Platform string match to Platform ID
IOSPlatformStr = "IOS"
AndroidPlatformStr = "Android"
WindowsPlatformStr = "Windows"
OSXPlatformStr = "OSX"
WebPlatformStr = "Web"
MiniWebPlatformStr = "MiniWeb"
LinuxPlatformStr = "Linux"
//terminal types
TerminalPC = "PC"
TerminalMobile = "Mobile"
)
var PlatformID2Name = map[int32]string{
IOSPlatformID: IOSPlatformStr,
AndroidPlatformID: AndroidPlatformStr,
WindowsPlatformID: WindowsPlatformStr,
OSXPlatformID: OSXPlatformStr,
WebPlatformID: WebPlatformStr,
MiniWebPlatformID: MiniWebPlatformStr,
LinuxPlatformID: LinuxPlatformStr,
}
var PlatformName2ID = map[string]int32{
IOSPlatformStr: IOSPlatformID,
AndroidPlatformStr: AndroidPlatformID,
WindowsPlatformStr: WindowsPlatformID,
OSXPlatformStr: OSXPlatformID,
WebPlatformStr: WebPlatformID,
MiniWebPlatformStr: MiniWebPlatformID,
LinuxPlatformStr: LinuxPlatformID,
}
var Platform2class = map[string]string{
IOSPlatformStr: TerminalMobile,
AndroidPlatformStr: TerminalMobile,
MiniWebPlatformStr: TerminalMobile,
WindowsPlatformStr: TerminalPC,
OSXPlatformStr: TerminalPC,
WebPlatformStr: TerminalPC,
LinuxPlatformStr: TerminalPC,
}
func PlatformIDToName(num int32) string {
return PlatformID2Name[num]
}
func PlatformNameToID(name string) int32 {
return PlatformName2ID[name]
}
func PlatformNameToClass(name string) string {
return Platform2class[name]
}

41
utils/strings.go Normal file
View File

@ -0,0 +1,41 @@
/*
** description("").
** copyright('tuoyun,www.tuoyun.net').
** author("fg,Gordon@tuoyun.net").
** time(2021/4/8 15:09).
*/
package utils
import "strconv"
func IntToString(i int) string {
return strconv.FormatInt(int64(i), 10)
}
func StringToInt(i string) int {
j, _ := strconv.Atoi(i)
return j
}
func StringToInt64(i string) int64 {
j, _ := strconv.ParseInt(i, 10, 64)
return j
}
//judge a string whether in the string list
func IsContain(target string, List []string) bool {
for _, element := range List {
if target == element {
return true
}
}
return false
}
func InterfaceArrayToStringArray(data []interface{}) (i []string) {
for _, param := range data {
i = append(i, param.(string))
}
return i
}

72
utils/time_format.go Normal file
View File

@ -0,0 +1,72 @@
/*
** description("").
** copyright('tuoyun,www.tuoyun.net').
** author("fg,Gordon@tuoyun.net").
** time(2021/2/22 11:52).
*/
package utils
import (
"strconv"
"time"
)
const (
TimeOffset = 8 * 3600 //8 hour offset
HalfOffset = 12 * 3600 //Half-day hourly offset
)
//Get the current timestamp by Second
func GetCurrentTimestampBySecond() int64 {
return time.Now().Unix()
}
//Convert timestamp to time.Time type
func UnixSecondToTime(second int64) time.Time {
return time.Unix(second, 0)
}
//Get the current timestamp by Nano
func GetCurrentTimestampByNano() int64 {
return time.Now().UnixNano()
}
//Get the current timestamp by Mill
func GetCurrentTimestampByMill() int64 {
return time.Now().UnixNano() / 1e6
}
//Get the timestamp at 0 o'clock of the day
func GetCurDayZeroTimestamp() int64 {
timeStr := time.Now().Format("2006-01-02")
t, _ := time.Parse("2006-01-02", timeStr)
return t.Unix() - TimeOffset
}
//Get the timestamp at 12 o'clock on the day
func GetCurDayHalfTimestamp() int64 {
return GetCurDayZeroTimestamp() + HalfOffset
}
//Get the formatted time at 0 o'clock of the day, the format is "2006-01-02_00-00-00"
func GetCurDayZeroTimeFormat() string {
return time.Unix(GetCurDayZeroTimestamp(), 0).Format("2006-01-02_15-04-05")
}
//Get the formatted time at 12 o'clock of the day, the format is "2006-01-02_12-00-00"
func GetCurDayHalfTimeFormat() string {
return time.Unix(GetCurDayZeroTimestamp()+HalfOffset, 0).Format("2006-01-02_15-04-05")
}
func GetTimeStampByFormat(datetime string) string {
timeLayout := "2006-01-02 15:04:05"
loc, _ := time.LoadLocation("Local")
tmp, _ := time.ParseInLocation(timeLayout, datetime, loc)
timestamp := tmp.Unix()
return strconv.FormatInt(timestamp, 10)
}
func TimeStringFormatTimeUnix(timeFormat string, timeSrc string) int64 {
tm, _ := time.Parse(timeFormat, timeSrc)
return tm.Unix()
}