서버간 api 호출 간소화

This commit is contained in:
2023-09-05 17:14:07 +09:00
parent 6410056c87
commit 4a51f7d433
6 changed files with 189 additions and 287 deletions

View File

@ -1,56 +1,9 @@
package core package core
import ( import (
"net/http"
"reflect"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler" "repositories.action2quare.com/ayo/gocommon/wshandler"
) )
var groupTypes map[string]reflect.Type
func groupTypeContainer() map[string]reflect.Type {
if groupTypes == nil {
groupTypes = make(map[string]reflect.Type)
}
return groupTypes
}
type apiFuncType func(http.ResponseWriter, *http.Request)
type apiFuncsContainer struct {
normfuncs map[string]apiFuncType
funcs map[string][]apiFuncType
}
func (afc *apiFuncsContainer) registApiFunction(name string, f apiFuncType) {
afc.funcs[name] = append(afc.funcs[name], f)
}
func (afc *apiFuncsContainer) normalize() {
for k, v := range afc.funcs {
if len(v) == 1 {
afc.normfuncs[k] = v[0]
} else {
afc.normfuncs[k] = func(w http.ResponseWriter, r *http.Request) {
for _, f := range v {
f(w, r)
}
}
}
}
afc.funcs = nil
}
func (afc *apiFuncsContainer) call(fn string, w http.ResponseWriter, r *http.Request) {
f := afc.normfuncs[fn]
if f != nil {
f(w, r)
} else {
logger.Println("api func is missing :", fn)
}
}
type configDocument map[string]any type configDocument map[string]any
type group interface { type group interface {
Initialize(*Tavern, configDocument) error Initialize(*Tavern, configDocument) error

View File

@ -3,24 +3,17 @@ package core
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"reflect"
"strings" "strings"
"time" "time"
"github.com/go-redis/redis/v8" "github.com/go-redis/redis/v8"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler" "repositories.action2quare.com/ayo/gocommon/wshandler"
) )
func init() {
groupTypeContainer()["chat"] = reflect.TypeOf(&groupChat{})
}
type channelID = string type channelID = string
type channelConfig struct { type channelConfig struct {
Capacity int64 `json:"capacity"` Capacity int64 `json:"capacity"`
@ -63,10 +56,6 @@ func (gc *groupChat) Initialize(tv *Tavern, cfg configDocument) error {
gc.rh = tv.redison gc.rh = tv.redison
tv.apiFuncs.registApiFunction("FetchChattingChannels", gc.FetchChattingChannels)
tv.apiFuncs.registApiFunction("BroadcastMessageOnChannel", gc.BroadcastMessageOnChannel)
tv.apiFuncs.registApiFunction("QueryPlayerChattingChannel", gc.QueryPlayerChattingChannel)
tv.apiFuncs.registApiFunction("SendMessageOnChannel", gc.SendMessageOnChannel)
for name, cfg := range gc.chatConfig.Channels { for name, cfg := range gc.chatConfig.Channels {
if cfg.Capacity == 0 { if cfg.Capacity == 0 {
cfg.Capacity = gc.chatConfig.DefaultCapacity cfg.Capacity = gc.chatConfig.DefaultCapacity
@ -223,7 +212,16 @@ func (gc *groupChat) ClientMessageReceived(sender *wshandler.Sender, mt wshandle
} }
func (gc *groupChat) FetchChattingChannels(w http.ResponseWriter, r *http.Request) { func (gc *groupChat) FetchChattingChannels(w http.ResponseWriter, r *http.Request) {
prefix, _ := gocommon.ReadStringFormValue(r.URL.Query(), "prefix") var data struct {
Prefix string `bson:"prefix"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("FetchChattingChannels failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
prefix := data.Prefix
if len(prefix) == 0 { if len(prefix) == 0 {
logger.Println("FetchChattingChannel failed. prefix is missing") logger.Println("FetchChattingChannel failed. prefix is missing")
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -269,8 +267,18 @@ func (gc *groupChat) FetchChattingChannels(w http.ResponseWriter, r *http.Reques
} }
func (gc *groupChat) QueryPlayerChattingChannel(w http.ResponseWriter, r *http.Request) { func (gc *groupChat) QueryPlayerChattingChannel(w http.ResponseWriter, r *http.Request) {
accid, _ := gocommon.ReadStringFormValue(r.URL.Query(), "accid") var data struct {
typename, _ := gocommon.ReadStringFormValue(r.URL.Query(), "typename") Accid string `bson:"accid"`
Typename string `bson:"typename"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("QueryPlayerChattingChannel failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
accid := data.Accid
typename := data.Typename
var fields []string var fields []string
if len(typename) == 0 { if len(typename) == 0 {
@ -298,60 +306,12 @@ func (gc *groupChat) QueryPlayerChattingChannel(w http.ResponseWriter, r *http.R
} }
func (gc *groupChat) SendMessageOnChannel(w http.ResponseWriter, r *http.Request) { func (gc *groupChat) SendMessageOnChannel(w http.ResponseWriter, r *http.Request) {
channel, _ := gocommon.ReadStringFormValue(r.URL.Query(), "channel") var msg wshandler.UpstreamMessage
target, _ := gocommon.ReadStringFormValue(r.URL.Query(), "target") if err := gocommon.ReadBsonDocumentFromBody(r.Body, &msg); err != nil {
tag, _ := gocommon.ReadStringFormValue(r.URL.Query(), "tag") logger.Println("SendMessageOnChannel failed. ReadBsonDocumentFromBody return err :", err)
if len(channel) == 0 || len(target) == 0 || len(tag) == 0 {
logger.Println("SendMessageOnChannel failed. channel or target or tag is empty")
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
var doc map[string]any gc.sendUpstreamMessage(&msg)
bt, err := io.ReadAll(r.Body)
if err != nil {
logger.Println("SendMessageOnChannel failed. io.ReadAll returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if len(bt) > 0 {
if err := bson.Unmarshal(bt, &doc); err != nil {
logger.Println("SendMessageOnChannel failed. decode returns err :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
}
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: fmt.Sprintf("%s@%s", target, channel),
Body: doc,
Tag: []string{tag},
})
}
func (gc *groupChat) BroadcastMessageOnChannel(w http.ResponseWriter, r *http.Request) {
nickname, _ := gocommon.ReadStringFormValue(r.URL.Query(), "nickname")
channel, _ := gocommon.ReadStringFormValue(r.URL.Query(), "channel")
tag, _ := gocommon.ReadStringFormValue(r.URL.Query(), "tag")
text, _ := io.ReadAll(r.Body)
if len(tag) > 0 {
var doc map[string]any
if err := bson.Unmarshal(text, &doc); err == nil {
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + channel,
Body: doc,
Tag: []string{tag},
})
} else {
logger.Println("BroadcastMessageOnChannel failed :", err)
}
} else {
gc.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + channel,
Body: map[string]any{"sender": nickname, "msg": string(text)},
Tag: []string{"TextMessage"},
})
}
} }

View File

@ -2,11 +2,9 @@ package core
import ( import (
"context" "context"
"encoding/gob"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"reflect"
"strings" "strings"
"time" "time"
@ -22,15 +20,6 @@ import (
type accountID = primitive.ObjectID type accountID = primitive.ObjectID
type groupID = primitive.ObjectID type groupID = primitive.ObjectID
func init() {
gob.Register(memberDoc{})
gob.Register(groupDoc{})
gob.Register(Invitation{})
gob.Register(InvitationFail{})
groupTypeContainer()["party"] = reflect.TypeOf(&groupParty{})
}
func makeTid(gid groupID, in accountID) string { func makeTid(gid groupID, in accountID) string {
var out primitive.ObjectID var out primitive.ObjectID
for i := range in { for i := range in {
@ -127,9 +116,9 @@ func (gd *groupDoc) mid(tid string) accountID {
return out return out
} }
func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*memberDoc, error) { func (gd *groupDoc) addInvite(mid accountID, body bson.M, ttl time.Duration, max int) (*memberDoc, error) {
targetmid := inviteeDoc["_mid"].(accountID) targetmid := mid
targetbody := inviteeDoc["body"].(bson.M) targetbody := body
// 초대 가능한 빈 자리가 있나 // 초대 가능한 빈 자리가 있나
tids, err := gd.rh.JSONObjKeys(gd.strid(), "$._members") tids, err := gd.rh.JSONObjKeys(gd.strid(), "$._members")
@ -181,11 +170,11 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
return newdoc, err return newdoc, err
} }
func (gd *groupDoc) addMember(mid accountID, doc bson.M) (bson.M, error) { func (gd *groupDoc) addMember(mid accountID, character bson.M) (bson.M, error) {
tid := gd.tid(mid) tid := gd.tid(mid)
prefix := "$._members." + tid prefix := "$._members." + tid
if _, err := gd.rh.JSONMerge(gd.strid(), prefix+"._body", doc, gocommon.RedisonSetOptionXX); err != nil { if _, err := gd.rh.JSONMerge(gd.strid(), prefix+"._body", character, gocommon.RedisonSetOptionXX); err != nil {
return nil, err return nil, err
} }
@ -273,15 +262,15 @@ func (gp *groupParty) Initialize(tv *Tavern, cfg configDocument) error {
tv.wsh.LeaveRoom(gid.Hex(), accid) tv.wsh.LeaveRoom(gid.Hex(), accid)
} }
tv.apiFuncs.registApiFunction("JoinParty", gp.JoinParty) // tv.apiFuncs.registApiFunction("JoinParty", gp.JoinParty)
tv.apiFuncs.registApiFunction("InviteToParty", gp.InviteToParty) // tv.apiFuncs.registApiFunction("InviteToParty", gp.InviteToParty)
tv.apiFuncs.registApiFunction("AcceptPartyInvitation", gp.AcceptPartyInvitation) // tv.apiFuncs.registApiFunction("AcceptPartyInvitation", gp.AcceptPartyInvitation)
tv.apiFuncs.registApiFunction("DenyPartyInvitation", gp.DenyPartyInvitation) // tv.apiFuncs.registApiFunction("DenyPartyInvitation", gp.DenyPartyInvitation)
tv.apiFuncs.registApiFunction("QueryPartyMemberState", gp.QueryPartyMemberState) // tv.apiFuncs.registApiFunction("QueryPartyMemberState", gp.QueryPartyMemberState)
tv.apiFuncs.registApiFunction("LeaveParty", gp.LeaveParty) // tv.apiFuncs.registApiFunction("LeaveParty", gp.LeaveParty)
tv.apiFuncs.registApiFunction("UpdatePartyMemberDocument", gp.UpdatePartyMemberDocument) // tv.apiFuncs.registApiFunction("UpdatePartyMemberDocument", gp.UpdatePartyMemberDocument)
tv.apiFuncs.registApiFunction("UpdatePartyDocument", gp.UpdatePartyDocument) // tv.apiFuncs.registApiFunction("UpdatePartyDocument", gp.UpdatePartyDocument)
tv.apiFuncs.registApiFunction("QueryPartyMembers", gp.QueryPartyMembers) // tv.apiFuncs.registApiFunction("QueryPartyMembers", gp.QueryPartyMembers)
return nil return nil
} }
@ -296,20 +285,22 @@ func (gp *groupParty) RegisterApiFunctions() {
// - member_id : 참가 멤버의 아이디 // - member_id : 참가 멤버의 아이디
// - body : 멤버의 속성 bson document // - body : 멤버의 속성 bson document
func (gp *groupParty) JoinParty(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) JoinParty(w http.ResponseWriter, r *http.Request) {
doc := bson.M{} var data struct {
if err := readBsonDoc(r.Body, &doc); err != nil { Gid primitive.ObjectID `bson:"gid"`
logger.Error("JoinParty failed. readBsonDoc returns err :", err) Mid primitive.ObjectID `bson:"mid"`
w.WriteHeader(http.StatusBadRequest) Character bson.M `bson:"character"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("JoinParty failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return return
} }
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid")
if !ok { doc := data.Character
logger.Println("JoinParty failed. gid is missing :", r.URL.Query()) gid := data.Gid
w.WriteHeader(http.StatusBadRequest) mid := data.Mid
return
} if gid.IsZero() || mid.IsZero() {
mid, midok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid")
if !midok {
logger.Println("JoinParty failed. mid should be exist") logger.Println("JoinParty failed. mid should be exist")
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -328,7 +319,7 @@ func (gp *groupParty) JoinParty(w http.ResponseWriter, r *http.Request) {
} }
// 내 정보 업데이트할 때에도 사용됨 // 내 정보 업데이트할 때에도 사용됨
if memdoc, err := gd.addMember(mid, doc); err == nil { if memdoc, err := gd.addMember(mid, doc["character"].(map[string]any)); err == nil {
// 기존 유저에게 새 유저 알림 // 기존 유저에게 새 유저 알림
gp.sendUpstreamMessage(&wshandler.UpstreamMessage{ gp.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(), Target: "#" + gid.Hex(),
@ -363,35 +354,23 @@ func (gp *groupParty) JoinParty(w http.ResponseWriter, r *http.Request) {
// - timeout : 초대 유지시간(optional. 없으면 config 기본 값) // - timeout : 초대 유지시간(optional. 없으면 config 기본 값)
// - (body) : 검색시 노출되는 document // - (body) : 검색시 노출되는 document
func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var doc struct {
if !ok { Gid primitive.ObjectID `bson:"gid"`
logger.Println("InviteToParty failed. gid is missing :", r) Mid primitive.ObjectID `bson:"mid"`
w.WriteHeader(http.StatusBadRequest) Targetid primitive.ObjectID `bson:"targetid"`
return Inviter bson.M `bson:"inviter"`
} Invitee bson.M `bson:"invitee"`
mid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid")
if !ok {
logger.Println("InviteToParty failed. mid is missing :", r)
w.WriteHeader(http.StatusBadRequest)
return
} }
var reqdoc struct { if err := gocommon.ReadBsonDocumentFromBody(r.Body, &doc); err != nil {
Inviter bson.M `bson:"inviter"` logger.Println("InviteToParty failed. ReadBsonDocumentFromBody returns err :", err)
Invitee bson.M `bson:"invitee"`
}
if err := readBsonDoc(r.Body, &reqdoc); err != nil {
logger.Println("InviteToParty failed. readBsonDoc returns err :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
targetid, ok := reqdoc.Invitee["_mid"].(accountID) targetid := doc.Targetid
if !ok { gid := doc.Gid
logger.Println("InviteToParty failed. invitee mid is missing :", r) mid := doc.Mid
w.WriteHeader(http.StatusBadRequest)
return
}
// targetid에 초대한 mid가 들어있다. // targetid에 초대한 mid가 들어있다.
success, err := gp.rh.SetNX(context.Background(), "inv."+targetid.Hex(), mid.Hex(), time.Duration(gp.InviteExpire)*time.Second).Result() success, err := gp.rh.SetNX(context.Background(), "inv."+targetid.Hex(), mid.Hex(), time.Duration(gp.InviteExpire)*time.Second).Result()
@ -406,7 +385,7 @@ func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
// inviter한테 알려줘야 한다. // inviter한테 알려줘야 한다.
gp.sendUpstreamMessage(&wshandler.UpstreamMessage{ gp.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: mid.Hex(), Target: mid.Hex(),
Body: reqdoc.Invitee, Body: doc.Invitee,
Tag: []string{"InvitationFail"}, Tag: []string{"InvitationFail"},
}) })
return return
@ -420,7 +399,7 @@ func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
} }
if gd == nil { if gd == nil {
gd, err = gp.createGroup(gid, mid, reqdoc.Inviter) gd, err = gp.createGroup(gid, mid, doc.Inviter)
if err != nil { if err != nil {
logger.Println("InviteToParty failed. gp.createGroup() return err :", err) logger.Println("InviteToParty failed. gp.createGroup() return err :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -436,7 +415,7 @@ func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
}) })
} }
newdoc, err := gd.addInvite(reqdoc.Invitee, time.Duration(gp.InviteExpire+1)*time.Second, gp.MaxMember) newdoc, err := gd.addInvite(targetid, doc.Invitee, time.Duration(gp.InviteExpire+1)*time.Second, gp.MaxMember)
if err != nil { if err != nil {
logger.Println("InviteToParty failed. gp.addInvite() return err :", err) logger.Println("InviteToParty failed. gp.addInvite() return err :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -449,7 +428,7 @@ func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
Body: Invitation{ Body: Invitation{
GroupID: gid, GroupID: gid,
TicketID: gd.tid(targetid), TicketID: gd.tid(targetid),
Inviter: reqdoc.Inviter, Inviter: doc.Inviter,
ExpireAtUTC: newdoc.InviteExpire, ExpireAtUTC: newdoc.InviteExpire,
}, },
Tag: []string{"Invitation"}, Tag: []string{"Invitation"},
@ -459,15 +438,22 @@ func (gp *groupParty) InviteToParty(w http.ResponseWriter, r *http.Request) {
} }
func (gp *groupParty) AcceptPartyInvitation(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) AcceptPartyInvitation(w http.ResponseWriter, r *http.Request) {
gid, _ := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var doc struct {
mid, _ := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid") Gid primitive.ObjectID `bson:"gid"`
Mid primitive.ObjectID `bson:"mid"`
var member bson.M Tid string `bson:"tid"`
if err := readBsonDoc(r.Body, &member); err != nil { Character bson.M `bson:"character"`
logger.Error("AcceptPartyInvitation failed. readBsonDoc returns err :", err) }
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &doc); err != nil {
logger.Println("AcceptPartyInvitation failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
gid := doc.Gid
mid := doc.Mid
member := doc.Character
cnt, err := gp.rh.Del(context.Background(), "inv."+mid.Hex()).Result() cnt, err := gp.rh.Del(context.Background(), "inv."+mid.Hex()).Result()
if err != nil { if err != nil {
logger.Error("AcceptPartyInvitation failed. gp.rh.Del returns err :", err) logger.Error("AcceptPartyInvitation failed. gp.rh.Del returns err :", err)
@ -543,8 +529,19 @@ func (gp *groupParty) AcceptPartyInvitation(w http.ResponseWriter, r *http.Reque
} }
func (gp *groupParty) DenyPartyInvitation(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) DenyPartyInvitation(w http.ResponseWriter, r *http.Request) {
gid, _ := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var data struct {
mid, _ := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid") Gid primitive.ObjectID `bson:"gid"`
Mid primitive.ObjectID `bson:"mid"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("DenyPartyInvitation failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
gid := data.Gid
mid := data.Mid
gp.rh.Del(context.Background(), "inv."+mid.Hex()).Result() gp.rh.Del(context.Background(), "inv."+mid.Hex()).Result()
gd := groupDoc{ gd := groupDoc{
@ -555,14 +552,18 @@ func (gp *groupParty) DenyPartyInvitation(w http.ResponseWriter, r *http.Request
} }
func (gp *groupParty) QueryPartyMemberState(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) QueryPartyMemberState(w http.ResponseWriter, r *http.Request) {
mid, ok := gocommon.ReadStringFormValue(r.URL.Query(), "mid") var data struct {
if !ok { Mid primitive.ObjectID `bson:"mid"`
logger.Println("IsOnline failed. mid is missing :", r.URL.Query())
w.WriteHeader(http.StatusBadRequest)
return
} }
states, err := gp.rh.JSONGetString(mid, "$.party.state") if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("DenyPartyInvitation failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
mid := data.Mid
states, err := gp.rh.JSONGetString(mid.Hex(), "$.party.state")
if err == redis.Nil { if err == redis.Nil {
return return
} }
@ -579,26 +580,28 @@ func (gp *groupParty) QueryPartyMemberState(w http.ResponseWriter, r *http.Reque
// - 그룹 타입에 맞는 키(주로 _id) // - 그룹 타입에 맞는 키(주로 _id)
// - member_id : 나갈 멤버의 아이디 // - member_id : 나갈 멤버의 아이디
func (gp *groupParty) LeaveParty(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) LeaveParty(w http.ResponseWriter, r *http.Request) {
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var data struct {
if !ok { Gid primitive.ObjectID `bson:"gid"`
logger.Println("LeaveParty failed. gid is missing :", r.URL.Query()) Mid primitive.ObjectID `bson:"mid"`
w.WriteHeader(http.StatusBadRequest) Tid string `bson:"tid"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("LeaveParty failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return return
} }
mid, midok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid")
if !midok { gid := data.Gid
logger.Println("LeaveParty failed. mid is missing") mid := data.Mid
w.WriteHeader(http.StatusBadRequest) tid := data.Tid
return
}
tid, tidok := gocommon.ReadStringFormValue(r.URL.Query(), "tid")
gd := groupDoc{ gd := groupDoc{
id: gid, id: gid,
rh: gp.rh, rh: gp.rh,
} }
var err error var err error
if tidok { if len(tid) > 0 {
if tid != gd.tid(mid) { if tid != gd.tid(mid) {
// mid가 incharge여야 한다. 그래야 tid를 쫓아낼 수 있음 // mid가 incharge여야 한다. 그래야 tid를 쫓아낼 수 있음
incharge, err := gp.rh.JSONGet(gd.strid(), "$._incharge") incharge, err := gp.rh.JSONGet(gd.strid(), "$._incharge")
@ -674,26 +677,21 @@ func (gp *groupParty) updateMemberDocument(gid groupID, mid accountID, doc bson.
} }
func (gp *groupParty) UpdatePartyMemberDocument(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) UpdatePartyMemberDocument(w http.ResponseWriter, r *http.Request) {
mid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "mid") var data struct {
if !ok { Gid primitive.ObjectID `bson:"gid"`
logger.Println("UpdatePartyMemberDocument failed. member_id is missing") Mid primitive.ObjectID `bson:"mid"`
w.WriteHeader(http.StatusBadRequest) Doc bson.M `bson:"doc"`
}
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("UpdatePartyMemberDocument failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return return
} }
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") mid := data.Mid
if !ok { gid := data.Gid
logger.Println("UpdatePartyMemberDocument failed. _id is missing") updatedoc := data.Doc
w.WriteHeader(http.StatusBadRequest)
return
}
var updatedoc bson.M
if err := readBsonDoc(r.Body, &updatedoc); err != nil {
logger.Error("UpdatePartyMemberDocument failed. body decoding error :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if err := gp.updateMemberDocument(gid, mid, updatedoc); err != nil { if err := gp.updateMemberDocument(gid, mid, updatedoc); err != nil {
logger.Println("UpdatePartyMemberDocument failed :", err) logger.Println("UpdatePartyMemberDocument failed :", err)
@ -720,19 +718,19 @@ func (gp *groupParty) updatePartyDocument(gid groupID, frag bson.M) error {
} }
func (gp *groupParty) UpdatePartyDocument(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) UpdatePartyDocument(w http.ResponseWriter, r *http.Request) {
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var data struct {
if !ok { Gid primitive.ObjectID `bson:"gid"`
logger.Println("UpdatePartyDocument failed. gid is missing") Doc bson.M `bson:"doc"`
w.WriteHeader(http.StatusBadRequest) }
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("UpdatePartyDocument failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return return
} }
var frag bson.M gid := data.Gid
if err := readBsonDoc(r.Body, &frag); err != nil { frag := data.Doc
logger.Error("UpdatePartyDocument failed. readBsonDoc err :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if err := gp.updatePartyDocument(gid, frag); err != nil { if err := gp.updatePartyDocument(gid, frag); err != nil {
logger.Error("UpdatePartyDocument failed. group.UpdatePartyDocument returns err :", err) logger.Error("UpdatePartyDocument failed. group.UpdatePartyDocument returns err :", err)
@ -742,13 +740,17 @@ func (gp *groupParty) UpdatePartyDocument(w http.ResponseWriter, r *http.Request
} }
func (gp *groupParty) QueryPartyMembers(w http.ResponseWriter, r *http.Request) { func (gp *groupParty) QueryPartyMembers(w http.ResponseWriter, r *http.Request) {
gid, ok := gocommon.ReadObjectIDFormValue(r.URL.Query(), "gid") var data struct {
if !ok { Gid primitive.ObjectID `bson:"gid"`
logger.Println("QueryPartyMembers failed. gid is missing") }
w.WriteHeader(http.StatusBadRequest)
if err := gocommon.ReadBsonDocumentFromBody(r.Body, &data); err != nil {
logger.Println("QueryPartyMembers failed. ReadBsonDocumentFromBody returns err :", err)
w.WriteHeader(http.StatusInternalServerError)
return return
} }
gid := data.Gid
gd := groupDoc{ gd := groupDoc{
id: gid, id: gid,
rh: gp.rh, rh: gp.rh,

View File

@ -4,11 +4,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"net/http" "net/http"
"reflect"
"strings" "strings"
"time" "time"
@ -39,29 +37,6 @@ func writeBsonDoc[T any](w io.Writer, src T) error {
return enc.Encode(src) return enc.Encode(src)
} }
func readBsonDoc(r io.Reader, src any) error {
body, err := io.ReadAll(r)
if err != nil {
return err
}
if len(body) == 0 {
return nil
}
decoder, err := bson.NewDecoder(bsonrw.NewBSONDocumentReader(body))
if err != nil {
return err
}
err = decoder.Decode(src)
if err != nil {
return err
}
return nil
}
type TavernConfig struct { type TavernConfig struct {
session.SessionConfig `json:",inline"` session.SessionConfig `json:",inline"`
gocommon.StorageAddr `json:"storage"` gocommon.StorageAddr `json:"storage"`
@ -75,11 +50,11 @@ type TavernConfig struct {
var config TavernConfig var config TavernConfig
type Tavern struct { type Tavern struct {
wsh *wshandler.WebsocketHandler wsh *wshandler.WebsocketHandler
mongoClient gocommon.MongoClient mongoClient gocommon.MongoClient
redison *gocommon.RedisonHandler redison *gocommon.RedisonHandler
groups map[string]group groups map[string]group
apiFuncs *apiFuncsContainer apiReceivers gocommon.HttpApiHandlerContainer
} }
func getMacAddr() (string, error) { func getMacAddr() (string, error) {
@ -116,10 +91,6 @@ func New(context context.Context, wsh *wshandler.WebsocketHandler, inconfig *Tav
config.macAddr = macaddr config.macAddr = macaddr
tv := &Tavern{ tv := &Tavern{
wsh: wsh, wsh: wsh,
apiFuncs: &apiFuncsContainer{
normfuncs: make(map[string]apiFuncType),
funcs: make(map[string][]apiFuncType),
},
} }
if err = tv.prepare(context); err != nil { if err = tv.prepare(context); err != nil {
@ -143,25 +114,24 @@ func (tv *Tavern) prepare(ctx context.Context) error {
tv.redison = gocommon.NewRedisonHandler(redisClient.Context(), redisClient) tv.redison = gocommon.NewRedisonHandler(redisClient.Context(), redisClient)
groups := make(map[string]group) groups := make(map[string]group)
for typename, cfg := range config.Group { if cfg, ok := config.Group["chat"]; ok {
gtype, ok := groupTypeContainer()[typename] chat := new(groupChat)
if !ok { if err := chat.Initialize(tv, cfg); err != nil {
return fmt.Errorf("%s group type is not valid", typename)
}
if !gtype.Implements(reflect.TypeOf((*group)(nil)).Elem()) {
return fmt.Errorf("%s is not implement proper interface", typename)
}
ptrvalue := reflect.New(gtype.Elem())
instance := ptrvalue.Interface().(group)
if err := instance.Initialize(tv, cfg); err != nil {
return err return err
} }
groups[typename] = instance tv.apiReceivers.RegistReceiver(gocommon.MakeHttpApiReceiver(chat, "chat"))
groups["chat"] = chat
} }
if cfg, ok := config.Group["party"]; ok {
party := new(groupParty)
if err := party.Initialize(tv, cfg); err != nil {
return err
}
tv.apiReceivers.RegistReceiver(gocommon.MakeHttpApiReceiver(party, "party"))
groups["party"] = party
}
tv.groups = groups tv.groups = groups
tv.apiFuncs.normalize()
return nil return nil
} }
@ -259,11 +229,12 @@ func (tv *Tavern) api(w http.ResponseWriter, r *http.Request) {
return return
} }
operation := r.URL.Query().Get("operation") funcname := r.URL.Query().Get("call")
if len(operation) == 0 { if len(funcname) == 0 {
logger.Println("query param 'call' is missing")
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
tv.apiFuncs.call(operation, w, r) tv.apiReceivers.Call(funcname, w, r)
} }

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.20
require ( require (
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
go.mongodb.org/mongo-driver v1.11.7 go.mongodb.org/mongo-driver v1.11.7
repositories.action2quare.com/ayo/gocommon v0.0.0-20230904053533-821dc5c639d8 repositories.action2quare.com/ayo/gocommon v0.0.0-20230905074334-7951814f125d
) )
require ( require (

16
go.sum
View File

@ -160,3 +160,19 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230904005440-d396a35713ad h1
repositories.action2quare.com/ayo/gocommon v0.0.0-20230904005440-d396a35713ad/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230904005440-d396a35713ad/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230904053533-821dc5c639d8 h1:clQB7s726Rt/q2BgGjZMVjek7Z0YDBUrD4HjFKUFSIw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230904053533-821dc5c639d8 h1:clQB7s726Rt/q2BgGjZMVjek7Z0YDBUrD4HjFKUFSIw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230904053533-821dc5c639d8/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230904053533-821dc5c639d8/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905024342-84f56dfc505c h1:Db+5nxlTu9W7qM0DApVqpG+y2MimlOz02FgTza291Jw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905024342-84f56dfc505c/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905025653-511784774009 h1:GtedcdVrlMY9CFbd6BoayjgFRwR/pbNPngZfAZ7Ublg=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905025653-511784774009/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905030636-def9494dd387 h1:lx9zDFycfr1703qszt9YLz8lae79gpu4tGe+kX41bG4=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905030636-def9494dd387/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905032825-8877bf88c0b7 h1:PkQS5X4t610gxGQD1AYklb2zH2bOUM9HLZowNnPPqlY=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905032825-8877bf88c0b7/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905045203-0762d9311b90 h1:AiG+6UG9vxiDsaTI8c6mPd53qIhon9T3C93jvQ4paAY=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905045203-0762d9311b90/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905054212-7489fa657a86 h1:WtjuPV28reK4RZgWHYH98ggUL6u/yxpaZzGgCvYY7so=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905054212-7489fa657a86/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905060206-3a3839a46187 h1:x+/G4jg2YwOdTmT4rHEw/RVaaxAc38CN8woWP9L/eZY=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905060206-3a3839a46187/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905074334-7951814f125d h1:zvTbz/14pdfjpSpczpSOcRCBZKY8agFILxL/QrIJngQ=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230905074334-7951814f125d/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=