package core import ( "context" "encoding/json" "io" "net/http" "strings" common "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/wshandler" "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) func splitDocument(doc bson.M) bson.M { setdoc := bson.M{} unsetdoc := bson.M{} findoc := bson.M{} for k, v := range doc { if k == "$set" { setdoc = v.(bson.M) } else if k == "$unset" { unsetdoc = v.(bson.M) } } for k, v := range doc { if v == nil { unsetdoc[k] = 1 } else if k[0] != '$' { setdoc[k] = v } } if len(setdoc) > 0 { findoc["$set"] = setdoc } if len(unsetdoc) > 0 { findoc["$unset"] = unsetdoc } return findoc } // CreateGroup : 그룹 생성 // - 그룹 : 멤버와 권한을 관리할 수 있다. 그룹 타입에 따라 디비에 저장되거나 메모리에만 존재한다. // - 생성 요청이 오면 파티를 만든다. 파티을 만들 수 있는지 여부는 서비스에서 결정할 것이고, 이 요청을 호출했다는 것은 서비스가 결정한 그룹 생성 조건을 다 통과했다는 의미이다. // - parameter : // - type : 그룹 종류. 그룹 종류에 따라 인덱스와 쿼리 가능 field가 다르다. func (sub *subTavern) CreateGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") grouptype := sub.groups[typename] if grouptype == nil { logger.Println("CreateGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } doc := bson.M{} if err := readBsonDoc(r.Body, &doc); err != nil { logger.Error("CreateGroup failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusBadRequest) return } inserted, err := grouptype.Create(r.Form, doc) if err != nil { w.WriteHeader(http.StatusBadRequest) return } w.Write(inserted[:]) } // JoinGroup : 그룹에 참가 // - type : 그룹 타입 // - 그룹 타입에 맞는 키(주로 _id) // - member_id : 참가 멤버의 아이디 // - body : 멤버의 속성 bson document func (sub *subTavern) JoinGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("JoinGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } doc := bson.M{} if err := readBsonDoc(r.Body, &doc); err != nil { logger.Error("JoinGroup failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("JoinGroup failed. gid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } midobj, midok := common.ReadObjectIDFormValue(r.Form, "mid") tidobj, tidok := common.ReadObjectIDFormValue(r.Form, "tid") if !midok && !tidok { // 둘다 없네? logger.Println("JoinGroup failed. tid or mid should be exist") w.WriteHeader(http.StatusBadRequest) return } var err error if candidate, ok := common.ReadBoolFormValue(r.Form, "candidate"); ok && candidate { err = group.Candidate(gidobj, midobj, doc) } else { tidobj, err = group.Join(gidobj, midobj, tidobj, doc) } if err == nil { json.NewEncoder(w).Encode(map[string]string{ "gid": gidobj.Hex(), "tid": tidobj.Hex(), }) } else if err == errGroupNotExist { w.Write([]byte("{}")) } else if err != nil { logger.Error("JoinGroup failed :", err) w.WriteHeader(http.StatusInternalServerError) } } func (sub *subTavern) EnterCandidateChannel(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") if _, ok := sub.groups[typename]; !ok { logger.Println("EnterCandidateChannel failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } midobj, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("EnterCandidateChannel failed. mid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("EnterCandidateChannel failed. gid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } // candidate channel은 big endian 최상위 비트가 1 gidobj[0] |= 0x80 if conn := sub.wsh.Conn(sub.region, midobj); conn != nil { richConnOuter{wsh: sub.wsh, rc: conn}.JoinTag(sub.region, gidobj, midobj, typename) } else { sub.wshRpc.caller.One(midobj).JoinTag(sub.region, gidobj, midobj, typename) } } func (sub *subTavern) LeaveCandidateChannel(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") if _, ok := sub.groups[typename]; !ok { logger.Println("EnterCandidateChannel failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } midobj, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("EnterCandidateChannel failed. mid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("EnterCandidateChannel failed. gid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } // candidate channel은 big endian 최상위 비트가 1 gidobj[0] |= 0x80 if conn := sub.wsh.Conn(sub.region, midobj); conn != nil { richConnOuter{wsh: sub.wsh, rc: conn}.LeaveTag(sub.region, gidobj, midobj, typename) } else { sub.wshRpc.caller.One(midobj).LeaveTag(sub.region, gidobj, midobj, typename) } } func (sub *subTavern) EnterGroupChannel(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("EnterGroupChannel failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } midobj, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("EnterGroupChannel failed. mid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("EnterGroupChannel failed. gid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } tid := group.FindTicketID(gidobj, midobj) if tid.IsZero() { logger.Println("EnterGroupChannel failed. tid is zero") w.WriteHeader(http.StatusBadRequest) return } if conn := sub.wsh.Conn(sub.region, midobj); conn != nil { richConnOuter{wsh: sub.wsh, rc: conn}.JoinTag(sub.region, gidobj, tid, typename) } else { sub.wshRpc.caller.One(midobj).JoinTag(sub.region, gidobj, tid, typename) } writeBsonDoc(w, primitive.M{"_id": tid}) } func (sub *subTavern) SetStateInGroup(w http.ResponseWriter, r *http.Request) { gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("SetStateInGroup failed. tag is missing :", r) w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("SetStateInGroup failed. mid form value is missing") w.WriteHeader(http.StatusBadRequest) return } state, ok := common.ReadStringFormValue(r.Form, "state") if !ok { logger.Println("SetStateInGroup failed. state is missing :", r) w.WriteHeader(http.StatusBadRequest) return } typename, ok := common.ReadStringFormValue(r.Form, "type") if !ok { logger.Println("SetStateInGroup failed. type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } var doc bson.M if err := readBsonDoc(r.Body, &doc); err != nil { logger.Error("SetStateInGroup failed. readBsonDoc err :", err) w.WriteHeader(http.StatusBadRequest) return } tid := doc["_id"].(primitive.ObjectID) if conn := sub.wsh.Conn(sub.region, mid); conn != nil { richConnOuter{wsh: sub.wsh, rc: conn}.SetStateInTag(sub.region, gid, tid, state, typename) } else { sub.wshRpc.caller.One(mid).SetStateInTag(sub.region, gid, tid, state, typename) } } // Invite : 초대 // - type : 초대 타입 (required) // - from : 초대하는 자 (required) // - to : 초대받는 자 (required) // - timeout : 초대 유지시간(optional. 없으면 config 기본 값) // - (body) : 검색시 노출되는 document func (sub *subTavern) Invite(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("Invite failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("Invite failed. gid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("Invite failed. mid is missing :", r) w.WriteHeader(http.StatusBadRequest) return } var reqdoc struct { Inviter bson.M `bson:"inviter"` Invitee bson.M `bson:"invitee"` } if err := readBsonDoc(r.Body, &reqdoc); err != nil { logger.Error("Invite failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } result, err := group.Invite(gid, mid, reqdoc.Inviter, reqdoc.Invitee) if err != nil { logger.Error("Invite failed. group.Invite returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } w.Write([]byte(result)) } func (sub *subTavern) UpdateGroupMember(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("UpdateGroupMember failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("UpdateGroupMember failed. gid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } midobj, midok := common.ReadObjectIDFormValue(r.Form, "mid") tidobj, tidok := common.ReadObjectIDFormValue(r.Form, "tid") if !midok && !tidok { // 둘다 없네? logger.Println("JoinGroup failed. tid or mid should be exist") w.WriteHeader(http.StatusBadRequest) return } var err error delete, _ := common.ReadBoolFormValue(r.Form, "delete") if delete { err = group.UpdateGroupMember(gidobj, midobj, tidobj, nil) } else { var doc bson.M if err := readBsonDoc(r.Body, &doc); err != nil { logger.Error("UpdateGroupMember failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } err = group.UpdateGroupMember(gidobj, midobj, tidobj, doc) } if err != nil { logger.Println("UpdateGroupMember failed. Update returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) CancelInvitation(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("CancelInvitation failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } tid, ok := common.ReadObjectIDFormValue(r.Form, "tid") if !ok { logger.Println("CancelInvitation failed. form value 'tid' is missing") w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("CancelInvitation failed. form value 'gid' is missing") w.WriteHeader(http.StatusBadRequest) return } if err := group.CancelInvitation(gid, tid); err != nil { logger.Println("CancelInvitation failed. group.CancelInvitation returns err :", err) w.WriteHeader(http.StatusInternalServerError) } } func (sub *subTavern) AcceptInvitation(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("CancelInvitation failed. group type is missing :", r) w.WriteHeader(http.StatusBadRequest) return } gid, _ := common.ReadObjectIDFormValue(r.Form, "gid") mid, _ := common.ReadObjectIDFormValue(r.Form, "mid") tid, ok := common.ReadObjectIDFormValue(r.Form, "tid") if !ok { logger.Println("CancelInvitation failed. form value 'tid' is missing") w.WriteHeader(http.StatusBadRequest) return } var member bson.M if err := readBsonDoc(r.Body, &member); err != nil { logger.Error("AcceptInvitation failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } gidbytes, err := group.AcceptInvitation(gid, mid, tid, member) if err != nil { logger.Error("AcceptInvitation failed. group.AcceptInvitation returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } w.Write([]byte(gidbytes.Hex())) } func (sub *subTavern) DenyInvitation(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("DenyInvitation failed. group type is missing") w.WriteHeader(http.StatusBadRequest) return } gid, _ := common.ReadObjectIDFormValue(r.Form, "gid") mid, _ := common.ReadObjectIDFormValue(r.Form, "mid") tid, ok := common.ReadObjectIDFormValue(r.Form, "tid") if !ok { logger.Println("DenyInvitation failed. tid is missing") w.WriteHeader(http.StatusBadRequest) return } err := group.DenyInvitation(gid, mid, tid) if err != nil { logger.Error("DenyInvitation failed. group.DenyInvitation returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) QueryInvitations(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("QueryInvitations failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("QueryInvitations failed. mid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } var after primitive.Timestamp if v, ok := common.ReadStringFormValue(r.Form, "after"); ok && v != "0.0" { after = common.DotStringToTimestamp(v) } result, err := group.QueryInvitations(mid, after) if err != nil { logger.Println("QueryInvitations failed. group.QueryInvitations returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } if err := writeBsonArr(w, result); err != nil { logger.Println("QueryInvitations failed. Encode returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) TurnGroupOnline(w http.ResponseWriter, r *http.Request) { // group을 online 상태로 만든다. // 요청을 보내는 클라이언트의 conn이 끊이면 online에서 제거한다. // online인 group을 가지고 뭘 할지는 게임이 알아서... typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("TurnGroupOnline failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "_id") if !ok { logger.Println("TurnGroupOnline failed. group id '_id' form value is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("TurnGroupOnline failed. mid form value is missing") w.WriteHeader(http.StatusBadRequest) return } var filter bson.M if err := readBsonDoc(r.Body, &filter); err != nil { logger.Error("TurnGroupOnline failed. readBsonDoc return err :", err) w.WriteHeader(http.StatusBadRequest) return } exist, err := group.Exist(gid, filter) if err != nil { logger.Error("TurnGroupOnline failed. FindOne return err :", err) w.WriteHeader(http.StatusBadRequest) return } if !exist { logger.Println("TurnGroupOnline failed. filter not match", filter) w.WriteHeader(http.StatusBadRequest) return } score, ok := common.ReadFloatFormValue(r.Form, "score") if !ok { score = 100 } if conn := sub.wsh.Conn(sub.region, mid); conn != nil { err = richConnOuter{wsh: sub.wsh, rc: conn}.TurnGroupOnline(onlineGroupQueryKey(typename), gid, score) } else { err = sub.wshRpc.caller.One(mid).TurnGroupOnline(onlineGroupQueryKey(typename), gid, score) } if err != nil { logger.Error("TurnGroupOnline failed. TurnGroupOnline err :", err) w.WriteHeader(http.StatusBadRequest) return } } func (sub *subTavern) TurnGroupOffline(w http.ResponseWriter, r *http.Request) { // group을 offline 상태로 만든다. // 요청을 보내는 클라이언트의 conn이 끊이면 online에서 제거한다. // online인 group을 가지고 뭘 할지는 게임이 알아서... typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("TurnGroupOffline failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "_id") if !ok { logger.Println("TurnGroupOffline failed. group id '_id' form value is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("TurnGroupOffline failed. mid form value is missing") w.WriteHeader(http.StatusBadRequest) return } // onlinename := onlineGroupQueryKey(typename) // if onClose := conn.UnregistOnCloseFunc(onlinename); onClose != nil { // onClose() // } else { // gid, ok := common.ReadStringFormValue(form, "_id") // if ok { // sub.redisSync.ZRem(context.Background(), onlinename, gid) // } // } var err error if conn := sub.wsh.Conn(sub.region, mid); conn != nil { err = richConnOuter{wsh: sub.wsh, rc: conn}.TurnGroupOffline(onlineGroupQueryKey(typename), gid) } else { err = sub.wshRpc.caller.One(mid).TurnGroupOffline(onlineGroupQueryKey(typename), gid) } if err != nil { logger.Error("TurnGroupOffline failed. TurnGroupOnline err :", err) w.WriteHeader(http.StatusBadRequest) return } } func (sub *subTavern) QueryOnlineGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("QueryOnlineGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } var cmd *redis.StringSliceCmd scoreStart, _ := common.ReadStringFormValue(r.Form, "score_start") scoreStop, _ := common.ReadStringFormValue(r.Form, "score_stop") if len(scoreStart) > 0 || len(scoreStop) > 0 { if len(scoreStart) == 0 { scoreStart = "-inf" } if len(scoreStop) == 0 { scoreStop = "+inf" } cmd = sub.wsh.RedisSync.ZRangeArgs(context.Background(), redis.ZRangeArgs{ Key: onlineGroupQueryKey(typename), ByScore: true, Start: scoreStart, Stop: scoreStop, Rev: true, Count: 1, }) } else { // 아무거나 cmd = sub.wsh.RedisSync.ZRandMember(context.Background(), onlineGroupQueryKey(typename), 1, false) } result, err := cmd.Result() if err != nil { logger.Error("QueryOnlineGroup failed. redid.ZRandMember returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } writeBsonDoc(w, bson.M{"r": result}) } func (sub *subTavern) SearchGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("SearchGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } projection, _ := common.ReadStringFormValue(r.Form, "projection") var filter bson.M if err := readBsonDoc(r.Body, &filter); err != nil { logger.Error("SearchGroup failed. readBsonDoc returns err :", err) w.WriteHeader(http.StatusBadRequest) return } result, err := group.FindAll(filter, projection, primitive.Timestamp{}) if err != nil { logger.Error("SearchGroup failed. FindAll err :", err) w.WriteHeader(http.StatusInternalServerError) return } if result == nil { return } if err := writeBsonArr(w, result); err != nil { logger.Error("json marshal failed :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) QueryOnlineState(w http.ResponseWriter, r *http.Request) { mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("IsOnline failed. mid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } state, err := sub.wsh.GetState(mid) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.Write([]byte(state)) } func (sub *subTavern) IsOnline(w http.ResponseWriter, r *http.Request) { mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("IsOnline failed. mid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } ok, err := sub.wsh.IsOnline(mid) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } if ok { w.Write([]byte("true")) } else { w.Write([]byte("false")) } } // QueryGroup : 그룹조회 // - type : 그룹 타입 // - 그룹 타입에 맞는 키(주로 _id) // - projection : select할 필드. ,로 구분 func (sub *subTavern) QueryGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("QueryGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "_id") if !ok { logger.Println("QueryGroup failed. _id is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } projection, _ := common.ReadStringFormValue(r.Form, "projection") after, _ := common.ReadStringFormValue(r.Form, "after") if after != "0.0" { projection += ",+luts" } result, err := group.FindOne(gid, projection) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } if result == nil { return } if len(after) > 0 { if luts, ok := result["luts"].(primitive.Timestamp); ok { afterts := common.DotStringToTimestamp(after) if primitive.CompareTimestamp(luts, afterts) < 0 { return } } } if err := writeBsonDoc(w, result); err != nil { logger.Error("json marshal failed :", err) w.WriteHeader(http.StatusInternalServerError) return } } // QueryGroupMembers : 그룹내 멤버 조회 // - type : 그룹 타입 // - 그룹 타입에 맞는 키(주로 _id) // - projection : select할 필드. ,로 구분 func (sub *subTavern) QueryGroupMembers(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("QueryGroupMembers failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("QueryGroupMembers failed. _id is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } midobj, _ := common.ReadObjectIDFormValue(r.Form, "mid") var after primitive.Timestamp if ts, ok := common.ReadStringFormValue(r.Form, "after"); ok && ts != "0.0" { after = common.DotStringToTimestamp(ts) } projection, _ := common.ReadStringFormValue(r.Form, "projection") result, err := group.QueryMembers(gidobj, midobj, projection, after) if err != nil { logger.Error("QueryGroupMembers failed. FindAll err :", err) w.WriteHeader(http.StatusInternalServerError) return } if result == nil { return } if err := writeBsonDoc(w, result); err != nil { logger.Error("QueryGroupMembers failed. writeBsonArr err :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) QueryGroupMember(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("QueryGroupMember failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("QueryGroupMember failed. gid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } mid, midok := common.ReadObjectIDFormValue(r.Form, "mid") tid, tidok := common.ReadObjectIDFormValue(r.Form, "tid") if !midok && !tidok { // 둘 중 하나는 있어야지 logger.Println("QueryGroupMember failed. tid and mid are both missing") w.WriteHeader(http.StatusBadRequest) return } projection, _ := common.ReadStringFormValue(r.Form, "projection") result, err := group.QueryMember(gid, mid, tid, projection) if err != nil { logger.Println("QueryGroupMember failed. group.QueryMember returns err :", err) w.WriteHeader(http.StatusBadRequest) return } if result == nil { return } if err := writeBsonDoc(w, result); err != nil { logger.Error("QueryGroupMember failed. writeBsonDoc err :", err) w.WriteHeader(http.StatusInternalServerError) return } } // LeaveGroup : 그룹에서 나감 or 내보냄 // - type : 그룹 타입 // - 그룹 타입에 맞는 키(주로 _id) // - member_id : 나갈 멤버의 아이디 func (sub *subTavern) LeaveGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("LeaveGroup failed. group type is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("LeaveGroup failed. gid is missing :", r.Form) w.WriteHeader(http.StatusBadRequest) return } mid, midok := common.ReadObjectIDFormValue(r.Form, "mid") tid, tidok := common.ReadObjectIDFormValue(r.Form, "tid") if !midok && !tidok { // 둘 중 하나는 있어야지 logger.Println("LeaveGroup failed. tid and mid are both missing") w.WriteHeader(http.StatusBadRequest) return } if err := group.Leave(gid, mid, tid); err != nil { // 둘 중 하나는 있어야지 logger.Println("LeaveGroup failed. group.Leave returns err :", err) w.WriteHeader(http.StatusBadRequest) } } func (sub *subTavern) UpdateMemberDocument(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("DismissGroup failed. type is missing") w.WriteHeader(http.StatusBadRequest) return } midobj, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("UpdateMemberDocument failed. member_id is missing") w.WriteHeader(http.StatusBadRequest) return } gidobj, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("UpdateMemberDocument failed. _id is missing") w.WriteHeader(http.StatusBadRequest) return } var updatedoc bson.M if err := readBsonDoc(r.Body, &updatedoc); err != nil { logger.Error("UpdateMemberDocument failed. body decoding error :", err) w.WriteHeader(http.StatusBadRequest) return } if err := group.UpdateMemberDocument(gidobj, midobj, updatedoc); err != nil { logger.Println("UpdateMemberDocument failed :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) DismissGroup(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("DismissGroup failed. type is missing") w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("DismissGroup failed. gid is missing :") w.WriteHeader(http.StatusBadRequest) return } if err := group.Dismiss(gid); err != nil { logger.Error("DismissGroup failed. group.Dismiss returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } } func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("UpdateGroupDocument failed. type is missing") w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("UpdateGroupDocument failed. gid is missing") w.WriteHeader(http.StatusBadRequest) return } body, err := io.ReadAll(r.Body) if err != nil { logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err) w.WriteHeader(http.StatusBadRequest) return } if err := group.UpdateGroupDocument(gid, body); err != nil { logger.Error("UpdateGroupDocument failed. group.UpdateGroupDocument returns err :", err) w.WriteHeader(http.StatusBadRequest) return } } func (sub *subTavern) DropPausedMember(w http.ResponseWriter, r *http.Request) { typename, _ := common.ReadStringFormValue(r.Form, "type") group := sub.groups[typename] if group == nil { logger.Println("DropDeadMember failed. type is missing") w.WriteHeader(http.StatusBadRequest) return } gid, ok := common.ReadObjectIDFormValue(r.Form, "gid") if !ok { logger.Println("DropDeadMember failed. gid is missing") w.WriteHeader(http.StatusBadRequest) return } mid, ok := common.ReadObjectIDFormValue(r.Form, "mid") if !ok { logger.Println("DropDeadMember failed. mid is missing") w.WriteHeader(http.StatusBadRequest) return } if err := group.DropPausedMember(gid, mid); err != nil { logger.Error("DropDeadMember failed. group.DropDeadMember returns err :", err) w.WriteHeader(http.StatusBadRequest) return } } func (sub *subTavern) deliveryMessageHandler(deliveryChan <-chan wshandler.DeliveryMessage) { defer func() { r := recover() if r != nil { logger.Error(r) } }() redisSync := sub.wsh.RedisSync for msg := range deliveryChan { mid := msg.Alias if msg.Body != nil { buffer := msg.Body var channame string for i, ch := range buffer { if ch == 0 { channame = string(buffer[:i]) buffer = buffer[i+1:] break } } if len(channame) == 0 { continue } buffer = append(mid[:], buffer...) _, err := redisSync.Publish(context.Background(), channame, buffer).Result() if err != nil { logger.Error(err) } } if len(msg.Command) > 0 { switch msg.Command { case "pause": gidtype := msg.Conn.GetTag("gid") if len(gidtype) > 0 { tokens := strings.SplitN(gidtype, "@", 2) gidobj, _ := primitive.ObjectIDFromHex(tokens[0]) gtype := tokens[1] group := sub.groups[gtype] if group != nil { group.PauseMember(gidobj, msg.Alias, msg.Conn) } } } } } logger.Println("delivery chan fin") }