차단 목록 관리 완료
This commit is contained in:
@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type blockDoc struct {
|
type blockDoc struct {
|
||||||
Id primitive.ObjectID `bson:"_id,omitempty" json:"id"`
|
Id primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
|
||||||
From primitive.ObjectID `bson:"from,omitempty" json:"-"`
|
From primitive.ObjectID `bson:"from,omitempty" json:"-"`
|
||||||
To primitive.ObjectID `bson:"to,omitempty" json:"-"`
|
To primitive.ObjectID `bson:"to,omitempty" json:"-"`
|
||||||
ToAlias string `bson:"talias" json:"to"`
|
ToAlias string `bson:"talias" json:"to"`
|
||||||
@ -53,13 +53,14 @@ func (bl *blocklist) Block(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block.Timestamp = time.Now().UTC().Unix()
|
||||||
_, newid, err := bl.mongoClient.Update(block_collection_name, bson.M{
|
_, newid, err := bl.mongoClient.Update(block_collection_name, bson.M{
|
||||||
"_id": combineObjectID(block.From, block.To),
|
"_id": combineObjectID(block.From, block.To),
|
||||||
}, bson.M{
|
}, bson.M{
|
||||||
"$set": block,
|
"$set": block,
|
||||||
}, options.Update().SetUpsert(true))
|
}, options.Update().SetUpsert(true))
|
||||||
|
|
||||||
if err != nil || newid != block.Id {
|
if err != nil || newid == nil {
|
||||||
// 이미 있다고 봐야지
|
// 이미 있다고 봐야지
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@ -67,7 +68,7 @@ func (bl *blocklist) Block(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
block.Id = newid.(primitive.ObjectID)
|
block.Id = newid.(primitive.ObjectID)
|
||||||
bl.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
bl.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||||
Target: block.To.Hex(),
|
Target: block.From.Hex(),
|
||||||
Body: []blockDoc{block},
|
Body: []blockDoc{block},
|
||||||
Tag: blocks_tag,
|
Tag: blocks_tag,
|
||||||
})
|
})
|
||||||
@ -92,9 +93,8 @@ func (bl *blocklist) Unblock(ctx wshandler.ApiCallContext) {
|
|||||||
|
|
||||||
if updated {
|
if updated {
|
||||||
bl.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
bl.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
||||||
Alias: ctx.CallBy.Alias,
|
Body: []blockDoc{{Id: id, Deleted: true, Timestamp: now}},
|
||||||
Body: []blockDoc{{Id: id, Deleted: true, Timestamp: now}},
|
Tag: blocks_tag,
|
||||||
Tag: blocks_tag,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,3 +122,23 @@ func (bl *blocklist) QueryBlock(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(http.StatusGone)
|
w.WriteHeader(http.StatusGone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bl *blocklist) QueryBlocks(ctx wshandler.ApiCallContext) {
|
||||||
|
queryfrom := int64(ctx.Arguments[0].(float64))
|
||||||
|
|
||||||
|
var myblocks []blockDoc
|
||||||
|
err := bl.mongoClient.FindAllAs(block_collection_name, bson.M{
|
||||||
|
"from": ctx.CallBy.Accid,
|
||||||
|
"ts": bson.M{"$gt": queryfrom},
|
||||||
|
}, &myblocks)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("QueryBlocks failed. FindAllAs err :", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(myblocks) > 0 {
|
||||||
|
bl.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
||||||
|
Body: myblocks,
|
||||||
|
Tag: blocks_tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import "repositories.action2quare.com/ayo/gocommon"
|
import (
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"repositories.action2quare.com/ayo/gocommon"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
block_collection_name = gocommon.CollectionName("block")
|
block_collection_name = gocommon.CollectionName("block")
|
||||||
@ -12,3 +15,11 @@ var friend_state_tag = []string{"social.FriendState"}
|
|||||||
var invitations_tag = []string{"social.Invitations"}
|
var invitations_tag = []string{"social.Invitations"}
|
||||||
var friends_tag = []string{"social.Friends"}
|
var friends_tag = []string{"social.Friends"}
|
||||||
var blocks_tag = []string{"social.Blocks"}
|
var blocks_tag = []string{"social.Blocks"}
|
||||||
|
|
||||||
|
func stringsToObjs(in []any) (out []primitive.ObjectID) {
|
||||||
|
for _, i := range in {
|
||||||
|
p, _ := primitive.ObjectIDFromHex(i.(string))
|
||||||
|
out = append(out, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -299,6 +299,7 @@ func (fs *friends) DeleteFriend(ctx wshandler.ApiCallContext) {
|
|||||||
"ts": now,
|
"ts": now,
|
||||||
},
|
},
|
||||||
}, &yourdoc, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(false)); err == nil {
|
}, &yourdoc, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(false)); err == nil {
|
||||||
|
logger.Println("delete friend your doc :", yourdoc)
|
||||||
fs.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
fs.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||||
Target: fdoc.To.Hex(),
|
Target: fdoc.To.Hex(),
|
||||||
Body: []friendDoc{yourdoc},
|
Body: []friendDoc{yourdoc},
|
||||||
@ -369,23 +370,14 @@ func (fs *friends) QueryFriends(ctx wshandler.ApiCallContext) {
|
|||||||
|
|
||||||
if len(myfriends) > 0 {
|
if len(myfriends) > 0 {
|
||||||
fs.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
fs.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
||||||
Alias: ctx.CallBy.Alias,
|
Body: myfriends,
|
||||||
Body: myfriends,
|
Tag: friends_tag,
|
||||||
Tag: friends_tag,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *friends) Trim(ctx wshandler.ApiCallContext) {
|
func (fs *friends) Trim(ctx wshandler.ApiCallContext) {
|
||||||
stringsTobjs := func(in []any) (out []primitive.ObjectID) {
|
ids := stringsToObjs(ctx.Arguments[1].([]any))
|
||||||
for _, i := range in {
|
|
||||||
p, _ := primitive.ObjectIDFromHex(i.(string))
|
|
||||||
out = append(out, p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ids := stringsTobjs(ctx.Arguments[2].([]any))
|
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
if len(ids) == 1 {
|
if len(ids) == 1 {
|
||||||
fs.mongoClient.Delete(friends_collection_name, bson.M{"_id": ids[0]})
|
fs.mongoClient.Delete(friends_collection_name, bson.M{"_id": ids[0]})
|
||||||
|
|||||||
@ -28,7 +28,8 @@ type invitationDoc struct {
|
|||||||
FromAlias string `bson:"falias,omitempty" json:"from"`
|
FromAlias string `bson:"falias,omitempty" json:"from"`
|
||||||
ToAlias string `bson:"talias,omitempty" json:"to"`
|
ToAlias string `bson:"talias,omitempty" json:"to"`
|
||||||
Timestamp int64 `bson:"ts" json:"ts"`
|
Timestamp int64 `bson:"ts" json:"ts"`
|
||||||
Deleted bool `bson:"deleted,omitempty" json:"deleted,omitempty"`
|
Denied bool `bson:"denied,omitempty" json:"denied,omitempty"`
|
||||||
|
Canceled bool `bson:"canceled,omitempty" json:"canceled,omitempty"`
|
||||||
Blocked bool `bson:"blocked,omitempty" json:"-"` // From은 To에 의해 차단된 상태를 표시
|
Blocked bool `bson:"blocked,omitempty" json:"-"` // From은 To에 의해 차단된 상태를 표시
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ func makeInvitation(ctx context.Context, s *Social, f *friends) (*invitation, er
|
|||||||
|
|
||||||
// 내가 받은거
|
// 내가 받은거
|
||||||
if err := s.mongoClient.MakeIndices(invitation_collection_name, map[string]bson.D{
|
if err := s.mongoClient.MakeIndices(invitation_collection_name, map[string]bson.D{
|
||||||
"tots": {{Key: "to", Value: 1}, {Key: "ts", Value: -1}, {Key: "blocked", Value: 1}},
|
"tots": {{Key: "to", Value: 1}, {Key: "ts", Value: -1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -67,15 +68,17 @@ func (iv *invitation) QueryInvitations(ctx wshandler.ApiCallContext) {
|
|||||||
if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{
|
if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{
|
||||||
"to": ctx.CallBy.Accid,
|
"to": ctx.CallBy.Accid,
|
||||||
"ts": bson.M{"$gt": queryfrom},
|
"ts": bson.M{"$gt": queryfrom},
|
||||||
"blocked": false,
|
"blocked": bson.M{"$exists": false},
|
||||||
|
"denied": bson.M{"$exists": false},
|
||||||
}, &receives, options.Find().SetHint("tots")); err != nil {
|
}, &receives, options.Find().SetHint("tots")); err != nil {
|
||||||
logger.Println("QueryInvitations failed. FindAllAs err :", err)
|
logger.Println("QueryInvitations failed. FindAllAs err :", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sents []*invitationDoc
|
var sents []*invitationDoc
|
||||||
if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{
|
if err := iv.mongoClient.FindAllAs(invitation_collection_name, bson.M{
|
||||||
"from": ctx.CallBy.Accid,
|
"from": ctx.CallBy.Accid,
|
||||||
"ts": bson.M{"$gt": queryfrom},
|
"ts": bson.M{"$gt": queryfrom},
|
||||||
|
"canceled": bson.M{"$exists": false},
|
||||||
}, &sents, options.Find().SetHint("fromts")); err != nil {
|
}, &sents, options.Find().SetHint("fromts")); err != nil {
|
||||||
logger.Println("QueryInvitations failed. FindAllAs err :", err)
|
logger.Println("QueryInvitations failed. FindAllAs err :", err)
|
||||||
}
|
}
|
||||||
@ -92,45 +95,37 @@ func (iv *invitation) QueryInvitations(ctx wshandler.ApiCallContext) {
|
|||||||
|
|
||||||
func (iv *invitation) CancelInvitation(ctx wshandler.ApiCallContext) {
|
func (iv *invitation) CancelInvitation(ctx wshandler.ApiCallContext) {
|
||||||
// ctx.CallBy.Accid
|
// ctx.CallBy.Accid
|
||||||
id, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
invId, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
||||||
|
now := time.Now().UTC().Unix()
|
||||||
|
|
||||||
var ivdoc invitationDoc
|
var ivdoc invitationDoc
|
||||||
if err := iv.mongoClient.FindOneAs(invitation_collection_name, bson.M{
|
if err := iv.mongoClient.FindOneAndUpdateAs(invitation_collection_name, bson.M{
|
||||||
"_id": id,
|
"_id": invId,
|
||||||
}, &ivdoc); err != nil {
|
"from": bson.M{"$eq": ctx.CallBy.Accid},
|
||||||
logger.Println("CancelInvitation failed:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ivdoc.From != ctx.CallBy.Accid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ivdoc.Deleted = true
|
|
||||||
if _, _, err := iv.mongoClient.Update(invitation_collection_name, bson.M{
|
|
||||||
"_id": id,
|
|
||||||
}, bson.M{
|
}, bson.M{
|
||||||
"$set": bson.M{
|
"$set": bson.M{
|
||||||
"falias": "",
|
"canceled": true,
|
||||||
"deleted": true,
|
"ts": now,
|
||||||
"ts": time.Now().UTC().Unix(),
|
|
||||||
},
|
},
|
||||||
}); err != nil {
|
}, &ivdoc, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(false)); err != nil {
|
||||||
logger.Println("CancelInvitation failed:", err)
|
logger.Println("CancelInvitation failed :", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
if ivdoc.Id.IsZero() {
|
||||||
Target: ivdoc.To.Hex(),
|
return
|
||||||
Body: []invitationDoc{ivdoc},
|
}
|
||||||
Tag: invitations_tag,
|
|
||||||
})
|
|
||||||
|
|
||||||
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
if ivdoc.Blocked {
|
||||||
Target: ivdoc.From.Hex(),
|
// 차단된 초대다. 초대한 사람은 차단된 상태를 모르기 때문에 이 초대는 삭제하고, 초대받은 사람한테는 보내지 않는다.
|
||||||
Body: []invitationDoc{ivdoc},
|
iv.mongoClient.Delete(invitation_collection_name, bson.M{"_id": invId})
|
||||||
Tag: invitations_tag,
|
} else {
|
||||||
})
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||||
|
Target: ivdoc.To.Hex(),
|
||||||
|
Body: []invitationDoc{ivdoc},
|
||||||
|
Tag: invitations_tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iv *invitation) AcceptInvitation(ctx wshandler.ApiCallContext) {
|
func (iv *invitation) AcceptInvitation(ctx wshandler.ApiCallContext) {
|
||||||
@ -192,57 +187,31 @@ func (iv *invitation) AcceptInvitation(ctx wshandler.ApiCallContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
iv.mongoClient.Update(invitation_collection_name, bson.M{
|
iv.mongoClient.Delete(invitation_collection_name, bson.M{"_id": invId})
|
||||||
"_id": invId,
|
|
||||||
}, bson.M{
|
|
||||||
"$set": bson.M{
|
|
||||||
"deleted": true,
|
|
||||||
"ts": now,
|
|
||||||
},
|
|
||||||
}, options.Update().SetUpsert(false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iv *invitation) DenyInvitation(ctx wshandler.ApiCallContext) {
|
func (iv *invitation) DenyInvitation(ctx wshandler.ApiCallContext) {
|
||||||
invId, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
invId, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
||||||
|
now := time.Now().UTC().Unix()
|
||||||
|
|
||||||
var ivdoc invitationDoc
|
var ivdoc invitationDoc
|
||||||
if err := iv.mongoClient.FindOneAs(invitation_collection_name, bson.M{
|
if err := iv.mongoClient.FindOneAndUpdateAs(invitation_collection_name, bson.M{
|
||||||
"_id": invId,
|
|
||||||
}, &ivdoc); err != nil {
|
|
||||||
logger.Println("AcceptInvitation failed:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ivdoc.Id != invId {
|
|
||||||
// 초대가 없다
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ivdoc.To != ctx.CallBy.Accid {
|
|
||||||
// 내가 받은 초대가 아니네?
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now().UTC().Unix()
|
|
||||||
ivdoc.Timestamp = now
|
|
||||||
ivdoc.Deleted = true
|
|
||||||
if _, _, err := iv.mongoClient.Update(invitation_collection_name, bson.M{
|
|
||||||
"_id": invId,
|
"_id": invId,
|
||||||
|
"to": bson.M{"$eq": ctx.CallBy.Accid},
|
||||||
}, bson.M{
|
}, bson.M{
|
||||||
"$set": bson.M{
|
"$set": bson.M{
|
||||||
"deleted": true,
|
"denied": true,
|
||||||
"ts": now,
|
"ts": now,
|
||||||
},
|
},
|
||||||
}, options.Update().SetUpsert(false)); err != nil {
|
}, &ivdoc, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(false)); err != nil {
|
||||||
logger.Println("DenyInvitation failed. addFriend(f2) err :", err)
|
logger.Println("DenyInvitation failed. addFriend(f2) err :", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
if ivdoc.Id.IsZero() {
|
||||||
Target: ivdoc.To.Hex(),
|
// 없다
|
||||||
Body: []invitationDoc{ivdoc},
|
return
|
||||||
Tag: invitations_tag,
|
}
|
||||||
})
|
|
||||||
|
|
||||||
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||||
Target: ivdoc.From.Hex(),
|
Target: ivdoc.From.Hex(),
|
||||||
@ -252,23 +221,13 @@ func (iv *invitation) DenyInvitation(ctx wshandler.ApiCallContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (iv *invitation) Trim(ctx wshandler.ApiCallContext) {
|
func (iv *invitation) Trim(ctx wshandler.ApiCallContext) {
|
||||||
stringsTobjs := func(in []any) (out []primitive.ObjectID) {
|
ids := stringsToObjs(ctx.Arguments[2].([]any))
|
||||||
for _, i := range in {
|
|
||||||
p, _ := primitive.ObjectIDFromHex(i.(string))
|
|
||||||
out = append(out, p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ids := stringsTobjs(ctx.Arguments[0].([]any))
|
|
||||||
ids = append(ids, stringsTobjs(ctx.Arguments[1].([]any))...)
|
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
if len(ids) == 1 {
|
if len(ids) == 1 {
|
||||||
iv.mongoClient.Delete(invitation_collection_name, bson.M{"_id": ids[0], "deleted": true})
|
iv.mongoClient.Delete(block_collection_name, bson.M{"_id": ids[0]})
|
||||||
} else {
|
} else {
|
||||||
iv.mongoClient.DeleteMany(invitation_collection_name, bson.D{
|
iv.mongoClient.DeleteMany(block_collection_name, bson.D{
|
||||||
{Key: "_id", Value: bson.M{"$in": ids}},
|
{Key: "_id", Value: bson.M{"$in": ids}},
|
||||||
{Key: "deleted", Value: true},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,7 +312,7 @@ func (iv *invitation) Block(w http.ResponseWriter, r *http.Request) {
|
|||||||
// 사실은 삭제가 아니지만 초대 삭제 알림. 나중에 쿼리해도 안나옴
|
// 사실은 삭제가 아니지만 초대 삭제 알림. 나중에 쿼리해도 안나옴
|
||||||
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||||||
Target: block.From.Hex(),
|
Target: block.From.Hex(),
|
||||||
Body: []invitationDoc{{Id: id, Deleted: true, Timestamp: now}},
|
Body: []invitationDoc{{Id: id, Canceled: true, Timestamp: now}},
|
||||||
Tag: invitations_tag,
|
Tag: invitations_tag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -376,6 +335,10 @@ func (iv *invitation) Unblock(ctx wshandler.ApiCallContext) {
|
|||||||
"$set": bson.M{
|
"$set": bson.M{
|
||||||
"ts": now,
|
"ts": now,
|
||||||
},
|
},
|
||||||
|
"$unset": bson.M{
|
||||||
|
"canceled": "",
|
||||||
|
"blocked": "",
|
||||||
|
},
|
||||||
}, &ivdoc, options.FindOneAndUpdate().SetUpsert(false).SetReturnDocument(options.After))
|
}, &ivdoc, options.FindOneAndUpdate().SetUpsert(false).SetReturnDocument(options.After))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Println("Block failed:", err)
|
logger.Println("Block failed:", err)
|
||||||
@ -385,10 +348,10 @@ func (iv *invitation) Unblock(ctx wshandler.ApiCallContext) {
|
|||||||
if !ivdoc.Id.IsZero() {
|
if !ivdoc.Id.IsZero() {
|
||||||
// 받은 초대가 있었다.
|
// 받은 초대가 있었다.
|
||||||
// 나한테 알림
|
// 나한테 알림
|
||||||
|
ivdoc.Canceled = false
|
||||||
iv.f.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
iv.f.conns.writeMessage(ctx.CallBy.Accid, &wshandler.DownstreamMessage{
|
||||||
Alias: ctx.CallBy.Alias,
|
Body: []invitationDoc{ivdoc},
|
||||||
Body: []invitationDoc{ivdoc},
|
Tag: invitations_tag,
|
||||||
Tag: invitations_tag,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user