diff --git a/config_template.json b/config_template.json index 261b84c..ef23c98 100644 --- a/config_template.json +++ b/config_template.json @@ -1,65 +1,72 @@ { - "region_storage" : { - "default" : { - "mongo" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", - "redis" : { - "url" : "redis://192.168.8.94:6379", - "offset" : { - "cache" : 0, - "session" : 1, - "ranking" : 2 - } + "region_storage": { + "default": { + "mongo": "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", + "redis": { + "cache": "redis://192.168.8.94:6380/0", + "session": "redis://192.168.8.94:6380/1", + "ranking": "redis://192.168.8.94:6380/2", + "wshandler": "redis://192.168.8.94:6380/3", + "tavern": "redis://192.168.8.94:6380/4" } }, - "dev" : { - "mongo" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", - "redis" : { - "url" : "redis://192.168.8.94:6379", - "offset" : { - "cache" : 0, - "session" : 1, - "ranking" : 2 - } + "dev": { + "mongo": "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", + "redis": { + "cache": "redis://192.168.8.94:6380/4", + "session": "redis://192.168.8.94:6380/5", + "ranking": "redis://192.168.8.94:6380/6", + "wshandler": "redis://192.168.8.94:6380/7", + "tavern": "redis://192.168.8.94:6380/8" } } }, - "maingate_mongodb_url" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", - "maingate_service_url" : "http://localhost/maingate", - "maingate_api_token" : "63d08aa34f0162622c11284b", - - "tavern_service_url" : "http://localhost/tavern", - "tavern_group_types" : { - "subjugate" : { - "text_search_field" : ["name"], - "unique_index" : ["name,_id", "_id,members", "name,hidden"], - "search_index" : ["rules"], - "member_index" : ["_gid,candidate,luts","_gid,luts","_gid,expiring"], - "invite_ttl" : 30, - "candidate_ttl" : 3600, - "invitee_exlusive" : true, - "invitee_is_member" : true, - "max_member" : 4 + "maingate_mongodb_url": "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false", + "maingate_service_url": "http://localhost/maingate", + "maingate_api_token": "63d08aa34f0162622c11284b", + "tavern_service_url": "http://localhost/tavern", + "tavern_group_types": { + "subjugate": { + "text_search_field": [ + "name" + ], + "unique_index": [ + "name,_id", + "_id,members", + "name,hidden" + ], + "search_index": [ + "rules" + ], + "member_index": [ + "_gid,candidate,luts", + "_gid,luts", + "_gid,expiring" + ], + "invite_ttl": 30, + "candidate_ttl": 3600, + "invitee_exlusive": true, + "invitee_is_member": true, + "max_member": 4 }, - "lobby" : { - "max_member" : 3, - "invitee_exlusive" : true, - "invitee_is_member" : true, - "transient" : true, - "invite_ttl" : 30 + "lobby": { + "max_member": 3, + "invitee_exlusive": true, + "invitee_is_member": true, + "transient": true, + "invite_ttl": 30 } }, - - "ws_sync_pipeline" : "redis://192.168.8.94:6379/3", - - "services" : { - "kingdom" : { - "개발중" : { - "url" :"http://localhost/warehouse/dev", - "development" : true + + "services": { + "kingdom": { + "개발중": { + "url": "http://localhost/warehouse/dev", + "development": true }, - "개인서버" : { - "url" : "http://localhost/warehouse/private", - "development" : false + "개인서버": { + "url": "http://localhost/warehouse/private", + "development": false } } } diff --git a/core/apiimpl.go b/core/apiimpl.go index 260fab2..8e58e25 100644 --- a/core/apiimpl.go +++ b/core/apiimpl.go @@ -1,7 +1,6 @@ package core import ( - "context" "encoding/json" "io" "net/http" @@ -9,7 +8,6 @@ import ( common "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" - "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -83,7 +81,7 @@ func (sub *subTavern) JoinGroup(w http.ResponseWriter, r *http.Request) { 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) + err = group.Join(gidobj, midobj, doc) } if err == nil { @@ -184,12 +182,6 @@ func (sub *subTavern) AcceptInvitation(w http.ResponseWriter, r *http.Request) { 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 { @@ -198,14 +190,15 @@ func (sub *subTavern) AcceptInvitation(w http.ResponseWriter, r *http.Request) { return } - gidbytes, err := group.AcceptInvitation(gid, mid, tid, member) + err := group.AcceptInvitation(gid, mid, member) if err != nil { logger.Error("AcceptInvitation failed. group.AcceptInvitation returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } - w.Write([]byte(gidbytes.Hex())) + // TODO : full group doc을 내려보냄 + // w.Write([]byte(gidbytes.Hex())) } func (sub *subTavern) DenyInvitation(w http.ResponseWriter, r *http.Request) { @@ -270,49 +263,6 @@ func (sub *subTavern) QueryInvitations(w http.ResponseWriter, r *http.Request) { } } -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] @@ -426,95 +376,6 @@ func (sub *subTavern) QueryGroup(w http.ResponseWriter, r *http.Request) { } } -// 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) @@ -535,15 +396,14 @@ func (sub *subTavern) LeaveGroup(w http.ResponseWriter, r *http.Request) { 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") + + if !midok { + logger.Println("LeaveGroup failed. mid is missing") w.WriteHeader(http.StatusBadRequest) return } - if err := group.Leave(gid, mid, tid); err != nil { + if err := group.Leave(gid, mid); err != nil { // 둘 중 하나는 있어야지 logger.Println("LeaveGroup failed. group.Leave returns err :", err) w.WriteHeader(http.StatusBadRequest) @@ -639,112 +499,3 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request return } } - -func (sub *subTavern) PauseGroupMember(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 - } - - group.PauseMember(gidobj, midobj) -} - -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") -// } diff --git a/core/config.json b/core/config.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/core/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/core/group.go b/core/group.go index dc1cf2e..44de821 100644 --- a/core/group.go +++ b/core/group.go @@ -25,21 +25,16 @@ type groupConfig struct { type group interface { Create(form url.Values, doc bson.M) (primitive.ObjectID, error) Candidate(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error - Join(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID, doc bson.M) (newTicketID primitive.ObjectID, err error) - FindTicketID(groupID primitive.ObjectID, memberID primitive.ObjectID) primitive.ObjectID + Join(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error Invite(groupID primitive.ObjectID, memberID primitive.ObjectID, inviterDoc bson.M, inviteeDoc bson.M) (string, error) CancelInvitation(groupID primitive.ObjectID, ticketID primitive.ObjectID) error - AcceptInvitation(groupID primitive.ObjectID, mid primitive.ObjectID, ticketID primitive.ObjectID, member bson.M) (primitive.ObjectID, error) + AcceptInvitation(groupID primitive.ObjectID, mid primitive.ObjectID, member bson.M) error DenyInvitation(groupID primitive.ObjectID, mid primitive.ObjectID, ticketID primitive.ObjectID) error QueryInvitations(memberID primitive.ObjectID, after primitive.Timestamp) ([]bson.M, error) Exist(groupID primitive.ObjectID, filter bson.M) (bool, error) FindAll(filter bson.M, projection string, after primitive.Timestamp) ([]bson.M, error) FindOne(groupID primitive.ObjectID, projection string) (bson.M, error) - QueryMembers(groupID primitive.ObjectID, requesterID primitive.ObjectID, projection string, after primitive.Timestamp) (map[string]bson.M, error) - QueryMember(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID, projection string) (bson.M, error) - Leave(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID) error - DropPausedMember(groupID primitive.ObjectID, memberID primitive.ObjectID) error - PauseMember(groupID primitive.ObjectID, memberID primitive.ObjectID) error + Leave(groupID primitive.ObjectID, memberID primitive.ObjectID) error UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error Dismiss(groupID primitive.ObjectID) error UpdateGroupDocument(groupID primitive.ObjectID, body []byte) error diff --git a/core/group_memory.go b/core/group_memory.go index b3fa700..ee766d4 100644 --- a/core/group_memory.go +++ b/core/group_memory.go @@ -2,24 +2,16 @@ package core import ( "context" - "crypto/md5" - "encoding/gob" - "encoding/hex" - "encoding/json" "errors" - "fmt" "net/url" - "os" - "strings" - "sync" "time" - "repositories.action2quare.com/ayo/gocommon/flagx" + "github.com/go-redis/redis/v8" + "github.com/nitishm/go-rejson/v4/rjs" + "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" - "repositories.action2quare.com/ayo/gocommon/rpc" "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" ) @@ -28,440 +20,182 @@ type accountID = primitive.ObjectID type ticketID = primitive.ObjectID type groupID = primitive.ObjectID -type Invitation struct { - GroupID groupID `json:"gid"` - TicketID ticketID `json:"tid"` - Inviter bson.M `json:"inviter"` - ExpireAtUTC int64 `json:"expire_at_utc"` +func makeTid(gid groupID, in accountID) string { + var out primitive.ObjectID + for i := range in { + out[12-i-1] = gid[i] ^ in[12-i-1] + } + return out.Hex() } -type memberDocCommon struct { - Body bson.M - Invite bool - InviteExpire time.Time - JoinTime int64 +type Invitation struct { + GroupID groupID `json:"gid"` + TicketID string `json:"tid"` + Inviter bson.M `json:"inviter"` + ExpireAtUTC int64 `json:"expire_at_utc"` } // 플레이어한테 공유하는 멤버 정보 -type PublicMemberDoc struct { - memberDocCommon `json:",inline"` - Tid ticketID +type memberDoc struct { + Body bson.M `json:"body"` + Invite bool `json:"invite"` + InviteExpire int64 `json:"invite_exp"` } -type FullGroupDoc struct { - Gid groupID - AllMembers []*PublicMemberDoc `json:",omitempty"` - Body GroupDocBody `json:",omitempty"` -} - -type GroupDocBody bson.M +type GroupDocBody = bson.M type InvitationFail bson.M -type memberDoc struct { - memberDocCommon `json:",inline"` - - // underscore keys in Hidden - Hidden bson.M - rconn *connection - Mid accountID -} - type groupDoc struct { - sync.Mutex + Body GroupDocBody `json:"body"` + Members map[string]*memberDoc `json:"members"` + InCharge string `json:"incharge"` - Body GroupDocBody - - InCharge accountID - tickets map[ticketID]*memberDoc - createTime time.Time + rh *RejsonHandler + id groupID + idhex string } -func init() { - gob.Register(PublicMemberDoc{}) -} - -func (gd *groupDoc) updateBodyWithBson(src []byte) ([]byte, error) { - gd.Lock() - defer gd.Unlock() - - err := bson.Unmarshal(src, &gd.Body) - if err != nil { - return nil, err +func (gd *groupDoc) strid() string { + if len(gd.idhex) == 0 { + gd.idhex = gd.id.Hex() } - - return bson.Marshal(gd.Body) + return gd.idhex } -func (gd *groupDoc) updateBodyWithJson(src []byte) GroupDocBody { - gd.Lock() - defer gd.Unlock() +func (gd *groupDoc) tid(in accountID) string { + return makeTid(gd.id, in) +} - err := json.Unmarshal(src, &gd.Body) - if err != nil { - return nil +func (gd *groupDoc) mid(tid string) accountID { + tidobj, _ := primitive.ObjectIDFromHex(tid) + var out primitive.ObjectID + for i := range tidobj { + out[12-i-1] = gd.id[i] ^ tidobj[12-i-1] } - - return gd.Body + return out } -func (gd *groupDoc) updateBodyBsonToJson(bsonSrc []byte) GroupDocBody { - gd.Lock() - defer gd.Unlock() - - err := bson.Unmarshal(bsonSrc, &gd.Body) - if err != nil { - return nil - } - - return gd.Body -} - -func (gd *groupDoc) updateBody(bsonSrc []byte) error { - gd.Lock() - defer gd.Unlock() - - return bson.Unmarshal(bsonSrc, &gd.Body) -} - -func (gd *groupDoc) addInvite(inviteeDoc bson.M, rconn *connection, ttl time.Duration, max int) (ticketID, *memberDoc) { - gd.Lock() - defer gd.Unlock() - +func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*memberDoc, error) { mid := inviteeDoc["_mid"].(accountID) body := inviteeDoc["body"].(bson.M) // 초대 가능한 빈 자리가 있나 + tids, err := gd.rh.JSONObjKeys(gd.strid(), "$.members") + if err != nil { + return nil, err + } + now := time.Now().UTC() - if len(gd.tickets) < max { - tid := primitive.NewObjectID() - newdoc := &memberDoc{ - memberDocCommon: memberDocCommon{ - Body: body, - Invite: true, - InviteExpire: now.Add(ttl), - }, - rconn: rconn, - Mid: mid, - } - gd.tickets[tid] = newdoc - return tid, newdoc - } - - for oldtid, mem := range gd.tickets { - if !mem.Invite { - continue - } - if mem.InviteExpire.Before(now) { - delete(gd.tickets, oldtid) - tid := primitive.NewObjectID() - newdoc := &memberDoc{ - memberDocCommon: memberDocCommon{ - Body: body, - Invite: true, - InviteExpire: now.Add(ttl), - }, - rconn: rconn, - Mid: mid, - } - gd.tickets[tid] = newdoc - return tid, newdoc + createNewDoc := func() *memberDoc { + return &memberDoc{ + Body: body, + Invite: true, + InviteExpire: now.Add(ttl).Unix(), } } - return primitive.NilObjectID, nil + if len(tids) < max { + newdoc := createNewDoc() + _, err := gd.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid), newdoc) + return newdoc, err + } + + expires, err := gd.rh.JSONGetInt64(gd.strid(), "$.members..invite_exp") + if err != nil { + return nil, err + } + + var delpaths []string + for i, expire := range expires { + if expire < now.Unix() { + // 만료된 초대가 있네? 지우자 + delpaths = append(delpaths, "$.members."+tids[i]) + } + } + + if len(delpaths) == 0 { + // 빈자리가 없다 + return nil, nil + } + + if err := gd.rh.JSONMDel(gd.strid(), delpaths); err != nil { + return nil, err + } + + newdoc := createNewDoc() + _, err = gd.rh.JSONSet(gd.strid(), "$.members."+mid.Hex(), newdoc) + return newdoc, err } -func seperateHidden(in bson.M) (public bson.M, hidden bson.M) { - for k, v := range in { - if k[0] == '_' { - if hidden == nil { - hidden = make(bson.M) - } - hidden[k] = v - } +func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) { + memdoc := &memberDoc{ + Invite: false, + InviteExpire: 0, + Body: doc, } - for k := range hidden { - delete(in, k) + if _, err := gd.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid), memdoc, rjs.SetOptionXX); err != nil { + return nil, err } - return in, hidden + + return memdoc, nil } -func (gd *groupDoc) addInCharge(mid accountID, rconn *connection, doc bson.M) (ticketID, *memberDoc) { - gd.Lock() - defer gd.Unlock() - - if !gd.InCharge.IsZero() { - return primitive.NilObjectID, nil - } - - gd.InCharge = mid - newtid := primitive.NewObjectID() - doc, hidden := seperateHidden(doc) - newdoc := &memberDoc{ - memberDocCommon: memberDocCommon{ - Body: doc, - Invite: false, - JoinTime: time.Now().UTC().Unix(), - }, - rconn: rconn, - Mid: mid, - Hidden: hidden, - } - - gd.tickets[newtid] = newdoc - if gd.Body == nil { - gd.Body = GroupDocBody(make(bson.M)) - } - gd.Body["incharge"] = newtid.Hex() - return newtid, newdoc -} - -func (gd *groupDoc) addMember(mid accountID, tid *ticketID, doc bson.M) (*memberDoc, bool) { - gd.Lock() - defer gd.Unlock() - - var memdoc *memberDoc - isNew := false - if tid.IsZero() { - for oldtid, d := range gd.tickets { - if d.Mid == mid { - memdoc = d - *tid = oldtid - isNew = true - break - } - } - } else { - var ok bool - memdoc, ok = gd.tickets[*tid] - if !ok { - // 티켓이 업네? - return nil, false - } - - if memdoc.Mid != mid { - // 내 티켓이 아니네? - return nil, false - } - } - - doc, hidden := seperateHidden(doc) - if memdoc != nil { - memdoc.Body = doc - memdoc.Hidden = hidden - - if memdoc.Invite { - isNew = true - memdoc.Invite = false - } - - if memdoc.JoinTime == 0 { - memdoc.JoinTime = time.Now().UTC().Unix() - } - } - return memdoc, isNew -} - -func (gd *groupDoc) removeMember(mid accountID, tid *ticketID) { - gd.Lock() - defer gd.Unlock() - - if tid.IsZero() { - for t, mem := range gd.tickets { - if mem.Mid == mid { - *tid = t - delete(gd.tickets, t) - return - } - } - } - - delete(gd.tickets, *tid) - - if gd.InCharge == mid { - gd.InCharge = primitive.NilObjectID - } -} - -func (gd *groupDoc) ticket(mid accountID) ticketID { - gd.Lock() - defer gd.Unlock() - - if mid.IsZero() { - return primitive.NilObjectID - } - - for t, m := range gd.tickets { - if m.Mid == mid { - return t - } - } - return primitive.NilObjectID -} - -func (gd *groupDoc) member(tid ticketID) *memberDoc { - gd.Lock() - defer gd.Unlock() - - return gd.tickets[tid] -} - -func (gd *groupDoc) memberByAccount(mid accountID) (ticketID, *memberDoc) { - gd.Lock() - defer gd.Unlock() - - for tid, doc := range gd.tickets { - if doc.Mid == mid { - return tid, doc - } - } - return primitive.NilObjectID, nil -} - -func (gd *groupDoc) modifyMemberDocument(mid accountID, tid *ticketID, cb func(b *memberDoc)) *memberDoc { - gd.Lock() - defer gd.Unlock() - - if tid.IsZero() { - for t, mem := range gd.tickets { - if mem.Mid == mid { - *tid = t - break - } - } - } - - if tid.IsZero() { - return nil - } - - if mem := gd.tickets[*tid]; mem != nil { - cb(mem) - return mem - } - - return nil -} - -func (gd *groupDoc) overwriteMemberDocument(mid accountID, tid *ticketID, raw []byte) *memberDoc { - gd.Lock() - defer gd.Unlock() - - if tid.IsZero() { - for t, mem := range gd.tickets { - if mem.Mid == mid { - *tid = t - json.Unmarshal(raw, &mem.Body) - return mem - } - } - } - - if mem := gd.tickets[*tid]; mem != nil { - var newbody primitive.M - json.Unmarshal(raw, &newbody) - mem.Body = newbody - return mem - } - - return nil -} - -func (gd *groupDoc) iterateMembers(cb func(ticketID, *memberDoc)) { - gd.Lock() - defer gd.Unlock() - - for k, v := range gd.tickets { - cb(k, v) - } -} - -func (gd *groupDoc) serializeFull(gid groupID) FullGroupDoc { - gd.Lock() - defer gd.Unlock() - - var output []*PublicMemberDoc - for k, v := range gd.tickets { - if v.Invite { - // 아직 초대 중인 대상. 패스 - continue - } - - output = append(output, &PublicMemberDoc{ - memberDocCommon: v.memberDocCommon, - Tid: k, - }) - } - - return FullGroupDoc{ - Gid: gid, - AllMembers: output, - Body: gd.Body, - } -} - -type groupContainer struct { - sync.Mutex - groupDocs map[groupID]*groupDoc +func (gd *groupDoc) removeMember(mid accountID) error { + _, err := gd.rh.JSONDel(gd.strid(), "$.members."+gd.tid(mid)) + return err } type groupInMemory struct { *groupConfig - groupDocSync func(groupID, []byte) error - memberSync func(groupID, accountID, ticketID, *memberDoc, bool) error - rpcCall func([]byte) error - hasConn func(accountID) *connection sendUpstreamMessage func(*wshandler.UpstreamMessage) sendEnterRoomMessage func(groupID, accountID) sendLeaveRoomMessage func(groupID, accountID) - groups groupContainer + rh *RejsonHandler } -func (gc *groupContainer) add(id groupID, doc *groupDoc) { - gc.Lock() - defer gc.Unlock() +func (gm *groupInMemory) createGroup(newid groupID, charge accountID, chargeDoc bson.M) (*groupDoc, error) { + tid := makeTid(newid, charge) - gc.groupDocs[id] = doc -} + gd := &groupDoc{ + Body: bson.M{}, + Members: map[string]*memberDoc{ + tid: { + Body: chargeDoc, + Invite: false, + InviteExpire: 0, + }, + }, + InCharge: tid, -func (gc *groupContainer) createWithID(newid groupID, base bson.M) (groupID, *groupDoc) { - gc.Lock() - defer gc.Unlock() - - if _, ok := gc.groupDocs[newid]; ok { - return primitive.NilObjectID, nil + rh: gm.rh, + id: newid, } - newdoc := newGroupDoc(base) - gc.groupDocs[newid] = newdoc - - return newid, newdoc -} - -func (gc *groupContainer) delete(gid groupID) { - gc.Lock() - defer gc.Unlock() - - delete(gc.groupDocs, gid) -} - -func (gc *groupContainer) find(id groupID) *groupDoc { - gc.Lock() - defer gc.Unlock() - - if found, ok := gc.groupDocs[id]; ok { - return found + _, err := gm.rh.JSONSet(gd.strid(), "$", gd, rjs.SetOptionNX) + if err != nil { + return nil, err } - return nil + return gd, nil } -func newGroupDoc(base bson.M) *groupDoc { +func (gm *groupInMemory) find(id groupID) (*groupDoc, error) { + if id.IsZero() { + return nil, nil + } + + _, err := gm.rh.JSONObjLen(id.Hex(), "$") + if err == redis.Nil { + return nil, nil + } + if err != nil { + return nil, err + } + return &groupDoc{ - Body: GroupDocBody(base), - createTime: time.Now().UTC(), - tickets: make(map[ticketID]*memberDoc), - } + rh: gm.rh, + id: id, + }, nil } func (gm *groupInMemory) Create(form url.Values, base bson.M) (groupID, error) { @@ -474,114 +208,26 @@ func (gm *groupInMemory) Candidate(gid groupID, mid accountID, doc bson.M) error } var errGroupNotExist = errors.New("group does not exist") -var errNoEmptySlot = errors.New("no more seat in group") -func (gm *groupInMemory) Join(gid groupID, mid accountID, tid ticketID, doc bson.M) (ticketID, error) { - group := gm.groups.find(gid) +func (gm *groupInMemory) Join(gid groupID, mid accountID, doc bson.M) error { + group, err := gm.find(gid) + if err != nil { + return err + } + if group == nil { // 그룹이 없다. 실패 - return primitive.NilObjectID, errGroupNotExist - } - - // 내 정보 업데이트할 때에도 사용됨 - // 굳이 InCharge가 있는 호스트가 아니어도 가능 - if memdoc, isNew := group.addMember(mid, &tid, doc); memdoc != nil { - gm.memberSync(gid, mid, tid, memdoc, isNew) - } - - return tid, nil -} - -func (gm *groupInMemory) FindTicketID(gid groupID, mid groupID) ticketID { - return primitive.NilObjectID -} - -// func sendTypedMessageDirect[T any](rconn *wshandler.Richconn, msg T) { -// bt, _ := json.Marshal(makeTypeMessage(msg)) -// rconn.WriteBytes(bt) -// } - -// func sendTypedMessage[T any](gm *groupInMemory, target accountID, msg T) { -// bt, _ := json.Marshal(makeTypeMessage(msg)) -// gm.SendMessage(target, bt) -// } - -// func (gm *groupInMemory) SendMessage(target accountID, msg []byte) { -// rconn := gm.hasConn(target) -// if rconn != nil { -// rconn.WriteBytes(msg) -// } else { -// gm.rpc(target).call(target, msg) -// } -// } - -// func multicast(conns []*wshandler.Richconn, raw []byte) { -// for _, rconn := range conns { -// rconn.WriteBytes(raw) -// } -// } - -// func broadcastTypedMessage[T any](gm *groupInMemory, gid groupID, msg T) { -// if gd := gm.groups.find(gid); gd != nil { -// bt, _ := json.Marshal(makeTypeMessage(msg)) -// go multicast(gd.conns(false), bt) -// } -// } - -var errInviteeDocMidMissing = errors.New("inviteeDoc must have '_mid' field") - -func (gm *groupInMemory) InviteImplement(gid groupID, mid accountID, inviteeDoc bson.M, inviterDoc bson.M) error { - targetid := inviteeDoc["_mid"].(accountID) - - // invitee에게 알림 - // invitee의 rconn이 종료될 때 그룹에 반영해야 하므로 rconn을 찾자 - rconn := gm.hasConn(targetid) - if rconn == nil { - return rpc.Make(gm).To(targetid).Call(gid, mid, inviteeDoc, inviterDoc) - } - - gd := gm.groups.find(gid) - if gd == nil { return errGroupNotExist } - if rconn.hasOnCloseFunc("member_remove_invite") { - // 이미 초대 중이다. - // inviter한테 알려줘야 한다. - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "@" + mid.Hex(), - Body: inviteeDoc, - Tag: []string{"InvitationFail"}, - }) - return nil - } + // 내 정보 업데이트할 때에도 사용됨 + _, err = group.addMember(mid, doc) - tid, newdoc := gd.addInvite(inviteeDoc, rconn, time.Duration(gm.InviteExpire)*time.Second, gm.MaxMember) - if newdoc == nil { - return errNoEmptySlot - } - - rconn.registOnCloseFunc("member_remove_invite", func() { - gd.removeMember(targetid, &tid) - gm.memberSync(gid, targetid, tid, nil, false) - }) - - gm.memberSync(gid, targetid, tid, newdoc, false) - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "@" + targetid.Hex(), - Body: Invitation{ - GroupID: gid, - TicketID: tid, - Inviter: inviterDoc, - ExpireAtUTC: newdoc.InviteExpire.Unix(), - }, - Tag: []string{"Invitation"}, - }) - - return nil + return err } -var errAlreayMember = errors.New("this target is already member") +var errInviteeDocMidMissing = errors.New("inviteeDoc must have '_mid' field") +var errAlreadyInvited = errors.New("this target is already invited by someone or me") func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, inviteeDoc bson.M) (string, error) { targetid, ok := inviteeDoc["_mid"].(accountID) @@ -589,111 +235,99 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i return "", errInviteeDocMidMissing } - if !gid.IsZero() { - if gd := gm.groups.find(gid); gd != nil { - if gd.InCharge != mid { - // 이러면 안된다. - // 초대는 InCharge만 할 수 있음 - return "", nil - } + // targetid에 초대한 mid가 들어있다. + already, err := gm.rh.Get(gm.rh.ctx, targetid.Hex()).Result() + if err != nil && err != redis.Nil { + return "", err + } + + if len(already) > 0 { + if already != mid.Hex() { + // 이미 초대 중이다. + // inviter한테 알려줘야 한다. + gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "@" + mid.Hex(), + Body: inviteeDoc, + Tag: []string{"InvitationFail"}, + }) } + return "", errAlreadyInvited } - // gid는 미리 만들어 놔야함. - // 초대하는 클라이언트가 아직 group을 소유하지 않고 있을 수 있다. - // mid의 rconn이 이 호스트에 없더라도 gid는 이 request를 보낸 클라이언트가 받아야 하기 떄문 - if gid.IsZero() { - gid = primitive.NewObjectID() + gd, err := gm.find(gid) + if err != nil { + return "", err } - rconn := gm.hasConn(mid) - if rconn == nil { - // mid가 있는 곳에서 처리를 해야 접속 끊겼을 때 콜백을 먼저 등록할 수 있다. - // 콜백이 rconn에 먼저 등록되지 않으면 좀비 group이 생길 가능성이 생긴다. - return gid.Hex(), rpc.Make(gm).To(targetid).Call(gid, mid, inviteeDoc, inviterDoc) - } - - // 이제 여기는 mid가 InCharge이면서 rconn이 존재 - gd := gm.groups.find(gid) if gd == nil { - _, gd = gm.groups.createWithID(gid, bson.M{}) - tid, newdoc := gd.addInCharge(mid, rconn, inviterDoc) - rconn.registOnCloseFunc("member_remove", func() { - // 내가 InCharge이므로 접속이 종료될 때 그룹을 해체한다. - gm.groupDocSync(gid, nil) - }) - bt, err := bson.Marshal(gd.Body) + gd, err = gm.createGroup(gid, mid, inviterDoc) if err != nil { return "", err } - gm.groupDocSync(gid, bt) - gm.memberSync(gid, mid, tid, newdoc, true) // 내가 wshandler room에 입장 gm.sendEnterRoomMessage(gid, mid) - } else { - // targetid가 이미 멤버인지 미리 확인 가능 - if !gd.ticket(targetid).IsZero() { - // 이미 멤버네 - return "", errAlreayMember - } } - return gid.Hex(), gm.InviteImplement(gid, mid, inviteeDoc, inviterDoc) + newdoc, err := gd.addInvite(inviteeDoc, time.Duration(gm.InviteExpire+1)*time.Second, gm.MaxMember) + if err != nil { + return "", err + } + + // 초대 중 표시 + gm.rh.SetNX(gm.rh.ctx, targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)).Result() + + // invitee에게 알림 + gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: "@" + targetid.Hex(), + Body: Invitation{ + GroupID: gid, + TicketID: gd.tid(targetid), + Inviter: inviterDoc, + ExpireAtUTC: newdoc.InviteExpire, + }, + Tag: []string{"Invitation"}, + }) + + return gd.strid(), nil } func (gm *groupInMemory) CancelInvitation(gid groupID, tid ticketID) error { return nil } -func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, tid ticketID, member bson.M) (groupID, error) { - gd := gm.groups.find(gid) - if gd == nil { - return primitive.NilObjectID, errGroupNotExist +var errInvitationExpired = errors.New("invitation is already expired") + +func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error { + cnt, err := gm.rh.Del(gm.rh.ctx, mid.Hex()).Result() + if err != nil { + return err + } + if cnt == 0 { + // 만료됨 + return errInvitationExpired } - rconn := gm.hasConn(mid) - if rconn == nil { - return gid, rpc.Make(gm).To(mid).Call(gid, mid, tid, member) + gd := groupDoc{ + id: gid, + rh: gm.rh, } - oldFunc := rconn.unregistOnCloseFunc("member_remove") - if oldFunc != nil { - // 기존 멤버였으면 탈퇴 처리 - oldFunc() - } - - inviteFunc := rconn.unregistOnCloseFunc("member_remove_invite") - rconn.registOnCloseFunc("member_remove", inviteFunc) - - result, isNew := gd.addMember(mid, &tid, member) - if result != nil { + _, err = gd.addMember(mid, member) + if err == nil { gm.sendEnterRoomMessage(gid, mid) - return gid, gm.memberSync(gid, mid, tid, result, isNew) } // 실패 - return primitive.NilObjectID, nil + return err } func (gm *groupInMemory) DenyInvitation(gid groupID, mid accountID, tid ticketID) error { - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist + gm.rh.Del(gm.rh.ctx, mid.Hex()).Result() + gd := groupDoc{ + id: gid, + rh: gm.rh, } - - rconn := gm.hasConn(mid) - if rconn == nil { - return rpc.Make(gm).To(mid).Call(gid, mid, tid) - } - - inviteFunc := rconn.unregistOnCloseFunc("member_remove_invite") - if inviteFunc != nil { - inviteFunc() // removeMember는 여기에 들어있다. - return nil - } - - gd.removeMember(mid, &tid) - return gm.memberSync(gid, mid, tid, nil, false) + return gd.removeMember(mid) } func (gm *groupInMemory) QueryInvitations(mid accountID, after primitive.Timestamp) ([]bson.M, error) { @@ -708,168 +342,42 @@ func (gm *groupInMemory) FindAll(filter bson.M, projection string, after primiti func (gm *groupInMemory) FindOne(gid groupID, projection string) (bson.M, error) { return nil, nil } - -func (gm *groupInMemory) DropPausedMember(gid primitive.ObjectID, mid primitive.ObjectID) error { - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist +func (gm *groupInMemory) Leave(gid groupID, mid accountID) error { + gd := groupDoc{ + id: gid, + rh: gm.rh, } - - tid, memdoc := gd.memberByAccount(mid) - if memdoc == nil { - return errNotMember - } - - if _, ok := memdoc.Body["paused"]; ok { - // 드랍해야 한다. - if gd.InCharge == mid { - // 내가 방장인 경우 - return gm.groupDocSync(gid, nil) - } else { - // 내가 방장이 아닌 경우 - gd.removeMember(mid, &tid) - return gm.memberSync(gid, mid, tid, nil, false) - } - } - - return nil -} - -func (gm *groupInMemory) PauseMember(gid primitive.ObjectID, mid primitive.ObjectID) error { - rconn := gm.hasConn(mid) - if rconn == nil { - return rpc.Make(gm).To(mid).Call(gid, mid) - } - - // 접속은 끊기지만 그룹에서 제거하지는 않는 상태 - rconn.unregistOnCloseFunc("member_remove") - rconn.unregistOnCloseFunc("member_remove_invite") - gm.sendLeaveRoomMessage(gid, mid) - - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist - } - - tid := primitive.NilObjectID - newdoc := gd.modifyMemberDocument(mid, &tid, func(memdoc *memberDoc) { - memdoc.Body["paused"] = true - memdoc.rconn = nil - }) - - return gm.memberSync(gid, mid, tid, newdoc, false) -} - -func (gm *groupInMemory) QueryMembers(gid groupID, reqID accountID, projection string, after primitive.Timestamp) (map[string]bson.M, error) { - gd := gm.groups.find(gid) - if gd == nil { - return nil, errGroupNotExist - } - - if gd.InCharge != reqID { - return nil, errGroupNotExist - } - - outdocs := make(map[string]bson.M) - if len(projection) > 0 { - projkeys := map[string]bool{} - for _, p := range strings.Split(projection, ",") { - if p[0] == '+' { - projkeys[strings.TrimSpace(p[1:])] = true - } else { - projkeys[strings.TrimSpace(p)] = true - } - } - - gd.iterateMembers(func(tid ticketID, memdoc *memberDoc) { - outdoc := bson.M{} - for k := range projkeys { - if k[0] == '_' { - outdoc[k] = memdoc.Hidden[k] - } else { - outdoc[k] = memdoc.Body[k] - } - } - outdocs[memdoc.Mid.Hex()] = outdoc - }) - } else { - gd.iterateMembers(func(tid ticketID, memdoc *memberDoc) { - outdoc := bson.M{} - for k, v := range memdoc.Hidden { - outdoc[k] = v - } - for k, v := range memdoc.Body { - outdoc[k] = v - } - outdocs[memdoc.Mid.Hex()] = outdoc - }) - } - - return outdocs, nil -} - -func (gm *groupInMemory) QueryMember(gid groupID, mid accountID, tid ticketID, projection string) (bson.M, error) { - return nil, nil -} - -var errHaveNoAuthority = errors.New("cannot kick other member") -var errNotMember = errors.New("ticket is not in this group") - -func (gm *groupInMemory) Leave(gid groupID, mid accountID, tid ticketID) error { - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist - } - - // mid가 InCharge인 경우에는 tid가 누구든 내쫓고, - // mid가 InCharge가 아닌 경우는 tid가 mid일 경우에만 나갈 수 있다. - memdoc := gd.member(tid) - if memdoc == nil { - return errNotMember - } - targetmid := memdoc.Mid - - // 내가 방장이면 아무나 내보낼 수 있다. - if gd.InCharge != mid && targetmid != mid { - // targetmid와 mid가 같아야 한다. 방장이 아니므로 나는 나만 내보낼 수 있다. - return errHaveNoAuthority - } - - // targetmid의 "member_remove" 함수를 등록 해제해야 하므로 rconn이 있는 곳에서 하자 - rconn := gm.hasConn(targetmid) - if rconn == nil { - return rpc.Make(gm).To(targetmid).Call(gid, mid, tid) - } - - if oldfunc := rconn.unregistOnCloseFunc("member_remove"); oldfunc != nil { - oldfunc() // 이 안에 다 있다. + if err := gd.removeMember(mid); err != nil { + return err } // 나한테는 빈 FullGroupDoc을 보낸다. gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ Target: "@" + mid.Hex(), - Body: FullGroupDoc{Gid: gid}, + Body: bson.M{"gid": gid}, Tag: []string{"FullGroupDoc", gid.Hex()}, }) - gm.sendLeaveRoomMessage(gid, targetmid) + gm.sendLeaveRoomMessage(gid, mid) return nil } func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bson.M) error { - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist + gd := groupDoc{ + id: gid, + rh: gm.rh, + } + _, err := gm.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid)+".body", doc, rjs.SetOptionXX) + if err != nil { + return err } - tid := gd.ticket(mid) - bt, _ := json.Marshal(doc) - - personalized := []byte(fmt.Sprintf(`{"%s":%s}`, tid.Hex(), string(bt))) gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ Target: "#" + gid.Hex(), - Body: gd.updateBodyWithJson(personalized), - Tag: []string{"GroupDocBody"}, + Body: map[string]any{ + gd.tid(mid): doc, + }, + Tag: []string{"GroupDocBody"}, }) return nil @@ -878,89 +386,31 @@ func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bs func (gm *groupInMemory) Dismiss(gid groupID) error { return nil } + func (gm *groupInMemory) UpdateGroupDocument(gid groupID, body []byte) error { - gd := gm.groups.find(gid) - if gd == nil { - return errGroupNotExist + gd := groupDoc{ + id: gid, + rh: gm.rh, } - - newbody, err := gd.updateBodyWithBson(body) - if err != nil { - return err - } - - return gm.groupDocSync(gid, newbody) + _, err := gm.rh.JSONSet(gd.strid(), "$.members.body", body, rjs.SetOptionXX) + return err } -func (gm *groupInMemory) TargetExists(target primitive.ObjectID) bool { - return gm.hasConn(target) != nil -} - -var devflag = flagx.Bool("dev", false, "") - func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, sub *subTavern) (group, error) { // group document // member document region := sub.region wsh := sub.wsh - - groupDocSyncChanName := fmt.Sprintf("d_mgc_%s_%s", region, typename) - memberSyncChanName := fmt.Sprintf("m_mgc_%s_%s", region, typename) - rpcChanName := fmt.Sprintf("r_mgc_%s_%s", region, typename) - - toHashHex := func(name string) string { - hash := md5.New() - hash.Write([]byte(name)) - if *devflag { - hn, _ := os.Hostname() - hash.Write([]byte(hn)) - } - - return hex.EncodeToString(hash.Sum(nil)[:8]) + storage := config.RegionStorage[sub.region] + redisClient, err := gocommon.NewRedisClient(storage.Redis["tavern"]) + if err != nil { + return nil, err } - - groupDocSyncChanName = toHashHex(groupDocSyncChanName) - memberSyncChanName = toHashHex(memberSyncChanName) - rpcChanName = toHashHex(rpcChanName) - // 여기서는 subscribe channel // 각 함수에서는 publish gm := &groupInMemory{ groupConfig: cfg, - groupDocSync: func(gid groupID, newbody []byte) error { - bt := []byte(fmt.Sprintf("%s%s", config.macAddr, gid.Hex())) - bt = append(bt, newbody...) - _, err := wsh.RedisSync.Publish(ctx, groupDocSyncChanName, bt).Result() - return err - }, - memberSync: func(gid groupID, mid accountID, tid ticketID, doc *memberDoc, newmember bool) error { - var payload string - if doc != nil { - bt, _ := json.Marshal(doc) - newmemberflag := func() string { - if newmember { - return "t" - } else { - return "f" - } - }() - payload = fmt.Sprintf("%s%s%s%s%s%s", config.macAddr, gid.Hex(), mid.Hex(), tid.Hex(), newmemberflag, string(bt)) - } else { - payload = fmt.Sprintf("%s%s%s%s", config.macAddr, gid.Hex(), mid.Hex(), tid.Hex()) - } - _, err := wsh.RedisSync.Publish(ctx, memberSyncChanName, payload).Result() - return err - }, - rpcCall: func(bt []byte) error { - _, err := wsh.RedisSync.Publish(ctx, rpcChanName, bt).Result() - return err - }, - hasConn: func(t accountID) *connection { - return sub.cm.get(t) - }, - groups: groupContainer{ - groupDocs: make(map[groupID]*groupDoc), - }, + rh: NewReJSONHandler(ctx, redisClient), sendUpstreamMessage: func(msg *wshandler.UpstreamMessage) { wsh.SendUpstreamMessage(region, msg) }, @@ -972,175 +422,5 @@ func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, su }, } - rpc.RegistReceiver(gm) - - processChannelMessage := func(gm *groupInMemory, pubsub *redis.PubSub) *redis.PubSub { - defer func() { - r := recover() - if r != nil { - logger.Error(r) - } - }() - - for msg := range pubsub.Channel() { - if msg == nil { - pubsub = nil - break - } - - switch msg.Channel { - case groupDocSyncChanName: // 호스트들간 그룹 정보 동기화 채널 - payload := []byte(msg.Payload) - if len(payload) < len(config.macAddr) { - break - } - - senderHost, remain := payload[:len(config.macAddr)], payload[len(config.macAddr):] - if len(remain) < 24 { - break - } - - idstr, remain := remain[:24], remain[24:] - gid, _ := primitive.ObjectIDFromHex(string(idstr)) - gd := gm.groups.find(gid) - if gd != nil { - if len(remain) == 0 { - // gid 그룹 삭제 - // 그룹 안에 있는 멤버에게 알림 - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gid.Hex(), - Body: FullGroupDoc{Gid: gid}, - Tag: []string{"FullGroupDoc"}, - }) - gm.groups.delete(gid) - } else if string(senderHost) != config.macAddr { - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gid.Hex(), - Body: gd.updateBodyBsonToJson(remain), - Tag: []string{"GroupDocBody"}, - }) - } - } else if string(senderHost) != config.macAddr { - var newDoc groupDoc - if err := newDoc.updateBody(remain); err != nil { - logger.Error("groupDocSyncChanName message decode failed :", remain, err) - } else { - gm.groups.add(gid, &newDoc) - } - } - - case memberSyncChanName: // 호스트들간 멤버 정보 동기화 채널 - if len(msg.Payload) < len(config.macAddr) { - break - } - - senderHost, remain := msg.Payload[:len(config.macAddr)], msg.Payload[len(config.macAddr):] - if len(remain) < 24 { - break - } - - idstr, remain := remain[:24], remain[24:] - gid, _ := primitive.ObjectIDFromHex(idstr) - gd := gm.groups.find(gid) - if gd == nil { - // 미리 그룹을 없애고 싱크 메시지를 보낸후 받은 것일 수 있다. - break - } - - idstr, remain = remain[:24], remain[24:] - mid, _ := primitive.ObjectIDFromHex(idstr) - idstr, remain = remain[:24], remain[24:] - tid, _ := primitive.ObjectIDFromHex(idstr) - - isNewMember := false - if len(remain) > 0 { - idstr, remain = remain[:1], remain[1:] - isNewMember = idstr == "t" - } - - var updated *memberDoc - rconn := gm.hasConn(mid) - - if senderHost != config.macAddr { - // 내가 보낸 메시지가 아니면 멤버 도큐먼트 업데이트 하고 브로드캐스팅 - if len(remain) == 0 { - // mid 삭제 - gd.removeMember(mid, &tid) - updated = nil - } else { - updated = gd.overwriteMemberDocument(mid, &tid, []byte(remain)) - } - } else { - updated = gd.member(tid) - } - - if updated == nil { - // 멤버 삭제 알림 - if rconn != nil { - // gid에 이미 다른 값이 있을 수 있다. - // 정확하게 이 값이면 제거하고, 아니면 넘어간다. - rconn.removeTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name)) - } - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gid.Hex(), - Body: PublicMemberDoc{Tid: tid}, - Tag: []string{"PublicMemberDoc"}, - }) - } else { - if isNewMember && updated.rconn == nil && rconn != nil { - updated.rconn = rconn - } - // 업데이트 된 플레이어(새로 들어온 플레이어 포함)를 모두에게 알려준다. 본인 포함, invitee 제외 - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "#" + gid.Hex(), - Body: PublicMemberDoc{ - Tid: tid, - memberDocCommon: updated.memberDocCommon, - }, - Tag: []string{"PublicMemberDoc"}, - }) - } - - if isNewMember { - if rconn != nil { - // 새 멤버이므로 기존 멤버를 다 보내준다. - rconn.addTag("gid", fmt.Sprintf("%s@%s", gid.Hex(), gm.Name)) - gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: "@" + mid.Hex(), - Body: gd.serializeFull(gid), - Tag: []string{"FullGroupDoc"}, - }) - } - } - - default: - logger.Println("unknown channel") - } - } - return pubsub - } - - go func() { - defer func() { - r := recover() - if r != nil { - logger.Error(r) - } - }() - - var pubsub *redis.PubSub - for { - if pubsub == nil { - pubsub = wsh.RedisSync.Subscribe(ctx, groupDocSyncChanName, memberSyncChanName, rpcChanName) - } - - if pubsub == nil { - time.Sleep(time.Second) - continue - } - pubsub = processChannelMessage(gm, pubsub) - } - }() - return gm, nil } diff --git a/core/rejson_handler.go b/core/rejson_handler.go new file mode 100644 index 0000000..698fc3c --- /dev/null +++ b/core/rejson_handler.go @@ -0,0 +1,210 @@ +package core + +import ( + "context" + "encoding/json" + + "github.com/go-redis/redis/v8" + "github.com/nitishm/go-rejson/v4" + "github.com/nitishm/go-rejson/v4/rjs" +) + +// gocommon으로 옮길 거 +type RejsonHandler struct { + *redis.Client + inner *rejson.Handler + ctx context.Context +} + +func NewReJSONHandler(ctx context.Context, redisClient *redis.Client) *RejsonHandler { + inner := rejson.NewReJSONHandler() + inner.SetGoRedisClientWithContext(ctx, redisClient) + return &RejsonHandler{ + Client: redisClient, + inner: inner, + ctx: ctx, + } +} + +func respToArray[T any](resp any) []T { + resArr := resp.([]any) + v := make([]T, len(resArr)) + for i, e := range resArr { + v[i] = e.(T) + } + return v +} + +func (rh *RejsonHandler) JSONMSet(key string, kv map[string]any) error { + pl := rh.Pipeline() + for path, obj := range kv { + b, err := json.Marshal(obj) + if err != nil { + return err + } + pl.Do(rh.ctx, []any{"JSON.SET"}, key, path, b) + } + + cmders, err := pl.Exec(rh.ctx) + if err != nil { + return err + } + + for _, cmder := range cmders { + if cmder.Err() != nil { + return cmder.Err() + } + } + return nil +} + +func (rh *RejsonHandler) JSONSet(key, path string, obj any, opts ...rjs.SetOption) (res interface{}, err error) { + b, err := json.Marshal(obj) + if err != nil { + return nil, err + } + args := []any{ + []any{"JSON.SET"}, + key, + path, + b, + } + if len(opts) > 0 { + args = append(args, opts[0]) + } + + return rh.Do(rh.ctx, args...).Result() +} + +func (rh *RejsonHandler) JSONGet(key, path string, opts ...rjs.GetOption) (res interface{}, err error) { + return rh.inner.JSONGet(key, path, opts...) +} + +func (rh *RejsonHandler) JSONGetString(key, path string) ([]string, error) { + res, err := rh.inner.JSONResp(key, path) + if err != nil { + return nil, err + } + return respToArray[string](res), nil +} + +func (rh *RejsonHandler) JSONGetInt64(key, path string) ([]int64, error) { + res, err := rh.inner.JSONResp(key, path) + if err != nil { + return nil, err + } + return respToArray[int64](res), nil +} + +func (rh *RejsonHandler) JSONMGet(path string, keys ...string) (res interface{}, err error) { + return rh.inner.JSONMGet(path, keys...) +} + +func (rh *RejsonHandler) JSONMDel(key string, paths []string) error { + pl := rh.Pipeline() + for _, path := range paths { + name, args, err := rjs.CommandBuilder(rjs.ReJSONCommandDEL, key, path) + if err != nil { + return err + } + args = append([]interface{}{name}, args...) + pl.Do(rh.ctx, args...) + } + _, err := pl.Exec(rh.ctx) + return err +} + +func (rh *RejsonHandler) JSONDel(key, path string) (res interface{}, err error) { + return rh.inner.JSONDel(key, path) +} + +func (rh *RejsonHandler) JSONType(key, path string) (res interface{}, err error) { + return rh.inner.JSONType(key, path) +} + +func (rh *RejsonHandler) JSONNumIncrBy(key, path string, number int) (res interface{}, err error) { + return rh.inner.JSONNumIncrBy(key, path, number) +} + +func (rh *RejsonHandler) JSONNumMultBy(key, path string, number int) (res interface{}, err error) { + return rh.inner.JSONNumMultBy(key, path, number) +} + +func (rh *RejsonHandler) JSONStrAppend(key, path string, jsonstring string) (res interface{}, err error) { + return rh.inner.JSONStrAppend(key, path, jsonstring) +} + +func (rh *RejsonHandler) JSONStrLen(key, path string) (res interface{}, err error) { + return rh.inner.JSONStrLen(key, path) +} + +func (rh *RejsonHandler) JSONArrAppend(key, path string, values ...interface{}) (res interface{}, err error) { + return rh.inner.JSONArrAppend(key, path, values...) +} + +func (rh *RejsonHandler) JSONArrLen(key, path string) (res interface{}, err error) { + return rh.inner.JSONArrLen(key, path) +} + +func (rh *RejsonHandler) JSONArrPop(key, path string, index int) (res interface{}, err error) { + return rh.inner.JSONArrPop(key, path, index) +} + +func (rh *RejsonHandler) JSONArrIndex(key, path string, jsonValue interface{}, optionalRange ...int) (res interface{}, err error) { + return rh.inner.JSONArrIndex(key, path, jsonValue, optionalRange...) +} + +func (rh *RejsonHandler) JSONArrTrim(key, path string, start, end int) (res interface{}, err error) { + return rh.inner.JSONArrTrim(key, path, start, end) +} + +func (rh *RejsonHandler) JSONArrInsert(key, path string, index int, values ...interface{}) (res interface{}, err error) { + return rh.inner.JSONArrInsert(key, path, index, values...) +} + +func (rh *RejsonHandler) JSONObjKeys(key, path string) ([]string, error) { + name, args, err := rjs.CommandBuilder(rjs.ReJSONCommandOBJKEYS, key, path) + if err != nil { + return nil, err + } + + args = append([]interface{}{name}, args...) + res, err := rh.Do(rh.ctx, args...).Result() + if err != nil { + return nil, err + } + + resArr := res.([]any) + resArr = resArr[0].([]any) + slc := make([]string, len(resArr)) + + for i, r := range resArr { + slc[i] = r.(string) + } + + return slc, nil +} + +func (rh *RejsonHandler) JSONObjLen(key, path string) (res interface{}, err error) { + return rh.inner.JSONObjLen(key, path) +} + +func (rh *RejsonHandler) JSONObjLenInt64(key, path string) (int64, error) { + res, err := rh.inner.JSONObjLen(key, path) + if err != nil { + return 0, err + } + return res.([]any)[0].(int64), nil +} + +func (rh *RejsonHandler) JSONDebug(subCmd rjs.DebugSubCommand, key, path string) (res interface{}, err error) { + return rh.inner.JSONDebug(subCmd, key, path) +} + +func (rh *RejsonHandler) JSONForget(key, path string) (res interface{}, err error) { + return rh.inner.JSONForget(key, path) +} + +func (rh *RejsonHandler) JSONResp(key, path string) (res interface{}, err error) { + return rh.inner.JSONResp(key, path) +} diff --git a/core/richconn.go b/core/richconn.go deleted file mode 100644 index 218b5e5..0000000 --- a/core/richconn.go +++ /dev/null @@ -1,97 +0,0 @@ -package core - -import ( - "fmt" - "strings" - "sync" -) - -type connection struct { - locker sync.Mutex - alias string - tags []string - onClose map[string]func() -} - -func (rc *connection) addTag(name, val string) { - rc.locker.Lock() - defer rc.locker.Unlock() - - prefix := name + "=" - for i, tag := range rc.tags { - if strings.HasPrefix(tag, prefix) { - rc.tags[i] = prefix + val - return - } - } - rc.tags = append(rc.tags, prefix+val) -} - -func (rc *connection) removeTag(name string, val string) { - rc.locker.Lock() - defer rc.locker.Unlock() - - whole := fmt.Sprintf("%s=%s", name, val) - for i, tag := range rc.tags { - if tag == whole { - if i == 0 && len(rc.tags) == 1 { - rc.tags = nil - } else { - lastidx := len(rc.tags) - 1 - if i < lastidx { - rc.tags[i] = rc.tags[lastidx] - } - rc.tags = rc.tags[:lastidx] - } - return - } - } -} - -func (rc *connection) registOnCloseFunc(name string, f func()) { - rc.locker.Lock() - defer rc.locker.Unlock() - - if rc.onClose == nil { - f() - return - } - rc.onClose[name] = f -} - -func (rc *connection) hasOnCloseFunc(name string) bool { - rc.locker.Lock() - defer rc.locker.Unlock() - - if rc.onClose == nil { - return false - } - - _, ok := rc.onClose[name] - return ok -} - -func (rc *connection) unregistOnCloseFunc(name string) (out func()) { - rc.locker.Lock() - defer rc.locker.Unlock() - - if rc.onClose == nil { - return - } - out = rc.onClose[name] - delete(rc.onClose, name) - return -} - -func (rc *connection) cleanup() { - rc.locker.Lock() - defer rc.locker.Unlock() - - cp := rc.onClose - rc.onClose = nil - go func() { - for _, f := range cp { - f() - } - }() -} diff --git a/core/tavern.go b/core/tavern.go index d415722..47b70aa 100644 --- a/core/tavern.go +++ b/core/tavern.go @@ -9,7 +9,6 @@ import ( "net/http" "reflect" "strings" - "sync" "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" @@ -30,10 +29,6 @@ func writeBsonArr(w io.Writer, src []bson.M) error { }) } -func onlineGroupQueryKey(prefix string) string { - return prefix + "_olg" -} - func writeBsonDoc[T any](w io.Writer, src T) error { rw, err := bsonrw.NewBSONValueWriter(w) if err != nil { @@ -82,43 +77,6 @@ type TavernConfig struct { var config TavernConfig -type connectionMap struct { - sync.Mutex - conns map[primitive.ObjectID]*connection -} - -func (cm *connectionMap) add(accid accountID, alias string) { - cm.Lock() - defer cm.Unlock() - - old := cm.conns[accid] - if old != nil { - old.cleanup() - } - cm.conns[accid] = &connection{ - alias: alias, - onClose: make(map[string]func()), - } -} - -func (cm *connectionMap) remove(accid accountID) { - cm.Lock() - defer cm.Unlock() - - old := cm.conns[accid] - if old != nil { - delete(cm.conns, accid) - old.cleanup() - } -} - -func (cm *connectionMap) get(accid accountID) *connection { - cm.Lock() - defer cm.Unlock() - - return cm.conns[accid] -} - type Tavern struct { subTaverns []*subTavern wsh *wshandler.WebsocketHandler @@ -130,7 +88,6 @@ type subTavern struct { region string groups map[string]group methods map[string]reflect.Method - cm connectionMap } func getMacAddr() (string, error) { @@ -183,23 +140,6 @@ func (tv *Tavern) Cleanup() { } } -// func (tv *Tavern) SocketMessageReceived(accid primitive.ObjectID, alias string, messageType wshandler.WebSocketMessageType, body io.Reader) { -// switch messageType { -// case wshandler.Connected: - -// case wshandler.Disconnected: -// } -// // 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) -// // } -// } - func (tv *Tavern) prepare(ctx context.Context) error { for region := range config.RegionStorage { var dbconn gocommon.MongoClient @@ -219,9 +159,6 @@ func (tv *Tavern) prepare(ctx context.Context) error { mongoClient: dbconn, region: region, methods: methods, - cm: connectionMap{ - conns: make(map[primitive.ObjectID]*connection), - }, } groups := make(map[string]group) @@ -269,9 +206,9 @@ func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageType wshandler.WebSocketMessageType, body io.Reader) { if messageType == wshandler.Connected { - sub.cm.add(sender.Accid, sender.Alias) + } else if messageType == wshandler.Disconnected { - sub.cm.remove(sender.Accid) + } else if messageType == wshandler.BinaryMessage { var msg map[string][]any dec := json.NewDecoder(body) diff --git a/core/tavern_test.go b/core/tavern_test.go new file mode 100644 index 0000000..9cacfbc --- /dev/null +++ b/core/tavern_test.go @@ -0,0 +1,74 @@ +// warroom project main.go +package core + +import ( + "context" + "testing" + + "github.com/go-redis/redis/v8" + "go.mongodb.org/mongo-driver/bson/primitive" + "repositories.action2quare.com/ayo/gocommon/logger" +) + +func TestReJSON(t *testing.T) { + rc := redis.NewClient(&redis.Options{Addr: "192.168.8.94:6380"}) + rh := NewReJSONHandler(context.Background(), rc) + + testDoc := map[string]any{ + "members": map[string]any{ + "mid2": map[string]any{ + "key": "val", + "exp": 20202020, + }, + "mid1": map[string]any{ + "key": "val", + "exp": 10101010, + }, + }, + } + + gd := groupDoc{ + id: primitive.NewObjectID(), + } + + midin := primitive.NewObjectID() + tid := gd.tid(midin) + midout := gd.mid(tid) + logger.Println(midin, tid, midout) + + res, err := rh.JSONSet("jsontest", "$", testDoc) + logger.Println(res, err) + + res, err = rh.JSONResp("jsontest", "$.members") + vars := res.([]any) + logger.Println(vars, err) + + res, err = rh.JSONGetString("jsontest", "$.members..key") + logger.Println(res, err) + + res, err = rh.JSONGetInt64("jsontest", "$.members..exp") + logger.Println(res, err) + + res, err = rh.JSONObjKeys("jsontest", "$.members") + logger.Println(res, err) + + err = rh.JSONMSet("jsontest", map[string]any{ + "$.members.mid1.key": "newval", + "$.members.mid2.key": "newval", + }) + logger.Println(err) + + res, err = rh.JSONGet("jsontest", "$") + logger.Println(string(res.([]byte)), err) + + err = rh.JSONMDel("jsontest", []string{"$.members.mid1", "$.members.mid2"}) + logger.Println(err) + + res, err = rh.JSONGet("jsontest", "$") + logger.Println(string(res.([]byte)), err) + + res, err = rh.JSONObjLen("jsontest", "$.members") + count := res.([]any)[0].(int64) + logger.Println(count, err) + +} diff --git a/go.mod b/go.mod index 5dbe3f9..5c7e213 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.20 require ( github.com/go-redis/redis/v8 v8.11.5 + github.com/nitishm/go-rejson/v4 v4.1.0 go.mongodb.org/mongo-driver v1.11.7 - repositories.action2quare.com/ayo/gocommon v0.0.0-20230713080645-269fa0f8700e + repositories.action2quare.com/ayo/gocommon v0.0.0-20230715080833-f0f459332d1a ) require ( @@ -24,4 +25,5 @@ require ( golang.org/x/crypto v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/text v0.10.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/go.sum b/go.sum index 388ec98..06ed224 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -5,16 +6,33 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-redis/redis/v8 v8.4.4/go.mod h1:nA0bQuF0i5JFx4Ta9RZxGKXFrQ8cRWntra97f0196iY= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/gomodule/redigo v1.8.3 h1:HR0kYDX2RJZvAup8CsiJwxB4dTCSC0AaUq6S4SiLwUc= +github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -26,8 +44,17 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/nitishm/go-rejson/v4 v4.1.0 h1:NckPgP5ct9ZsQp+aueVCXBiFZ7FBUwltBkEAjg98mJY= +github.com/nitishm/go-rejson/v4 v4.1.0/go.mod h1:LG1zga7gFp/GH+0IAbXZ7rM4MJruA8B2dXvmXwV7VZo= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= @@ -36,6 +63,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -54,26 +82,39 @@ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= +go.opentelemetry.io/otel v0.15.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -83,6 +124,7 @@ golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -93,32 +135,26 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230710053024-a842845685ee h1:Aau1j/b9wI4nyvrM7m1Q+2xkcW1Qo7i3q+QBD4Umnzg= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230710053024-a842845685ee/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711003621-3bb985d0b617 h1:91mBIGIyxzcnvOaIdegUuV+i9xs8YTSRcmyRaIytzx8= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711003621-3bb985d0b617/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711005604-a42eb2888e97 h1:ARzXt3HBmiAUDyACfNm5Kvz1JMTn7+ryE03kB8x/km0= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711005604-a42eb2888e97/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711033135-d7b26608df94 h1:VrNj5gBFFN9/roWCxyBCZ2gu5k58eremNHQvQNPrfrU= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711033135-d7b26608df94/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711035757-9fd0dd00cb7d h1:RdxKmMc7kHrTk+SvTYse2IGxmdDhbEDeM0fKAUW+G+w= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711035757-9fd0dd00cb7d/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711053010-4acb81a20d9c h1:SktFqjnc/UOMjJrq/brSw5lQjW1IA+KkB5YgeovusmQ= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711053010-4acb81a20d9c/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711062613-74829b93ac1b h1:04rlgT+zeKSpekyleb8Mfi8kENIoka5DYJLuk65wqxc= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711062613-74829b93ac1b/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711084112-d48d4c0f2189 h1:4ugcv2AlTYjTEtw8ekjCfVzp+xNnNTOHpfWWRbugxvw= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230711084112-d48d4c0f2189/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230713064012-522bd4a597bc h1:ToHccG1AAGFoAVldbJxCv+yBh/GvNyCd74sBp1Hf7YY= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230713064012-522bd4a597bc/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230713080645-269fa0f8700e h1:94hUQRdZYbsYaTqm/cTI0pEBdp8zdfOSEfkxdO9kS9o= -repositories.action2quare.com/ayo/gocommon v0.0.0-20230713080645-269fa0f8700e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230715080833-f0f459332d1a h1:n2FF/GQYtCsi57Lh5m9LyQ2IZQ8pIppscBzhpvugmZg= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230715080833-f0f459332d1a/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= diff --git a/main.go b/main.go index cd81f4c..0b86333 100644 --- a/main.go +++ b/main.go @@ -24,12 +24,11 @@ func main() { panic(err) } - authcache, err := common.NewAuthCollectionGlobal(ctx, config.MaingateApiToken) + wsh, err := wshandler.NewWebsocketHandler() if err != nil { panic(err) } - wsh := wshandler.NewWebsocketHandler(authcache) if tv, err := core.New(ctx, wsh, &config); err != nil { panic(err) } else {