diff --git a/core/blocklist.go b/core/blocklist.go new file mode 100644 index 0000000..55a3b9c --- /dev/null +++ b/core/blocklist.go @@ -0,0 +1,124 @@ +package core + +import ( + "context" + "net/http" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" + "repositories.action2quare.com/ayo/gocommon" + "repositories.action2quare.com/ayo/gocommon/logger" + "repositories.action2quare.com/ayo/gocommon/wshandler" +) + +type blockDoc struct { + Id primitive.ObjectID `bson:"_id,omitempty" json:"id"` + From primitive.ObjectID `bson:"from,omitempty" json:"-"` + To primitive.ObjectID `bson:"to,omitempty" json:"-"` + ToAlias string `bson:"talias" json:"to"` + Timestamp int64 `bson:"ts" json:"ts"` + Deleted bool `bson:"deleted,omitempty" json:"deleted,omitempty"` +} + +type blocklist struct { + mongoClient gocommon.MongoClient + redison *gocommon.RedisonHandler + wsh *wshandler.WebsocketHandler + conns *connections +} + +func makeBlocklist(ctx context.Context, so *Social, conns *connections) (*blocklist, error) { + if err := so.mongoClient.MakeUniqueIndices(block_collection_name, map[string]bson.D{ + "fromto": {{Key: "from", Value: 1}, {Key: "to", Value: 1}}, + }); err != nil { + return nil, err + } + + return &blocklist{ + mongoClient: so.mongoClient, + redison: so.redison, + wsh: so.wsh, + conns: conns, + }, nil +} + +func (bl *blocklist) Block(w http.ResponseWriter, r *http.Request) { + // 차단목록에 추가 + var block blockDoc + if err := gocommon.MakeDecoder(r).Decode(&block); err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + _, newid, err := bl.mongoClient.Update(block_collection_name, bson.M{ + "_id": combineObjectID(block.From, block.To), + }, bson.M{ + "$set": block, + }, options.Update().SetUpsert(true)) + + if err != nil || newid != block.Id { + // 이미 있다고 봐야지 + w.WriteHeader(http.StatusBadRequest) + return + } + + block.Id = newid.(primitive.ObjectID) + bl.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: block.To.Hex(), + Body: []blockDoc{block}, + Tag: blocks_tag, + }) +} + +func (bl *blocklist) Unblock(ctx wshandler.ApiCallContext) { + id, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string)) + now := time.Now().UTC().Unix() + updated, _, err := bl.mongoClient.Update(block_collection_name, bson.M{ + "_id": id, + }, bson.M{ + "$set": bson.M{ + "deleted": true, + "ts": now, + }, + }, options.Update().SetUpsert(false)) + + if err != nil { + logger.Println("Unblock failed :", err) + return + } + + if updated { + bl.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ + Alias: ctx.CallBy.Alias, + Body: []blockDoc{{Id: id, Deleted: true, Timestamp: now}}, + Tag: blocks_tag, + }) + } +} + +func (bl *blocklist) QueryBlock(w http.ResponseWriter, r *http.Request) { + var block blockDoc + if err := gocommon.MakeDecoder(r).Decode(&block); err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + exists, err := bl.mongoClient.Exists(block_collection_name, bson.M{ + "from": block.From, + "to": block.To, + }) + + if err != nil { + logger.Println("QueryBlock failed :", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if !exists { + w.WriteHeader(http.StatusGone) + } +} diff --git a/core/common.go b/core/common.go new file mode 100644 index 0000000..11f8dbd --- /dev/null +++ b/core/common.go @@ -0,0 +1,14 @@ +package core + +import "repositories.action2quare.com/ayo/gocommon" + +const ( + block_collection_name = gocommon.CollectionName("block") + friends_collection_name = gocommon.CollectionName("friend") + invitation_collection_name = gocommon.CollectionName("invitation") +) + +var friend_state_tag = []string{"social.FriendState"} +var invitations_tag = []string{"social.Invitations"} +var friends_tag = []string{"social.Friends"} +var blocks_tag = []string{"social.Blocks"} diff --git a/core/connection.go b/core/connection.go new file mode 100644 index 0000000..667d314 --- /dev/null +++ b/core/connection.go @@ -0,0 +1,106 @@ +package core + +import ( + "encoding/json" + "sync" + + "github.com/gorilla/websocket" + "go.mongodb.org/mongo-driver/bson/primitive" + "repositories.action2quare.com/ayo/gocommon/wshandler" +) + +type connWithFriends struct { + c *websocket.Conn + friends []*friendDoc + initialized bool +} + +type connections struct { + connLock sync.Mutex + conns map[primitive.ObjectID]*connWithFriends +} + +func (cs *connections) new(accid primitive.ObjectID, conn *websocket.Conn) { + cs.connLock.Lock() + defer cs.connLock.Unlock() + cs.conns[accid] = &connWithFriends{ + c: conn, + initialized: false, + } +} + +func (cs *connections) delete(accid primitive.ObjectID) { + cs.connLock.Lock() + defer cs.connLock.Unlock() + + delete(cs.conns, accid) +} + +func (cs *connections) conn(accid primitive.ObjectID) *websocket.Conn { + cs.connLock.Lock() + defer cs.connLock.Unlock() + if cf, ok := cs.conns[accid]; ok { + return cf.c + } + return nil +} + +func (cs *connections) addFriend(accid primitive.ObjectID, fdoc *friendDoc) bool { + cs.connLock.Lock() + defer cs.connLock.Unlock() + if cf, ok := cs.conns[accid]; ok { + if cf.initialized { + cf.friends = append(cf.friends, fdoc) + return true + } + } + return false +} + +func (cs *connections) initFriends(accid primitive.ObjectID, fdocs []*friendDoc) { + cs.connLock.Lock() + defer cs.connLock.Unlock() + if cf, ok := cs.conns[accid]; ok { + cf.friends = fdocs + cf.initialized = true + } +} + +func (cs *connections) clearFriends(accid primitive.ObjectID) (out []*friendDoc) { + cs.connLock.Lock() + defer cs.connLock.Unlock() + if cf, ok := cs.conns[accid]; ok { + out = cf.friends + cf.friends = nil + cf.initialized = false + } + return +} + +func (cs *connections) writeMessage(acc primitive.ObjectID, src any) { + cs.connLock.Lock() + defer cs.connLock.Unlock() + + conn := cs.conns[acc] + if conn == nil { + return + } + + if bt, err := json.Marshal(src); err == nil { + conn.c.WriteMessage(websocket.TextMessage, bt) + } +} + +func (cs *connections) ClientConnected(conn *websocket.Conn, callby *wshandler.Sender) { + cs.new(callby.Accid, conn) +} + +func (cs *connections) ClientDisconnected(conn *websocket.Conn, callby *wshandler.Sender) { + cs.delete(callby.Accid) +} + +func makeConnections() *connections { + return &connections{ + conns: make(map[primitive.ObjectID]*connWithFriends), + } +} diff --git a/core/friend.go b/core/friend.go index 9610363..023c621 100644 --- a/core/friend.go +++ b/core/friend.go @@ -6,8 +6,8 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "strings" - "sync" "time" "github.com/gorilla/websocket" @@ -20,14 +20,11 @@ import ( ) const ( - friends_collection_name = gocommon.CollectionName("friends") monitoring_center_count = 100 state_online = "online" state_offline = "offline" ) -var friend_state_tag = []string{"social.FriendState"} - type friendDoc struct { Id primitive.ObjectID `bson:"_id,omitempty" json:"_id"` From primitive.ObjectID `bson:"from" json:"-"` @@ -48,80 +45,12 @@ type monitoringCenter struct { publishState func(string, string, string) } -type connWithFriends struct { - c *websocket.Conn - friends []*friendDoc - initialized bool -} - -type connections struct { - connLock sync.Mutex - conns map[primitive.ObjectID]*connWithFriends -} - -func (cs *connections) new(accid primitive.ObjectID, conn *websocket.Conn) { - cs.connLock.Lock() - defer cs.connLock.Unlock() - cs.conns[accid] = &connWithFriends{ - c: conn, - initialized: false, - } -} - -func (cs *connections) delete(accid primitive.ObjectID) { - cs.connLock.Lock() - defer cs.connLock.Unlock() - - delete(cs.conns, accid) -} - -func (cs *connections) conn(accid primitive.ObjectID) *websocket.Conn { - cs.connLock.Lock() - defer cs.connLock.Unlock() - if cf, ok := cs.conns[accid]; ok { - return cf.c - } - return nil -} - -func (cs *connections) addFriend(accid primitive.ObjectID, fdoc *friendDoc) bool { - cs.connLock.Lock() - defer cs.connLock.Unlock() - if cf, ok := cs.conns[accid]; ok { - if cf.initialized { - cf.friends = append(cf.friends, fdoc) - return true - } - } - return false -} - -func (cs *connections) initFriends(accid primitive.ObjectID, fdocs []*friendDoc) { - cs.connLock.Lock() - defer cs.connLock.Unlock() - if cf, ok := cs.conns[accid]; ok { - cf.friends = fdocs - cf.initialized = true - } -} - -func (cs *connections) clearFriends(accid primitive.ObjectID) (out []*friendDoc) { - cs.connLock.Lock() - defer cs.connLock.Unlock() - if cf, ok := cs.conns[accid]; ok { - out = cf.friends - cf.friends = nil - cf.initialized = false - } - return -} - type friends struct { mongoClient gocommon.MongoClient redison *gocommon.RedisonHandler wsh *wshandler.WebsocketHandler moncen []monitoringCenter - conns connections + conns *connections } type listener struct { @@ -145,6 +74,14 @@ func init() { // - listener(objectid) : socket // - listener(objectid) : socket +func combineObjectID(l primitive.ObjectID, r primitive.ObjectID) (out primitive.ObjectID) { + copy(out[0:2], l[2:4]) + copy(out[2:6], l[8:12]) + copy(out[6:8], r[2:4]) + copy(out[8:12], r[8:12]) + return +} + func makeSrcMap(src string, connected bool) *listenerMap { online, _ := json.Marshal(wshandler.DownstreamMessage{ Body: bson.M{ @@ -170,7 +107,7 @@ func makeSrcMap(src string, connected bool) *listenerMap { } } -func makeFriends(ctx context.Context, so *Social) (*friends, error) { +func makeFriends(ctx context.Context, so *Social, conns *connections) (*friends, error) { if err := so.mongoClient.MakeUniqueIndices(friends_collection_name, map[string]bson.D{ "fromto": {{Key: "from", Value: 1}, {Key: "to", Value: 1}}, }); err != nil { @@ -261,15 +198,11 @@ func makeFriends(ctx context.Context, so *Social) (*friends, error) { redison: so.redison, wsh: so.wsh, moncen: moncen, - conns: connections{ - conns: make(map[primitive.ObjectID]*connWithFriends), - }, + conns: conns, }, nil } func (fs *friends) ClientConnected(conn *websocket.Conn, callby *wshandler.Sender) { - fs.conns.new(callby.Accid, conn) - // 내 로그인 상태를 알림 meidx := callby.Accid[11] % monitoring_center_count fs.moncen[meidx].publishState(callby.Accid.Hex(), callby.Alias, state_online) @@ -281,26 +214,13 @@ func (fs *friends) ClientDisconnected(conn *websocket.Conn, callby *wshandler.Se fs.moncen[meidx].publishState(callby.Accid.Hex(), callby.Alias, state_offline) fs.stopMonitoringFriends(callby.Accid) - - fs.conns.delete(callby.Accid) -} - -func (fs *friends) writeMessage(acc primitive.ObjectID, src any) { - c := fs.conns.conn(acc) - if c == nil { - return - } - - if bt, err := json.Marshal(src); err == nil { - c.WriteMessage(websocket.TextMessage, bt) - } } var errAddFriendFailed = errors.New("addFriend failed") func (fs *friends) addFriend(f *friendDoc) error { _, newid, err := fs.mongoClient.Update(friends_collection_name, bson.M{ - "_id": primitive.NewObjectID(), + "_id": combineObjectID(f.From, f.To), }, bson.M{ "$setOnInsert": f, }, options.Update().SetUpsert(true)) @@ -332,21 +252,6 @@ func (fs *friends) addFriend(f *friendDoc) error { return nil } -func (fs *friends) Block(ctx wshandler.ApiCallContext) { - // BlockByMe 에 추가하고 상대의 BlockByYou를 설정한다. - - // var bi struct { - // From primitive.ObjectID - // To primitive.ObjectID - // } - // if err := gocommon.MakeDecoder(r).Decode(&bi); err != nil { - // logger.Println("friends.Block failed :", err) - // w.WriteHeader(http.StatusBadRequest) - // return - // } - // logger.Println("friends.Block :", bi) -} - func (fs *friends) DeleteFriend(ctx wshandler.ApiCallContext) { fid, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string)) @@ -378,7 +283,7 @@ func (fs *friends) DeleteFriend(ctx wshandler.ApiCallContext) { "ts": fdoc.Timestamp, }, }, options.Update().SetUpsert(false)) - fs.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ + fs.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ Body: []friendDoc{fdoc}, Tag: friends_tag, }) @@ -463,7 +368,7 @@ func (fs *friends) QueryFriends(ctx wshandler.ApiCallContext) { } if len(myfriends) > 0 { - fs.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ + fs.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ Alias: ctx.CallBy.Alias, Body: myfriends, Tag: friends_tag, @@ -489,3 +394,59 @@ func (fs *friends) Trim(ctx wshandler.ApiCallContext) { } } } + +func (fs *friends) Block(w http.ResponseWriter, r *http.Request) { + // 친구를 삭제 + var block blockDoc + if err := gocommon.MakeDecoder(r).Decode(&block); err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + id := combineObjectID(block.From, block.To) + now := time.Now().UTC().Unix() + + // 나한테 삭제 + updated, _, err := fs.mongoClient.Update(friends_collection_name, bson.M{ + "_id": id, + }, bson.M{ + "$set": bson.M{ + "deleted": true, + "ts": now, + }, + }, options.Update().SetUpsert(false)) + + if err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if !updated { + // 친구가 아닌 모양. 그냥 넘어가면 끝 + return + } + + fs.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: block.From.Hex(), + Body: []friendDoc{{Id: id, Deleted: true, Timestamp: now}}, + Tag: friends_tag, + }) + + // 상대방한테서 나를 제거 + id = combineObjectID(block.To, block.From) + fs.mongoClient.Update(friends_collection_name, bson.M{ + "_id": id, + }, bson.M{ + "$set": bson.M{ + "deleted": true, + "ts": now, + }, + }, options.Update().SetUpsert(false)) + fs.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: block.To.Hex(), + Body: []friendDoc{{Id: id, Deleted: true, Timestamp: now}}, + Tag: friends_tag, + }) +} diff --git a/core/invitation.go b/core/invitation.go index 2789b7f..ae4b38b 100644 --- a/core/invitation.go +++ b/core/invitation.go @@ -14,14 +14,6 @@ import ( "repositories.action2quare.com/ayo/gocommon/wshandler" ) -const ( - invitation_collection_name = gocommon.CollectionName("invitation") -) - -var invitation_sent_tag = []string{"social.InvitationsSent"} -var invitation_received_tag = []string{"social.InvitationsReceived"} -var friends_tag = []string{"social.Friends"} - type invitation struct { mongoClient gocommon.MongoClient redison *gocommon.RedisonHandler @@ -37,6 +29,7 @@ type invitationDoc struct { ToAlias string `bson:"talias,omitempty" json:"to"` Timestamp int64 `bson:"ts" json:"ts"` Deleted bool `bson:"deleted,omitempty" json:"deleted,omitempty"` + Blocked bool `bson:"blocked,omitempty" json:"-"` // From은 To에 의해 차단된 상태를 표시 } func init() { @@ -45,14 +38,14 @@ func init() { func makeInvitation(ctx context.Context, s *Social, f *friends) (*invitation, error) { if err := s.mongoClient.MakeUniqueIndices(invitation_collection_name, map[string]bson.D{ - "fromto": {{Key: "from", Value: 1}, {Key: "to", Value: 1}}, + "fromts": {{Key: "from", Value: 1}, {Key: "ts", Value: -1}}, }); err != nil { return nil, err } // 내가 받은거 if err := s.mongoClient.MakeIndices(invitation_collection_name, map[string]bson.D{ - "received": {{Key: "to", Value: 1}, {Key: "ts", Value: -1}}, + "tots": {{Key: "to", Value: 1}, {Key: "ts", Value: -1}, {Key: "blocked", Value: 1}}, }); err != nil { return nil, err } @@ -65,49 +58,34 @@ func makeInvitation(ctx context.Context, s *Social, f *friends) (*invitation, er }, nil } -func (iv *invitation) QueryReceivedInvitations(ctx wshandler.ApiCallContext) { +func (iv *invitation) QueryInvitations(ctx wshandler.ApiCallContext) { // 내가 받은 초대 목록 queryfrom := int64(ctx.Arguments[0].(float64)) var receives []*invitationDoc - err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{ - "to": ctx.CallBy.Accid, - "ts": bson.M{"$gt": queryfrom}, - }, &receives) - if err != nil { - logger.Println("QueryReceivedInvitations failed. FindAllAs err :", err) + if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{ + "to": ctx.CallBy.Accid, + "ts": bson.M{"$gt": queryfrom}, + "blocked": false, + }, &receives, options.Find().SetHint("tots")); err != nil { + logger.Println("QueryInvitations failed. FindAllAs err :", err) } - if len(receives) > 0 { + var sents []*invitationDoc + if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{ + "from": ctx.CallBy.Accid, + "ts": bson.M{"$gt": queryfrom}, + }, &sents, options.Find().SetHint("fromts")); err != nil { + logger.Println("QueryInvitations failed. FindAllAs err :", err) + } + + invitations := append(receives, sents...) + if len(invitations) > 0 { iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ctx.CallBy.Accid.Hex(), - Body: receives, - Tag: invitation_received_tag, - }) - } -} - -func (iv *invitation) QuerySentInvitations(ctx wshandler.ApiCallContext) { - // 내가 보낸 초대 목록 - queryfrom := int64(ctx.Arguments[0].(float64)) - - var receives []*invitationDoc - - err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{ - "from": ctx.CallBy.Accid, - "ts": bson.M{"$gt": queryfrom}, - "falias": bson.M{"$exists": true}, - }, &receives) - if err != nil { - logger.Println("QueryReceivedInvitations failed. FindAllAs err :", err) - } - - if len(receives) > 0 { - iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: ctx.CallBy.Accid.Hex(), - Body: receives, - Tag: invitation_sent_tag, + Body: invitations, + Tag: invitations_tag, }) } } @@ -145,13 +123,13 @@ func (iv *invitation) CancelInvitation(ctx wshandler.ApiCallContext) { iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ivdoc.To.Hex(), Body: []invitationDoc{ivdoc}, - Tag: invitation_received_tag, + Tag: invitations_tag, }) iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ivdoc.From.Hex(), Body: []invitationDoc{ivdoc}, - Tag: invitation_sent_tag, + Tag: invitations_tag, }) } @@ -263,13 +241,13 @@ func (iv *invitation) DenyInvitation(ctx wshandler.ApiCallContext) { iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ivdoc.To.Hex(), Body: []invitationDoc{ivdoc}, - Tag: invitation_received_tag, + Tag: invitations_tag, }) iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ivdoc.From.Hex(), Body: []invitationDoc{ivdoc}, - Tag: invitation_sent_tag, + Tag: invitations_tag, }) } @@ -308,76 +286,109 @@ func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) { return } - ivdoc.Timestamp = time.Now().UTC().Unix() - _, newid, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ - "from": ivdoc.From, - "to": ivdoc.To, - }, bson.M{ - "$set": bson.M{ - "ts": ivdoc.Timestamp, - "falias": ivdoc.FromAlias, - "talias": ivdoc.ToAlias, - }, - }, options.Update().SetUpsert(true)) + // ivdoc.To가 invdoc.From을 차단했으면 표시 + exists, err := iv.mongoClient.Exists(block_collection_name, bson.M{"_id": combineObjectID(ivdoc.To, ivdoc.From)}) if err != nil { logger.Println("IniviteAsFriend failed:", err) w.WriteHeader(http.StatusInternalServerError) return } - if newid != nil { - ivdoc.Id = newid.(primitive.ObjectID) + // exists면 차단된 상태 + ivdoc.Blocked = exists + ivdoc.Timestamp = time.Now().UTC().Unix() + _, newid, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ + "_id": combineObjectID(ivdoc.From, ivdoc.To), + }, bson.M{"$setOnInsert": ivdoc}, options.Update().SetUpsert(true)) + if err != nil || newid == nil { + logger.Println("IniviteAsFriend failed:", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + ivdoc.Id = newid.(primitive.ObjectID) + if !ivdoc.Blocked { iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ Target: ivdoc.To.Hex(), Body: []invitationDoc{ivdoc}, - Tag: invitation_received_tag, - }) - } else { - found, _ := iv.mongoClient.FindOne(invitation_collection_name, bson.M{ - "from": ivdoc.From, - "to": ivdoc.To, - }, options.FindOne().SetProjection(bson.M{"_id": 1})) - - ivdoc.Id = found["_id"].(primitive.ObjectID) - } - - if !ivdoc.Id.IsZero() { - iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ - Target: ivdoc.From.Hex(), - Body: []invitationDoc{ivdoc}, - Tag: invitation_sent_tag, + Tag: invitations_tag, }) } + + iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: ivdoc.From.Hex(), + Body: []invitationDoc{ivdoc}, + Tag: invitations_tag, + }) } func (iv *invitation) Block(w http.ResponseWriter, r *http.Request) { - // 초대가 있으면 - // var bi struct { - // From primitive.ObjectID - // To primitive.ObjectID - // FromAlias string - // } - // if err := gocommon.MakeDecoder(r).Decode(&bi); err != nil { - // logger.Println("invitation.Block failed :", err) - // w.WriteHeader(http.StatusBadRequest) - // return - // } + var block blockDoc + if err := gocommon.MakeDecoder(r).Decode(&block); err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusBadRequest) + return + } - // now := time.Now().UTC().Unix() - // // From이 To를 block했으므로 To가 From을 초대하는 것을 방지하려면 둘을 뒤집어서 문서를 만들어 놔야 함 - // // 이미 존재하는 초대일 수도 있다. - // _, _, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ - // "from": bi.To, - // "to": bi.From, - // }, bson.M{ - // "$set": invitationDoc{ - // ToAlias: bi.FromAlias, - // Timestamp: now, - // }, - // }, options.Update().SetUpsert(true)) - // if err != nil { - // logger.Println("Block failed:", err) - // w.WriteHeader(http.StatusInternalServerError) - // return - // } + // 차단한 상대가 나한테 보낸 초대가 있으면 차단 표시 + // block.From이 block.To를 차단 -> block.To가 block.From을 초대한게 있나? + id := combineObjectID(block.To, block.From) + now := time.Now().UTC().Unix() + updated, _, err := iv.mongoClient.Update(invitation_collection_name, bson.M{ + "_id": id, + }, bson.M{ + "$set": bson.M{ + "blocked": true, + "ts": now, + }, + }, options.Update().SetUpsert(false)) + if err != nil { + logger.Println("Block failed:", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if updated { + // 초대가 있었다. + // 사실은 삭제가 아니지만 초대 삭제 알림. 나중에 쿼리해도 안나옴 + iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{ + Target: block.From.Hex(), + Body: []invitationDoc{{Id: id, Deleted: true, Timestamp: now}}, + Tag: invitations_tag, + }) + } +} + +func (iv *invitation) Unblock(ctx wshandler.ApiCallContext) { + // From이 To를 unblock = to가 from을 block했었다. = from이 to한테 보낸 초대가 있을 수 있다. + // invitation key는 to+from이고 block key는 from+to + id, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string)) + + var revertid primitive.ObjectID + copy(revertid[:6], id[6:]) + copy(revertid[6:], id[:6]) + + now := time.Now().UTC().Unix() + var ivdoc invitationDoc + err := iv.mongoClient.FindOneAndUpdateAs(invitation_collection_name, bson.M{ + "_id": revertid, + }, bson.M{ + "$set": bson.M{ + "ts": now, + }, + }, &ivdoc, options.FindOneAndUpdate().SetUpsert(false).SetReturnDocument(options.After)) + if err != nil { + logger.Println("Block failed:", err) + return + } + + if !ivdoc.Id.IsZero() { + // 받은 초대가 있었다. + // 나한테 알림 + iv.f.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{ + Alias: ctx.CallBy.Alias, + Body: []invitationDoc{ivdoc}, + Tag: invitations_tag, + }) + } } diff --git a/core/social.go b/core/social.go index eaba2c7..bb0ae08 100644 --- a/core/social.go +++ b/core/social.go @@ -79,7 +79,18 @@ func (so *Social) prepare(ctx context.Context) error { so.redison = gocommon.NewRedisonHandler(redisClient.Context(), redisClient) - friends, err := makeFriends(ctx, so) + connections := makeConnections() + so.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(connections, "social")) + so.httpApiBorker.AddHandler(gocommon.MakeHttpApiHandler(connections, "social")) + + blocks, err := makeBlocklist(ctx, so, connections) + if err != nil { + return logger.ErrorWithCallStack(err) + } + so.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(blocks, "social")) + so.httpApiBorker.AddHandler(gocommon.MakeHttpApiHandler(blocks, "social")) + + friends, err := makeFriends(ctx, so, connections) if err != nil { return logger.ErrorWithCallStack(err) }