diff --git a/core/api.go b/core/api.go index 2b00697..f82f4a5 100644 --- a/core/api.go +++ b/core/api.go @@ -161,18 +161,21 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error { mg := caller.mg logger.Println("blockAPI :", r.Method) - if r.Method == "GET" { + switch r.Method { + case "GET": target, ok := gocommon.ReadObjectIDFormValue(r.Form, "accid") - logger.Println("Get :", target, ok) if !ok { // 페이지네이션 해야할 듯 - json.NewEncoder(w).Encode(mg.bl.all()) + //json.NewEncoder(w).Encode(mg.bl.all()) } else if !target.IsZero() { - if blocked, ok := mg.bl.get(target); ok && blocked != nil { + var blocked []blockinfo + if err := caller.mg.mongoClient.FindAllAs(CollectionBlock, bson.M{ + "accid": target, + }, &blocked); err == nil { json.NewEncoder(w).Encode(blocked) } } - } else if r.Method == "PUT" { + case "PUT": var targets struct { Start primitive.DateTime End primitive.DateTime @@ -189,22 +192,15 @@ func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error { Meta: meta, } - _, _, err := mg.mongoClient.Update(CollectionBlock, bson.M{ - "_id": accid, - }, bson.M{ - "$set": &bi, - }, options.Update().SetUpsert(true)) + _, err := mg.mongoClient.Collection(CollectionBlock).InsertOne(r.Context(), bi) if err != nil { logger.Println("account is not blocked. err :", err) } else { logger.Println("account is blocked :", meta) - - bi.Accid = accid - caller.mg.bl.add(&bi) mg.sessionProvider.RevokeAll(accid) } } - } else if r.Method == "DELETE" { + case "DELETE": id := r.URL.Query().Get("id") if len(id) == 0 { @@ -215,29 +211,21 @@ func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error { return err } - _, _, err = mg.mongoClient.Update(CollectionBlock, bson.M{ - "_id": idobj, - }, bson.M{ - "$currentDate": bson.M{ - "_ts": bson.M{"$type": "date"}, - }, - }, options.Update().SetUpsert(false)) - - if err != nil { - return err - } - - caller.mg.bl.remove(idobj) + mg.mongoClient.Delete(CollectionBlock, bson.M{"_id": idobj}) } return nil } func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) error { mg := caller.mg - if r.Method == "GET" { - enc := json.NewEncoder(w) - enc.Encode(mg.wl.all()) - } else if r.Method == "PUT" { + switch r.Method { + case "GET": + var all []whitelistmember + if err := mg.mongoClient.AllAs(CollectionWhitelist, &all); err == nil { + enc := json.NewEncoder(w) + enc.Encode(all) + } + case "PUT": body, _ := io.ReadAll(r.Body) var member whitelistmember if err := json.Unmarshal(body, &member); err != nil { @@ -254,7 +242,7 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err if err != nil { return err } - } else if r.Method == "DELETE" { + case "DELETE": id := r.URL.Query().Get("id") if len(id) == 0 { diff --git a/core/maingate.go b/core/maingate.go index 9ff2077..3ec0c95 100644 --- a/core/maingate.go +++ b/core/maingate.go @@ -148,8 +148,6 @@ type Maingate struct { //services servicelist serviceptr unsafe.Pointer admins unsafe.Pointer - wl memberContainerPtr[string, *whitelistmember] - bl memberContainerPtr[primitive.ObjectID, *blockinfo] tokenEndpoints map[string]string authorizationEndpoints map[string]string @@ -345,7 +343,15 @@ func (mg *Maingate) prepare(context context.Context) (err error) { mg.mongoClient.DropIndex(CollectionBlock, "codeaccid") } - if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil { + if _, err := mg.mongoClient.Collection(CollectionBlock).Indexes().DropOne(context, "_ts_1"); err == nil { + // 인덱스가 방금 지워졌다. + // 전체 document 제거 + logger.Println(mg.mongoClient.Collection(CollectionBlock).Drop(context)) + } + + if err = mg.mongoClient.MakeUniqueIndices(CollectionBlock, map[string]bson.D{ + "accidend": {{Key: "accid", Value: 1}, {Key: "end", Value: 1}}, + }); err != nil { return logger.ErrorWithCallStack(err) } @@ -412,23 +418,6 @@ func (mg *Maingate) prepare(context context.Context) (err error) { } } - var whites []*whitelistmember - if err := mg.mongoClient.AllAs(CollectionWhitelist, &whites, options.Find().SetReturnKey(false)); err != nil { - return logger.ErrorWithCallStack(err) - } - mg.wl.init(whites) - - var blocks []*blockinfo - if err := mg.mongoClient.AllAs(CollectionBlock, &blocks); err != nil { - return logger.ErrorWithCallStack(err) - } - - logger.Println("allblocks :", blocks) - mg.bl.init(blocks) - - go mg.wl.watchCollection(context, CollectionWhitelist, mg.mongoClient) - go mg.bl.watchCollection(context, CollectionBlock, mg.mongoClient) - return nil } diff --git a/core/member_container.go b/core/member_container.go deleted file mode 100644 index 64f7b05..0000000 --- a/core/member_container.go +++ /dev/null @@ -1,159 +0,0 @@ -package core - -import ( - "context" - "sync/atomic" - "time" - "unsafe" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "repositories.action2quare.com/ayo/gocommon" - "repositories.action2quare.com/ayo/gocommon/logger" -) - -type memberContraints[K comparable] interface { - Key() K - Expired() bool -} - -type memberContainerPtr[K comparable, T memberContraints[K]] struct { - ptr unsafe.Pointer -} - -func (p *memberContainerPtr[K, T]) init(ms []T) { - next := map[K]T{} - for _, m := range ms { - next[m.Key()] = m - } - atomic.StorePointer(&p.ptr, unsafe.Pointer(&next)) -} - -func (p *memberContainerPtr[K, T]) add(m T) { - ptr := atomic.LoadPointer(&p.ptr) - src := (*map[K]T)(ptr) - - next := map[K]T{} - for k, v := range *src { - next[k] = v - } - next[m.Key()] = m - atomic.StorePointer(&p.ptr, unsafe.Pointer(&next)) -} - -func (p *memberContainerPtr[K, T]) get(key K) (T, bool) { - ptr := atomic.LoadPointer(&p.ptr) - src := (*map[K]T)(ptr) - - out, found := (*src)[key] - return out, found -} - -func (p *memberContainerPtr[K, T]) remove(key K) { - ptr := atomic.LoadPointer(&p.ptr) - src := (*map[K]T)(ptr) - - next := map[K]T{} - for k, v := range *src { - next[k] = v - } - delete(next, key) - atomic.StorePointer(&p.ptr, unsafe.Pointer(&next)) -} - -type memberPipelineDocument[K comparable, T memberContraints[K]] struct { - OperationType string `bson:"operationType"` - DocumentKey struct { - Id primitive.ObjectID `bson:"_id"` - } `bson:"documentKey"` - Member T `bson:"fullDocument"` -} - -func (p *memberContainerPtr[K, T]) all() []T { - ptr := atomic.LoadPointer(&p.ptr) - src := (*map[K]T)(ptr) - - out := make([]T, 0, len(*src)) - for _, m := range *src { - if m.Expired() { - continue - } - out = append(out, m) - } - return out -} - -func (p *memberContainerPtr[K, T]) watchCollection(parentctx context.Context, coll gocommon.CollectionName, mc gocommon.MongoClient) { - defer func() { - s := recover() - if s != nil { - logger.Error(s) - } - }() - - matchStage := bson.D{ - { - Key: "$match", Value: bson.D{ - {Key: "operationType", Value: bson.D{ - {Key: "$in", Value: bson.A{ - "update", - "insert", - }}, - }}, - }, - }} - projectStage := bson.D{ - { - Key: "$project", Value: bson.D{ - {Key: "documentKey", Value: 1}, - {Key: "fullDocument", Value: 1}, - }, - }, - } - - var stream *mongo.ChangeStream - var err error - var ctx context.Context - - for { - if stream == nil { - stream, err = mc.Watch(coll, mongo.Pipeline{matchStage, projectStage}) - if err != nil { - logger.Error("watchCollection watch failed :", err) - time.Sleep(time.Minute) - continue - } - ctx = context.TODO() - } - - changed := stream.TryNext(ctx) - if ctx.Err() != nil { - logger.Error("watchCollection stream.TryNext failed. process should be restarted! :", ctx.Err().Error()) - break - } - - if changed { - var data memberPipelineDocument[K, T] - if err := stream.Decode(&data); err == nil { - p.add(data.Member) - } else { - logger.Error("watchCollection stream.Decode failed :", err) - } - } else if stream.Err() != nil || stream.ID() == 0 { - select { - case <-ctx.Done(): - logger.Println("watchCollection is done") - stream.Close(ctx) - return - - case <-time.After(time.Second): - logger.Error("watchCollection stream error :", stream.Err()) - stream.Close(ctx) - stream = nil - } - } else { - time.Sleep(time.Second) - } - } -} diff --git a/core/service.go b/core/service.go index a138272..514ce58 100644 --- a/core/service.go +++ b/core/service.go @@ -22,9 +22,10 @@ import ( ) type blockinfo struct { + Id primitive.ObjectID `bson:"_id" json:"_id"` Start primitive.DateTime `bson:"start" json:"start"` - End primitive.DateTime `bson:"_ts" json:"_ts"` - Accid primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"` + End primitive.DateTime `bson:"end" json:"end"` + Accid primitive.ObjectID `bson:"accid,omitempty" json:"accid,omitempty"` Meta primitive.M `bson:"meta,omitempty" json:"meta,omitempty"` } @@ -99,8 +100,6 @@ type serviceDescription struct { VersionSplits map[string]string `bson:"version_splits" json:"version_splits"` sessionProvider session.Provider - wl *memberContainerPtr[string, *whitelistmember] - bl *memberContainerPtr[primitive.ObjectID, *blockinfo] mongoClient gocommon.MongoClient sessionTTL time.Duration @@ -264,9 +263,6 @@ func (sh *serviceDescription) prepare(mg *Maingate) error { } } } - - sh.wl = &mg.wl - sh.bl = &mg.bl sh.serviceSerialized, _ = json.Marshal(sh) return nil @@ -672,15 +668,6 @@ func (sh *serviceDescription) authorize_dev(w http.ResponseWriter, r *http.Reque if r.Method == "DELETE" { sk := r.Header.Get("AS-X-SESSION") if authinfo, err := sh.sessionProvider.Query(sk); err == nil { - bt := r.Header.Get("AS-X-BLOCK") - if len(bt) > 0 { - dur, _ := strconv.ParseInt(bt, 10, 0) - sh.bl.add(&blockinfo{ - Start: primitive.NewDateTimeFromTime(time.Now().UTC()), - End: primitive.NewDateTimeFromTime(time.Now().UTC().Add(time.Second * time.Duration(dur))), - Accid: authinfo.Account, - }) - } sh.sessionProvider.RevokeAll(authinfo.Account) } @@ -799,7 +786,8 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request) oldcreate := account["create"].(primitive.DateTime) newaccount := oldcreate == createtime - if bi, ok := sh.bl.get(accid); ok { + var bi blockinfo + if err := sh.mongoClient.FindOneAs(CollectionBlock, bson.M{"accid": accid, "end": bson.M{"$gt": time.Now().UTC()}}, &bi); err == nil { // 블럭된 계정. 블락 정보를 알려준다. w.Header().Add("MG-ACCOUNTBLOCK-START", strconv.FormatInt(bi.Start.Time().Unix(), 10)) w.Header().Add("MG-ACCOUNTBLOCK-END", strconv.FormatInt(bi.End.Time().Unix(), 10)) @@ -1081,7 +1069,8 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) } wm := &whitelistmember{Alias: authInfo.Alias, Platform: authInfo.Platform} - if _, ok := sh.wl.get(wm.Key()); ok { + doc, err := sh.mongoClient.FindOne(CollectionWhitelist, bson.M{"alias": wm.Key()}) + if err == nil && doc != nil { // qa 권한이면 입장 가능 addrresp = div.urlsSerialized } else if div.Maintenance != nil { diff --git a/go.mod b/go.mod index 538b89f..5aeedfc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module repositories.action2quare.com/ayo/maingate -go 1.19 +go 1.22.1 + + require ( firebase.google.com/go v3.13.0+incompatible