차단 목록 관리 추가하고 connection 분리

This commit is contained in:
2023-09-21 11:50:20 +09:00
parent cb1f28fc38
commit f9cec4cef3
6 changed files with 443 additions and 216 deletions

View File

@ -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,
})
}