Merge branch 'master' into kd-live
This commit is contained in:
144
core/api.go
144
core/api.go
@ -2,9 +2,7 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -27,7 +25,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type FileDocumentDesc struct {
|
type FileDocumentDesc struct {
|
||||||
Service string `bson:"service" json:"service"`
|
|
||||||
Key string `bson:"key" json:"key"`
|
Key string `bson:"key" json:"key"`
|
||||||
Src string `bson:"src" json:"src"`
|
Src string `bson:"src" json:"src"`
|
||||||
Link string `bson:"link" json:"link"`
|
Link string `bson:"link" json:"link"`
|
||||||
@ -110,11 +107,6 @@ var seq = uint32(0)
|
|||||||
|
|
||||||
func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error {
|
func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error {
|
||||||
if r.Method == "PUT" {
|
if r.Method == "PUT" {
|
||||||
servicename := r.FormValue("service")
|
|
||||||
hasher := md5.New()
|
|
||||||
hasher.Write([]byte(servicename))
|
|
||||||
subfolder := hex.EncodeToString(hasher.Sum(nil))[:8]
|
|
||||||
|
|
||||||
infile, header, err := r.FormFile("file")
|
infile, header, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
@ -130,17 +122,16 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
|||||||
var b [5]byte
|
var b [5]byte
|
||||||
binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix()))
|
binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix()))
|
||||||
b[4] = byte(atomic.AddUint32(&seq, 1) % 255)
|
b[4] = byte(atomic.AddUint32(&seq, 1) % 255)
|
||||||
rf := hex.EncodeToString(b[1:])
|
|
||||||
newidstr := subfolder + rf
|
|
||||||
newidbt, _ := hex.DecodeString(newidstr)
|
|
||||||
newidobj := primitive.NewObjectID()
|
|
||||||
copy(newidobj[:], newidbt[:8])
|
|
||||||
|
|
||||||
|
newidobj := primitive.NewObjectID()
|
||||||
|
copy(newidobj[:], b[1:])
|
||||||
|
|
||||||
|
rf := newidobj.Hex()
|
||||||
var link string
|
var link string
|
||||||
if extract {
|
if extract {
|
||||||
link = path.Join("static", subfolder, rf)
|
link = path.Join("static", rf)
|
||||||
} else {
|
} else {
|
||||||
link = path.Join("static", subfolder, rf, header.Filename)
|
link = path.Join("static", rf, header.Filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
newdoc := FileDocumentDesc{
|
newdoc := FileDocumentDesc{
|
||||||
@ -151,11 +142,9 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
|||||||
Link: link,
|
Link: link,
|
||||||
Desc: desc,
|
Desc: desc,
|
||||||
Key: rf,
|
Key: rf,
|
||||||
Service: servicename,
|
|
||||||
}
|
}
|
||||||
_, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{
|
_, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{
|
||||||
"_id": newidobj,
|
"_id": newidobj,
|
||||||
"service": servicename,
|
|
||||||
"key": rf,
|
"key": rf,
|
||||||
}, newdoc)
|
}, newdoc)
|
||||||
|
|
||||||
@ -169,44 +158,81 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) error {
|
func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error {
|
||||||
mg := caller.mg
|
mg := caller.mg
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
// if !caller.isAdminOrValidToken() {
|
enc := json.NewEncoder(w)
|
||||||
// logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
|
enc.Encode(mg.bl.all())
|
||||||
// w.WriteHeader(http.StatusUnauthorized)
|
} else if r.Method == "PUT" {
|
||||||
// return nil
|
body, _ := io.ReadAll(r.Body)
|
||||||
// }
|
|
||||||
|
|
||||||
all, err := mg.mongoClient.All(CollectionWhitelist)
|
var bipl blockinfoWithStringId
|
||||||
|
if err := json.Unmarshal(body, &bipl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
accid, err := primitive.ObjectIDFromHex(bipl.StrId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(all) > 0 {
|
bi := blockinfo{
|
||||||
var notexp []primitive.M
|
Start: primitive.NewDateTimeFromTime(time.Unix(bipl.StartUnix, 0)),
|
||||||
for _, v := range all {
|
End: primitive.NewDateTimeFromTime(time.Unix(bipl.EndUnix, 0)),
|
||||||
if _, exp := v["_ts"]; !exp {
|
Reason: bipl.Reason,
|
||||||
notexp = append(notexp, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Println("bi :", accid, bi)
|
||||||
|
|
||||||
|
_, _, err = mg.mongoClient.Update(CollectionBlock, bson.M{
|
||||||
|
"_id": accid,
|
||||||
|
}, bson.M{
|
||||||
|
"$set": &bi,
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
allraw, _ := json.Marshal(notexp)
|
} else if r.Method == "DELETE" {
|
||||||
w.Write(allraw)
|
id := r.URL.Query().Get("id")
|
||||||
|
|
||||||
|
if len(id) == 0 {
|
||||||
|
return errors.New("id param is missing")
|
||||||
}
|
}
|
||||||
|
idobj, err := primitive.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
mg.mongoClient.Delete(CollectionAuth, 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" {
|
} else if r.Method == "PUT" {
|
||||||
body, _ := io.ReadAll(r.Body)
|
body, _ := io.ReadAll(r.Body)
|
||||||
var member whitelistmember
|
var member whitelistmember
|
||||||
if err := json.Unmarshal(body, &member); err != nil {
|
if err := json.Unmarshal(body, &member); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
member.ExpiredAt = 0
|
||||||
// if !caller.isAdminOrValidToken() {
|
|
||||||
// logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
|
|
||||||
// w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
member.Expired = 0
|
|
||||||
|
|
||||||
_, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{
|
_, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{
|
||||||
"_id": primitive.NewObjectID(),
|
"_id": primitive.NewObjectID(),
|
||||||
@ -260,7 +286,7 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
|||||||
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(&newService))
|
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(&newService))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(mg.service().divisionsSerialized)
|
w.Write(mg.service().serviceSerialized)
|
||||||
} else if r.Method == "POST" {
|
} else if r.Method == "POST" {
|
||||||
body, _ := io.ReadAll(r.Body)
|
body, _ := io.ReadAll(r.Body)
|
||||||
var service serviceDescription
|
var service serviceDescription
|
||||||
@ -319,6 +345,38 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (caller apiCaller) couponAPI(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
switch r.Method {
|
||||||
|
case "PUT":
|
||||||
|
// 쿠폰 생성
|
||||||
|
logger.Println("begin generateCoupons")
|
||||||
|
generateCoupons(caller.mg.mongoClient, w, r)
|
||||||
|
|
||||||
|
case "POST":
|
||||||
|
// TODO : 쿠폰 사용
|
||||||
|
// 쿠폰 사용 표시 해주고 내용을 응답
|
||||||
|
logger.Println("begin useCoupon")
|
||||||
|
useCoupon(caller.mg.mongoClient, w, r)
|
||||||
|
|
||||||
|
case "GET":
|
||||||
|
// 쿠폰 조회
|
||||||
|
if r.Form.Has("code") {
|
||||||
|
// 쿠폰 코드 조회
|
||||||
|
logger.Println("begin queryCoupon")
|
||||||
|
queryCoupon(caller.mg.mongoClient, w, r)
|
||||||
|
} else if r.Form.Has("name") {
|
||||||
|
// 쿠폰 코드 다운
|
||||||
|
logger.Println("begin downloadCoupons")
|
||||||
|
downloadCoupons(caller.mg.mongoClient, w, r)
|
||||||
|
} else {
|
||||||
|
// 쿠폰 이름 목록
|
||||||
|
logger.Println("begin listAllCouponNames")
|
||||||
|
listAllCouponNames(caller.mg.mongoClient, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var errApiTokenMissing = errors.New("mg-x-api-token is missing")
|
var errApiTokenMissing = errors.New("mg-x-api-token is missing")
|
||||||
|
|
||||||
func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error {
|
func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error {
|
||||||
@ -359,6 +417,8 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
|
|||||||
r.Body.Close()
|
r.Body.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
r.ParseMultipartForm(32 << 20)
|
||||||
|
|
||||||
var userinfo map[string]any
|
var userinfo map[string]any
|
||||||
|
|
||||||
if !*devflag {
|
if !*devflag {
|
||||||
@ -439,6 +499,10 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = caller.maintenanceAPI(w, r)
|
err = caller.maintenanceAPI(w, r)
|
||||||
} else if strings.HasSuffix(r.URL.Path, "/files") {
|
} else if strings.HasSuffix(r.URL.Path, "/files") {
|
||||||
err = caller.filesAPI(w, r)
|
err = caller.filesAPI(w, r)
|
||||||
|
} else if strings.HasSuffix(r.URL.Path, "/block") {
|
||||||
|
err = caller.blockAPI(w, r)
|
||||||
|
} else if strings.HasSuffix(r.URL.Path, "/coupon") {
|
||||||
|
err = caller.couponAPI(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
372
core/api_coupon.go
Normal file
372
core/api_coupon.go
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"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"
|
||||||
|
coupon "repositories.action2quare.com/ayo/gocommon/coupon"
|
||||||
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CollectionCoupon = gocommon.CollectionName("coupon")
|
||||||
|
CollectionCouponUse = gocommon.CollectionName("coupon_use")
|
||||||
|
)
|
||||||
|
|
||||||
|
type couponDoc struct {
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
Effect string `json:"effect" bson:"effect"`
|
||||||
|
Desc string `json:"desc" bson:"desc"`
|
||||||
|
Total int64 `json:"total" bson:"total"`
|
||||||
|
Remains []string `json:"remains,omitempty" bson:"remains,omitempty"`
|
||||||
|
Used []string `json:"used,omitempty" bson:"used,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCouponKey(roundnum uint32, uid []byte) string {
|
||||||
|
left := binary.BigEndian.Uint16(uid[0:2])
|
||||||
|
right := binary.BigEndian.Uint16(uid[2:4])
|
||||||
|
multi := uint32(left) * uint32(right)
|
||||||
|
xor := roundnum ^ multi
|
||||||
|
|
||||||
|
final := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint32(final, xor)
|
||||||
|
copy(final[4:], uid)
|
||||||
|
return fmt.Sprintf("%s-%s-%s-%s", hex.EncodeToString(final[0:2]), hex.EncodeToString(final[2:4]), hex.EncodeToString(final[4:6]), hex.EncodeToString(final[6:8]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCouponCodes(name string, count int) (string, map[string]string) {
|
||||||
|
checkunique := make(map[string]bool)
|
||||||
|
keys := make(map[string]string)
|
||||||
|
uid := make([]byte, 4)
|
||||||
|
|
||||||
|
roundHash, roundnum := coupon.MakeCouponRoundHash(name)
|
||||||
|
seed := time.Now().UnixNano()
|
||||||
|
|
||||||
|
for len(keys) < count {
|
||||||
|
rand.Seed(seed)
|
||||||
|
rand.Read(uid)
|
||||||
|
|
||||||
|
code := makeCouponKey(roundnum, uid)
|
||||||
|
|
||||||
|
if _, ok := checkunique[code]; !ok {
|
||||||
|
checkunique[code] = true
|
||||||
|
keys[hex.EncodeToString(uid)] = code
|
||||||
|
}
|
||||||
|
seed = int64(binary.BigEndian.Uint32(uid))
|
||||||
|
}
|
||||||
|
return roundHash, keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCoupons(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||||
|
name, _ := gocommon.ReadStringFormValue(r.Form, "name")
|
||||||
|
effect, _ := gocommon.ReadStringFormValue(r.Form, "effect")
|
||||||
|
count, _ := gocommon.ReadIntegerFormValue(r.Form, "count")
|
||||||
|
desc, _ := gocommon.ReadStringFormValue(r.Form, "desc")
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
logger.Println("[generateCoupons] count == 0")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
roundHash, _ := coupon.MakeCouponRoundHash(name)
|
||||||
|
roundObj, _ := primitive.ObjectIDFromHex(roundHash + roundHash + roundHash)
|
||||||
|
|
||||||
|
if count < 0 {
|
||||||
|
// 무한 쿠폰이므로 그냥 문서 생성해 주고 끝
|
||||||
|
if _, _, err := mongoClient.Update(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, bson.M{
|
||||||
|
"$set": &couponDoc{
|
||||||
|
Name: name,
|
||||||
|
Effect: effect,
|
||||||
|
Desc: desc,
|
||||||
|
Total: -1,
|
||||||
|
},
|
||||||
|
}, options.Update().SetUpsert(true)); err != nil {
|
||||||
|
logger.Println("[generateCoupons] Update failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// effect가 비어있으면 기존의 roundName에 갯수를 추가해 준다
|
||||||
|
// effect가 비어있지 않으면 roundName이 겹쳐서는 안된다.
|
||||||
|
coupondoc, err := mongoClient.FindOne(CollectionCoupon, bson.M{"_id": roundObj})
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("[generateCoupons] FindOne failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKeys := make(map[string]bool)
|
||||||
|
if coupondoc != nil {
|
||||||
|
if r, ok := coupondoc["remains"]; ok {
|
||||||
|
remains := r.(primitive.A)
|
||||||
|
for _, uid := range remains {
|
||||||
|
lastKeys[uid.(string)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issuedKeys := make(map[string]string)
|
||||||
|
for len(issuedKeys) < int(count) {
|
||||||
|
_, vs := makeCouponCodes(name, int(count)-len(issuedKeys))
|
||||||
|
for k, v := range vs {
|
||||||
|
if _, ok := lastKeys[k]; !ok {
|
||||||
|
// 기존 키와 중복되지 않는 것만
|
||||||
|
issuedKeys[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var coupons []string
|
||||||
|
var uids []string
|
||||||
|
for uid, code := range issuedKeys {
|
||||||
|
uids = append(uids, uid)
|
||||||
|
coupons = append(coupons, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if coupondoc != nil {
|
||||||
|
_, _, err = mongoClient.Update(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, bson.M{
|
||||||
|
"$push": bson.M{"remains": bson.M{"$each": uids}},
|
||||||
|
"$inc": bson.M{"total": count},
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
} else {
|
||||||
|
_, _, err = mongoClient.Update(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, bson.M{
|
||||||
|
"$push": bson.M{"remains": bson.M{"$each": uids}},
|
||||||
|
"$set": couponDoc{
|
||||||
|
Name: name,
|
||||||
|
Effect: effect,
|
||||||
|
Desc: desc,
|
||||||
|
Total: count,
|
||||||
|
},
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("[generateCoupons] Update failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.Encode(coupons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadCoupons(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||||
|
name, _ := gocommon.ReadStringFormValue(r.Form, "name")
|
||||||
|
if len(name) == 0 {
|
||||||
|
logger.Println("[downloadCoupons] name is empty")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
round, _ := coupon.MakeCouponRoundHash(name)
|
||||||
|
|
||||||
|
roundObj, err := primitive.ObjectIDFromHex(round + round + round)
|
||||||
|
if err != nil {
|
||||||
|
// 유효하지 않은 형식의 code
|
||||||
|
logger.Println("[downloadCoupons] ObjectIDFromHex failed :", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var coupon couponDoc
|
||||||
|
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, &coupon, options.FindOne().SetProjection(bson.M{"_id": 0, "remains": 1})); err != nil {
|
||||||
|
logger.Println("[downloadCoupons] FindOne failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
roundnum := binary.BigEndian.Uint32(roundObj[:])
|
||||||
|
var coupons []string
|
||||||
|
for _, uid := range coupon.Remains {
|
||||||
|
coupons = append(coupons, makeCouponKey(roundnum, []byte(uid)))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.Encode(coupons)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryCoupon(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||||
|
code, _ := gocommon.ReadStringFormValue(r.Form, "code")
|
||||||
|
if len(code) == 0 {
|
||||||
|
logger.Println("[queryCoupon] code is empty")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
round, _ := coupon.DisolveCouponCode(code)
|
||||||
|
if len(round) == 0 {
|
||||||
|
// 유효하지 않은 형식의 code
|
||||||
|
// 쿠폰 이름일 수 있으므로 round hash를 계산한다.
|
||||||
|
round, _ = coupon.MakeCouponRoundHash(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
roundObj, err := primitive.ObjectIDFromHex(round + round + round)
|
||||||
|
if err != nil {
|
||||||
|
// 유효하지 않은 형식의 code
|
||||||
|
logger.Println("[queryCoupon] ObjectIDFromHex failed :", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var coupon couponDoc
|
||||||
|
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, &coupon, options.FindOne().SetProjection(bson.M{"effect": 1, "name": 1, "reason": 1, "total": 1, "desc": 1}).SetReturnKey(false)); err != nil {
|
||||||
|
logger.Println("[queryCoupon] FindOneAs failed :", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.Encode(coupon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAllCouponNames(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||||
|
all, err := mongoClient.FindAll(CollectionCoupon, bson.M{}, options.Find().SetProjection(bson.M{"name": 1}))
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for _, doc := range all {
|
||||||
|
names = append(names, doc["name"].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.Encode(names)
|
||||||
|
}
|
||||||
|
|
||||||
|
func useCoupon(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||||
|
acc, ok := gocommon.ReadObjectIDFormValue(r.Form, "accid")
|
||||||
|
if !ok || acc.IsZero() {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code, _ := gocommon.ReadStringFormValue(r.Form, "code")
|
||||||
|
code = strings.TrimSpace(code)
|
||||||
|
if len(code) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
round, key := coupon.DisolveCouponCode(code)
|
||||||
|
if len(round) == 0 {
|
||||||
|
// couponId가 쿠폰 이름일 수도 있다. 무한 쿠폰
|
||||||
|
round, _ = coupon.MakeCouponRoundHash(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 내가 이 라운드의 쿠폰을 쓴 적이 있나
|
||||||
|
already, err := mongoClient.Exists(CollectionCouponUse, bson.M{
|
||||||
|
"_id": acc,
|
||||||
|
"rounds": round,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if already {
|
||||||
|
// 이미 이 라운드의 쿠폰을 사용한 적이 있다.
|
||||||
|
w.WriteHeader(http.StatusConflict)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var coupon couponDoc
|
||||||
|
roundObj, _ := primitive.ObjectIDFromHex(round + round + round)
|
||||||
|
if len(key) == 0 {
|
||||||
|
// 무한 쿠폰일 수 있으므로 존재하는지 확인
|
||||||
|
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, &coupon, options.FindOne().SetProjection(bson.M{"_id": 0, "effect": 1, "name": 1, "reason": 1, "total": 1})); err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if coupon.Total > 0 {
|
||||||
|
// 무한 쿠폰 아니네?
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 2. 쿠폰을 하나 꺼냄
|
||||||
|
matched, _, err := mongoClient.Update(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, bson.M{
|
||||||
|
"$pull": bson.M{"remains": key},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
// 쿠폰이 없다.
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. round의 효과 읽기
|
||||||
|
if err := mongoClient.FindOneAndUpdateAs(CollectionCoupon, bson.M{
|
||||||
|
"_id": roundObj,
|
||||||
|
}, bson.M{
|
||||||
|
"$push": bson.M{"used": key},
|
||||||
|
}, &coupon, options.FindOneAndUpdate().SetProjection(bson.M{"effect": 1})); err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(coupon.Effect) == 0 {
|
||||||
|
// 쿠폰이 없네?
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 쿠폰은 사용한 것으로 표시
|
||||||
|
// 이제 이 아래에서 실패하면 이 쿠폰은 못쓴다.
|
||||||
|
updated, _, err := mongoClient.Update(CollectionCouponUse, bson.M{
|
||||||
|
"_id": acc,
|
||||||
|
}, bson.M{
|
||||||
|
"$push": bson.M{"rounds": round},
|
||||||
|
"$set": bson.M{round + ".id": code},
|
||||||
|
"$currentDate": bson.M{round + ".ts": true},
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !updated {
|
||||||
|
logger.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(coupon.Effect))
|
||||||
|
}
|
||||||
39
core/api_test.go
Normal file
39
core/api_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeLocalUniqueId(t *testing.T) {
|
||||||
|
ts := int64(1690815600)
|
||||||
|
start := primitive.NewDateTimeFromTime(time.Unix(ts, 0))
|
||||||
|
ts = int64(1693493999)
|
||||||
|
end := primitive.NewDateTimeFromTime(time.Unix(ts, 0))
|
||||||
|
|
||||||
|
fmt.Println(start.Time().Format(time.RFC3339))
|
||||||
|
fmt.Println(end.Time().Format(time.RFC3339))
|
||||||
|
|
||||||
|
mongoClient, err := gocommon.NewMongoClient(context.Background(), "mongodb://121.134.91.160:27018/?replicaSet=rs0&retrywrites=true", "mountain-maingate")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bi := blockinfo{
|
||||||
|
Start: start,
|
||||||
|
End: end,
|
||||||
|
Reason: "test",
|
||||||
|
}
|
||||||
|
mongoClient.Update(CollectionBlock, bson.M{
|
||||||
|
"_id": primitive.NewObjectID(),
|
||||||
|
}, bson.M{
|
||||||
|
"$set": &bi,
|
||||||
|
}, options.Update().SetUpsert(true))
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -169,7 +170,8 @@ type Maingate struct {
|
|||||||
//services servicelist
|
//services servicelist
|
||||||
serviceptr unsafe.Pointer
|
serviceptr unsafe.Pointer
|
||||||
admins unsafe.Pointer
|
admins unsafe.Pointer
|
||||||
wl whitelist
|
wl memberContainerPtr[string, *whitelistmember]
|
||||||
|
bl memberContainerPtr[primitive.ObjectID, *blockinfo]
|
||||||
|
|
||||||
tokenEndpoints map[string]string
|
tokenEndpoints map[string]string
|
||||||
authorizationEndpoints map[string]string
|
authorizationEndpoints map[string]string
|
||||||
@ -206,7 +208,6 @@ func New(ctx context.Context) (*Maingate, error) {
|
|||||||
|
|
||||||
err := mg.prepare(ctx)
|
err := mg.prepare(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("mg.prepare() failed :", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,106 +287,104 @@ func (mg *Maingate) discoverOpenIdConfiguration(name string, url string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeErrorWithStack(err error) error {
|
||||||
|
return fmt.Errorf("%s\n%s", err.Error(), string(debug.Stack()))
|
||||||
|
}
|
||||||
|
|
||||||
func (mg *Maingate) prepare(context context.Context) (err error) {
|
func (mg *Maingate) prepare(context context.Context) (err error) {
|
||||||
if err := mg.discoverOpenIdConfiguration(AuthPlatformMicrosoft, "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"); err != nil {
|
if err := mg.discoverOpenIdConfiguration(AuthPlatformMicrosoft, "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
if err := mg.discoverOpenIdConfiguration("google", "https://accounts.google.com/.well-known/openid-configuration"); err != nil {
|
if err := mg.discoverOpenIdConfiguration("google", "https://accounts.google.com/.well-known/openid-configuration"); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// redis에서 env를 가져온 후에
|
// redis에서 env를 가져온 후에
|
||||||
mg.mongoClient, err = gocommon.NewMongoClient(context, mg.Mongo, "maingate")
|
mg.mongoClient, err = gocommon.NewMongoClient(context, mg.Mongo, "maingate")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return makeErrorWithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionCouponUse, map[string]bson.D{
|
||||||
|
"idrounds": {{Key: "_id", Value: 1}, {Key: "rounds", Value: 1}},
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{
|
||||||
"skonly": {{Key: "sk", Value: 1}},
|
"skonly": {{Key: "sk", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
||||||
"platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}},
|
"platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
||||||
"emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}},
|
"emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeIndices(CollectionWhitelist, map[string]bson.D{
|
|
||||||
"service": {{Key: "service", Value: 1}},
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeIndices(CollectionFile, map[string]bson.D{
|
|
||||||
"service": {{Key: "service", Value: 1}},
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{
|
if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{
|
||||||
"accid": {{Key: "accid", Value: 1}},
|
"accid": {{Key: "accid", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{
|
||||||
"sk": {{Key: "service", Value: 1}, {Key: "key", Value: 1}},
|
"keyonly": {{Key: "key", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete대신 _ts로 expire시킴. pipeline에 삭제 알려주기 위함
|
||||||
if err = mg.mongoClient.MakeExpireIndex(CollectionWhitelist, 10); err != nil {
|
if err = mg.mongoClient.MakeExpireIndex(CollectionWhitelist, 10); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeExpireIndex(CollectionAuth, int32(mg.SessionTTL+300)); err != nil {
|
if err = mg.mongoClient.MakeExpireIndex(CollectionAuth, int32(mg.SessionTTL+300)); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionBlock, map[string]bson.D{
|
if *devflag {
|
||||||
"codeaccid": {{Key: "code", Value: 1}, {Key: "accid", Value: 1}},
|
// 에러 체크하지 말것
|
||||||
}); err != nil {
|
mg.mongoClient.DropIndex(CollectionBlock, "codeaccid")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil {
|
if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionPlatformLoginToken, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionPlatformLoginToken, map[string]bson.D{
|
||||||
"platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}},
|
"platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(mg.SessionTTL+300)); err != nil {
|
if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(mg.SessionTTL+300)); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionUserToken, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionUserToken, map[string]bson.D{
|
||||||
"platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}},
|
"platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{
|
||||||
"gamepotuserid": {{Key: "gamepotuserid", Value: 1}},
|
"gamepotuserid": {{Key: "gamepotuserid", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{
|
if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{
|
||||||
"firebaseuserid": {{Key: "firebaseuserid", Value: 1}},
|
"firebaseuserid": {{Key: "firebaseuserid", Value: 1}},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second)))
|
mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second)))
|
||||||
@ -397,7 +396,7 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
|
|||||||
if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{
|
if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{
|
||||||
"link": 1,
|
"link": 1,
|
||||||
})); err != nil {
|
})); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pre := range preall {
|
for _, pre := range preall {
|
||||||
@ -412,35 +411,33 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
|
|||||||
"_id": pre.Id,
|
"_id": pre.Id,
|
||||||
}, &fulldoc)
|
}, &fulldoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
err = fulldoc.Save()
|
err = fulldoc.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var whites []whitelistmember
|
var whites []*whitelistmember
|
||||||
if err := mg.mongoClient.AllAs(CollectionWhitelist, &whites, options.Find().SetReturnKey(false)); err != nil {
|
if err := mg.mongoClient.AllAs(CollectionWhitelist, &whites, options.Find().SetReturnKey(false)); err != nil {
|
||||||
return err
|
return makeErrorWithStack(err)
|
||||||
}
|
}
|
||||||
mg.wl.init(whites)
|
mg.wl.init(whites)
|
||||||
|
|
||||||
|
var blocks []*blockinfo
|
||||||
|
if err := mg.mongoClient.AllAs(CollectionBlock, &blocks); err != nil {
|
||||||
|
return makeErrorWithStack(err)
|
||||||
|
}
|
||||||
|
mg.bl.init(blocks)
|
||||||
|
|
||||||
go watchAuthCollection(context, mg.auths, mg.mongoClient)
|
go watchAuthCollection(context, mg.auths, mg.mongoClient)
|
||||||
go mg.watchWhitelistCollection(context)
|
go mg.wl.watchCollection(context, CollectionWhitelist, mg.mongoClient)
|
||||||
|
go mg.bl.watchCollection(context, CollectionBlock, mg.mongoClient)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func whitelistKey(email string, platform string) string {
|
|
||||||
if strings.HasPrefix(email, "*@") {
|
|
||||||
// 도메인 전체 허용
|
|
||||||
return email[2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return email
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
|
func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
|
||||||
var allServices []*serviceDescription
|
var allServices []*serviceDescription
|
||||||
if err := mg.mongoClient.AllAs(CollectionService, &allServices, options.Find().SetReturnKey(false)); err != nil {
|
if err := mg.mongoClient.AllAs(CollectionService, &allServices, options.Find().SetReturnKey(false)); err != nil {
|
||||||
|
|||||||
169
core/member_container.go
Normal file
169
core/member_container.go
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
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]) 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]) contains(key K, out *T) bool {
|
||||||
|
ptr := atomic.LoadPointer(&p.ptr)
|
||||||
|
src := (*map[K]T)(ptr)
|
||||||
|
|
||||||
|
found, exists := (*src)[key]
|
||||||
|
if exists {
|
||||||
|
if found.Expired() {
|
||||||
|
p.remove(key)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if out != nil {
|
||||||
|
out = &found
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,10 +39,11 @@ func (mg *Maingate) platform_steamsdk_authorize(w http.ResponseWriter, r *http.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !*noauth {
|
||||||
err = authenticateSteamUser(mg.SteamPublisherAuthKey, mg.SteamAppId, authinfo.UserSteamId, authinfo.UserAuthToken)
|
err = authenticateSteamUser(mg.SteamPublisherAuthKey, mg.SteamAppId, authinfo.UserSteamId, authinfo.UserAuthToken)
|
||||||
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
||||||
acceestoken_expire_time := time.Date(2999, 1, int(time.January), 0, 0, 0, 0, time.UTC).Unix()
|
acceestoken_expire_time := time.Date(2999, 1, int(time.January), 0, 0, 0, 0, time.UTC).Unix()
|
||||||
|
|
||||||
var info usertokeninfo
|
var info usertokeninfo
|
||||||
|
|||||||
126
core/service.go
126
core/service.go
@ -7,10 +7,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"repositories.action2quare.com/ayo/gocommon"
|
"repositories.action2quare.com/ayo/gocommon"
|
||||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||||
@ -22,19 +21,45 @@ import (
|
|||||||
|
|
||||||
type blockinfo struct {
|
type blockinfo struct {
|
||||||
Start primitive.DateTime `bson:"start" json:"start"`
|
Start primitive.DateTime `bson:"start" json:"start"`
|
||||||
End primitive.DateTime `bson:"_ts"`
|
End primitive.DateTime `bson:"_ts" json:"_ts"`
|
||||||
Reason string `bson:"reason" json:"reason"`
|
Reason string `bson:"reason" json:"reason"`
|
||||||
|
Accid primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type blockinfoWithStringId struct {
|
||||||
|
Reason string `bson:"reason" json:"reason"`
|
||||||
|
StrId string `bson:"id" json:"id"`
|
||||||
|
StartUnix int64 `bson:"start_unix" json:"start_unix"`
|
||||||
|
EndUnix int64 `bson:"end_unix" json:"end_unix"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type whitelistmember struct {
|
type whitelistmember struct {
|
||||||
|
Id primitive.ObjectID `bson:"_id" json:"_id"`
|
||||||
Email string `bson:"email" json:"email"`
|
Email string `bson:"email" json:"email"`
|
||||||
Platform string `bson:"platform" json:"platform"`
|
Platform string `bson:"platform" json:"platform"`
|
||||||
Desc string `bson:"desc" json:"desc"`
|
Desc string `bson:"desc" json:"desc"`
|
||||||
Expired primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
|
ExpiredAt primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type whitelist struct {
|
func (wh *whitelistmember) Key() string {
|
||||||
emailptr unsafe.Pointer
|
if strings.HasPrefix(wh.Email, "*@") {
|
||||||
|
// 도메인 전체 허용
|
||||||
|
return wh.Email[2:]
|
||||||
|
}
|
||||||
|
return wh.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wh *whitelistmember) Expired() bool {
|
||||||
|
// 얘는 Expired가 있기만 하면 제거된 상태
|
||||||
|
return wh.ExpiredAt != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bi *blockinfo) Key() primitive.ObjectID {
|
||||||
|
return bi.Accid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bi *blockinfo) Expired() bool {
|
||||||
|
return bi.End.Time().Unix() < time.Now().UTC().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
type usertokeninfo struct {
|
type usertokeninfo struct {
|
||||||
@ -47,54 +72,6 @@ type usertokeninfo struct {
|
|||||||
accesstoken_expire_time int64 // microsoft only
|
accesstoken_expire_time int64 // microsoft only
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wl *whitelist) init(total []whitelistmember) {
|
|
||||||
all := make(map[string]*whitelistmember)
|
|
||||||
for _, member := range total {
|
|
||||||
all[whitelistKey(member.Email, member.Platform)] = &member
|
|
||||||
}
|
|
||||||
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&all))
|
|
||||||
}
|
|
||||||
|
|
||||||
func addToUnsafePointer(to *unsafe.Pointer, m *whitelistmember) {
|
|
||||||
ptr := atomic.LoadPointer(to)
|
|
||||||
src := (*map[string]*whitelistmember)(ptr)
|
|
||||||
|
|
||||||
next := map[string]*whitelistmember{}
|
|
||||||
for k, v := range *src {
|
|
||||||
next[k] = v
|
|
||||||
}
|
|
||||||
next[whitelistKey(m.Email, m.Platform)] = m
|
|
||||||
atomic.StorePointer(to, unsafe.Pointer(&next))
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeFromUnsafePointer(from *unsafe.Pointer, email string, platform string) {
|
|
||||||
ptr := atomic.LoadPointer(from)
|
|
||||||
src := (*map[string]*whitelistmember)(ptr)
|
|
||||||
|
|
||||||
next := make(map[string]*whitelistmember)
|
|
||||||
for k, v := range *src {
|
|
||||||
next[k] = v
|
|
||||||
}
|
|
||||||
delete(next, whitelistKey(email, platform))
|
|
||||||
atomic.StorePointer(from, unsafe.Pointer(&next))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *whitelist) add(m *whitelistmember) {
|
|
||||||
addToUnsafePointer(&wl.emailptr, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *whitelist) remove(email string, platform string) {
|
|
||||||
removeFromUnsafePointer(&wl.emailptr, email, platform)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wl *whitelist) isMember(email string, platform string) bool {
|
|
||||||
ptr := atomic.LoadPointer(&wl.emailptr)
|
|
||||||
src := *(*map[string]*whitelistmember)(ptr)
|
|
||||||
|
|
||||||
_, exists := src[whitelistKey(email, platform)]
|
|
||||||
return exists
|
|
||||||
}
|
|
||||||
|
|
||||||
type DivisionStateName string
|
type DivisionStateName string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -134,7 +111,8 @@ type serviceDescription struct {
|
|||||||
VersionSplits map[string]string `bson:"version_splits" json:"version_splits"`
|
VersionSplits map[string]string `bson:"version_splits" json:"version_splits"`
|
||||||
|
|
||||||
auths *gocommon.AuthCollection
|
auths *gocommon.AuthCollection
|
||||||
wl *whitelist
|
wl memberContainerPtr[string, *whitelistmember]
|
||||||
|
bl memberContainerPtr[primitive.ObjectID, *blockinfo]
|
||||||
mongoClient gocommon.MongoClient
|
mongoClient gocommon.MongoClient
|
||||||
sessionTTL time.Duration
|
sessionTTL time.Duration
|
||||||
|
|
||||||
@ -280,10 +258,12 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
|||||||
sh.updateUserinfo = mg.updateUserinfo
|
sh.updateUserinfo = mg.updateUserinfo
|
||||||
sh.getProviderInfo = mg.getProviderInfo
|
sh.getProviderInfo = mg.getProviderInfo
|
||||||
|
|
||||||
sh.wl = &mg.wl
|
sh.wl = mg.wl
|
||||||
|
sh.bl = mg.bl
|
||||||
sh.serviceSummarySerialized, _ = json.Marshal(sh.ServiceDescriptionSummary)
|
sh.serviceSummarySerialized, _ = json.Marshal(sh.ServiceDescriptionSummary)
|
||||||
|
sh.serviceSerialized, _ = json.Marshal(sh)
|
||||||
|
|
||||||
logger.Println("service is ready :", sh.ServiceCode, string(sh.divisionsSerialized))
|
logger.Println("service is ready :", sh.ServiceCode, string(sh.serviceSerialized))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -721,28 +701,16 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
|||||||
oldcreate := account["create"].(primitive.DateTime)
|
oldcreate := account["create"].(primitive.DateTime)
|
||||||
newaccount := oldcreate == createtime
|
newaccount := oldcreate == createtime
|
||||||
|
|
||||||
var bi blockinfo
|
var bi *blockinfo
|
||||||
if err := sh.mongoClient.FindOneAs(CollectionBlock, bson.M{
|
if sh.bl.contains(accid, &bi) {
|
||||||
"code": sh.ServiceCode,
|
// 블럭된 계정. 블락 정보를 알려준다.
|
||||||
"accid": accid,
|
w.Header().Add("MG-ACCOUNTBLOCK-START", strconv.FormatInt(bi.Start.Time().Unix(), 10))
|
||||||
}, &bi); err != nil {
|
w.Header().Add("MG-ACCOUNTBLOCK-END", strconv.FormatInt(bi.End.Time().Unix(), 10))
|
||||||
logger.Error("authorize failed. find blockinfo in CollectionBlock err:", err)
|
w.Header().Add("MG-ACCOUNTBLOCK-REASON", bi.Reason)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bi.Start.Time().IsZero() {
|
|
||||||
now := time.Now().UTC()
|
|
||||||
if bi.Start.Time().Before(now) && bi.End.Time().After(now) {
|
|
||||||
// block됐네?
|
|
||||||
// status는 정상이고 reason을 넘겨주자
|
|
||||||
json.NewEncoder(w).Encode(map[string]any{
|
|
||||||
"blocked": bi,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newsession := primitive.NewObjectID()
|
newsession := primitive.NewObjectID()
|
||||||
expired := primitive.NewDateTimeFromTime(time.Now().UTC().Add(sh.sessionTTL))
|
expired := primitive.NewDateTimeFromTime(time.Now().UTC().Add(sh.sessionTTL))
|
||||||
newauth := gocommon.Authinfo{
|
newauth := gocommon.Authinfo{
|
||||||
@ -769,6 +737,9 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
|||||||
"newAccount": newaccount,
|
"newAccount": newaccount,
|
||||||
"accid": newauth.Accid.Hex(),
|
"accid": newauth.Accid.Hex(),
|
||||||
}
|
}
|
||||||
|
if *noauth {
|
||||||
|
output["noauth"] = true
|
||||||
|
}
|
||||||
bt, _ := json.Marshal(output)
|
bt, _ := json.Marshal(output)
|
||||||
w.Write(bt)
|
w.Write(bt)
|
||||||
} else if len(session) > 0 {
|
} else if len(session) > 0 {
|
||||||
@ -971,7 +942,8 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request)
|
|||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sh.wl.isMember(cell.ToAuthinfo().Email, cell.ToAuthinfo().Platform) {
|
wm := &whitelistmember{Email: cell.ToAuthinfo().Email, Platform: cell.ToAuthinfo().Platform}
|
||||||
|
if sh.wl.contains(wm.Key(), nil) {
|
||||||
// qa 권한이면 입장 가능
|
// qa 권한이면 입장 가능
|
||||||
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
|
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
|
||||||
} else if div.Maintenance != nil {
|
} else if div.Maintenance != nil {
|
||||||
|
|||||||
@ -43,102 +43,6 @@ type filePipelineDocument struct {
|
|||||||
File *FileDocumentDesc `bson:"fullDocument"`
|
File *FileDocumentDesc `bson:"fullDocument"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type whilelistPipelineDocument struct {
|
|
||||||
OperationType string `bson:"operationType"`
|
|
||||||
DocumentKey struct {
|
|
||||||
Id primitive.ObjectID `bson:"_id"`
|
|
||||||
} `bson:"documentKey"`
|
|
||||||
Member *whitelistmember `bson:"fullDocument"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mg *Maingate) watchWhitelistCollection(parentctx context.Context) {
|
|
||||||
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: "operationType", Value: 1},
|
|
||||||
{Key: "fullDocument", Value: 1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream *mongo.ChangeStream
|
|
||||||
var err error
|
|
||||||
var ctx context.Context
|
|
||||||
|
|
||||||
for {
|
|
||||||
if stream == nil {
|
|
||||||
stream, err = mg.mongoClient.Watch(CollectionWhitelist, mongo.Pipeline{matchStage, projectStage})
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("watchWhitelistCollection watch failed :", err)
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ctx = context.TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
changed := stream.TryNext(ctx)
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
logger.Error("watchWhitelistCollection stream.TryNext failed. process should be restarted! :", ctx.Err().Error())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed {
|
|
||||||
var data whilelistPipelineDocument
|
|
||||||
if err := stream.Decode(&data); err == nil {
|
|
||||||
ot := data.OperationType
|
|
||||||
switch ot {
|
|
||||||
case "insert":
|
|
||||||
// 새 화이트리스트 멤버
|
|
||||||
mg.service().wl.add(data.Member)
|
|
||||||
case "update":
|
|
||||||
if data.Member.Expired != 0 {
|
|
||||||
logger.Println("whitelist member is removed :", *data.Member)
|
|
||||||
mg.service().wl.remove(data.Member.Email, data.Member.Platform)
|
|
||||||
} else {
|
|
||||||
logger.Println("whitelist member is updated :", *data.Member)
|
|
||||||
mg.service().wl.add(data.Member)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Error("watchWhitelistCollection stream.Decode failed :", err)
|
|
||||||
}
|
|
||||||
} else if stream.Err() != nil || stream.ID() == 0 {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
logger.Println("watchWhitelistCollection is done")
|
|
||||||
stream.Close(ctx)
|
|
||||||
return
|
|
||||||
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
logger.Error("watchWhitelistCollection stream error :", stream.Err())
|
|
||||||
stream.Close(ctx)
|
|
||||||
stream = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *http.ServeMux, prefix string) {
|
func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *http.ServeMux, prefix string) {
|
||||||
defer func() {
|
defer func() {
|
||||||
s := recover()
|
s := recover()
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
|||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
go.mongodb.org/mongo-driver v1.11.7
|
go.mongodb.org/mongo-driver v1.11.7
|
||||||
google.golang.org/api v0.128.0
|
google.golang.org/api v0.128.0
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230801051747-b501160efc3b
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
6
go.sum
6
go.sum
@ -292,3 +292,9 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230710085810-8173216e9574 h1
|
|||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230710085810-8173216e9574/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230710085810-8173216e9574/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230801051747-b501160efc3b h1:yV1cBeu0GFxkDD6TDxzKv/rM3OMtyt1JXpeqDF5IO3Y=
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230801051747-b501160efc3b h1:yV1cBeu0GFxkDD6TDxzKv/rM3OMtyt1JXpeqDF5IO3Y=
|
||||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230801051747-b501160efc3b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230801051747-b501160efc3b/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230823084014-c34045e215fc h1:/nFKyjpcfMCdC7vrEZ7+IQOA5RoMmcBUHNRl40JN3ys=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230823084014-c34045e215fc/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230823134414-400c7f644333 h1:3QWHeK6eX1yhaeN/Lu88N4B2ORb/PdBkXUS+HzFOWgU=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230823134414-400c7f644333/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff h1:nTOqgPSfm0EANR1SFAi+Zi/KErAAlstVcEWWOnyDT5g=
|
||||||
|
repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
|
||||||
|
|||||||
Reference in New Issue
Block a user