384 lines
10 KiB
Go
384 lines
10 KiB
Go
|
|
package core
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"encoding/gob"
|
||
|
|
"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"
|
||
|
|
)
|
||
|
|
|
||
|
|
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
|
||
|
|
wsh *wshandler.WebsocketHandler
|
||
|
|
f *friends
|
||
|
|
}
|
||
|
|
|
||
|
|
type invitationDoc struct {
|
||
|
|
Id primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
|
||
|
|
From primitive.ObjectID `bson:"from,omitempty" json:"-"`
|
||
|
|
To primitive.ObjectID `bson:"to,omitempty" json:"-"`
|
||
|
|
FromAlias string `bson:"falias,omitempty" json:"from"`
|
||
|
|
ToAlias string `bson:"talias,omitempty" json:"to"`
|
||
|
|
Timestamp int64 `bson:"ts" json:"ts"`
|
||
|
|
Deleted bool `bson:"deleted,omitempty" json:"deleted,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
gob.Register([]invitationDoc{})
|
||
|
|
}
|
||
|
|
|
||
|
|
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}},
|
||
|
|
}); 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}},
|
||
|
|
}); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return &invitation{
|
||
|
|
mongoClient: s.mongoClient,
|
||
|
|
redison: s.redison,
|
||
|
|
wsh: s.wsh,
|
||
|
|
f: f,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) QueryReceivedInvitations(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 len(receives) > 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,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) CancelInvitation(ctx wshandler.ApiCallContext) {
|
||
|
|
// ctx.CallBy.Accid
|
||
|
|
id, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
||
|
|
|
||
|
|
var ivdoc invitationDoc
|
||
|
|
if err := iv.mongoClient.FindOneAs(invitation_collection_name, bson.M{
|
||
|
|
"_id": id,
|
||
|
|
}, &ivdoc); err != nil {
|
||
|
|
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{
|
||
|
|
"$set": bson.M{
|
||
|
|
"falias": "",
|
||
|
|
"deleted": true,
|
||
|
|
"ts": time.Now().UTC().Unix(),
|
||
|
|
},
|
||
|
|
}); err != nil {
|
||
|
|
logger.Println("CancelInvitation failed:", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: ivdoc.To.Hex(),
|
||
|
|
Body: []invitationDoc{ivdoc},
|
||
|
|
Tag: invitation_received_tag,
|
||
|
|
})
|
||
|
|
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: ivdoc.From.Hex(),
|
||
|
|
Body: []invitationDoc{ivdoc},
|
||
|
|
Tag: invitation_sent_tag,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) AcceptInvitation(ctx wshandler.ApiCallContext) {
|
||
|
|
invId, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
||
|
|
|
||
|
|
var ivdoc invitationDoc
|
||
|
|
if err := iv.mongoClient.FindOneAs(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()
|
||
|
|
f1 := friendDoc{
|
||
|
|
From: ivdoc.To, // 수락한 나
|
||
|
|
To: ivdoc.From, // 상대방
|
||
|
|
ToAlias: ivdoc.FromAlias,
|
||
|
|
Timestamp: now,
|
||
|
|
}
|
||
|
|
f2 := friendDoc{
|
||
|
|
From: ivdoc.From, // 상대방
|
||
|
|
To: ivdoc.To, // 나
|
||
|
|
ToAlias: ivdoc.ToAlias,
|
||
|
|
Timestamp: now,
|
||
|
|
}
|
||
|
|
|
||
|
|
// 나한테 상대방을 친구로 만들고
|
||
|
|
if err := iv.f.addFriend(&f1); err == nil {
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: f1.From.Hex(),
|
||
|
|
Body: []friendDoc{f1},
|
||
|
|
Tag: friends_tag,
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
logger.Println("AcceptInvitation failed. addFriend(f1) err :", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 상대방한테 나를 친구로 만듬
|
||
|
|
if err := iv.f.addFriend(&f2); err == nil {
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: f2.From.Hex(),
|
||
|
|
Body: []friendDoc{f2},
|
||
|
|
Tag: friends_tag,
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
logger.Println("AcceptInvitation failed. addFriend(f2) err :", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
iv.mongoClient.Update(invitation_collection_name, bson.M{
|
||
|
|
"_id": invId,
|
||
|
|
}, bson.M{
|
||
|
|
"$set": bson.M{
|
||
|
|
"deleted": true,
|
||
|
|
"ts": now,
|
||
|
|
},
|
||
|
|
}, options.Update().SetUpsert(false))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) DenyInvitation(ctx wshandler.ApiCallContext) {
|
||
|
|
invId, _ := primitive.ObjectIDFromHex(ctx.Arguments[0].(string))
|
||
|
|
|
||
|
|
var ivdoc invitationDoc
|
||
|
|
if err := iv.mongoClient.FindOneAs(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,
|
||
|
|
}, bson.M{
|
||
|
|
"$set": bson.M{
|
||
|
|
"deleted": true,
|
||
|
|
"ts": now,
|
||
|
|
},
|
||
|
|
}, options.Update().SetUpsert(false)); err != nil {
|
||
|
|
logger.Println("DenyInvitation failed. addFriend(f2) err :", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: ivdoc.To.Hex(),
|
||
|
|
Body: []invitationDoc{ivdoc},
|
||
|
|
Tag: invitation_received_tag,
|
||
|
|
})
|
||
|
|
|
||
|
|
iv.wsh.SendUpstreamMessage(&wshandler.UpstreamMessage{
|
||
|
|
Target: ivdoc.From.Hex(),
|
||
|
|
Body: []invitationDoc{ivdoc},
|
||
|
|
Tag: invitation_sent_tag,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) Trim(ctx wshandler.ApiCallContext) {
|
||
|
|
stringsTobjs := func(in []any) (out []primitive.ObjectID) {
|
||
|
|
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) == 1 {
|
||
|
|
iv.mongoClient.Delete(invitation_collection_name, bson.M{"_id": ids[0], "deleted": true})
|
||
|
|
} else {
|
||
|
|
iv.mongoClient.DeleteMany(invitation_collection_name, bson.D{
|
||
|
|
{Key: "_id", Value: bson.M{"$in": ids}},
|
||
|
|
{Key: "deleted", Value: true},
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (iv *invitation) InviteAsFriend(w http.ResponseWriter, r *http.Request) {
|
||
|
|
// 1. mongodb에 추가
|
||
|
|
// 1-1. block이 되어있다면(==이미 도큐먼트가 있다면) 마치 성공인 것처럼 아무것도 안하고 끝
|
||
|
|
// 2. mongodb에 추가가 성공하면 publish
|
||
|
|
var ivdoc invitationDoc
|
||
|
|
|
||
|
|
if err := gocommon.MakeDecoder(r).Decode(&ivdoc); err != nil {
|
||
|
|
logger.Println("IniviteAsFriend failed:", err)
|
||
|
|
w.WriteHeader(http.StatusBadRequest)
|
||
|
|
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))
|
||
|
|
if err != nil {
|
||
|
|
logger.Println("IniviteAsFriend failed:", err)
|
||
|
|
w.WriteHeader(http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if newid != nil {
|
||
|
|
ivdoc.Id = newid.(primitive.ObjectID)
|
||
|
|
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,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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
|
||
|
|
// }
|
||
|
|
|
||
|
|
// 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
|
||
|
|
// }
|
||
|
|
}
|