Compare commits
103 Commits
cb793092e9
...
kd-live
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d060fe117 | |||
| 231b1a35d5 | |||
| 845784d204 | |||
| 424c8be420 | |||
| ce39e8d878 | |||
| 54c727117b | |||
| 7816fd56c0 | |||
| 564a2ee14a | |||
| cbcae00c77 | |||
| 28dc84769d | |||
| 0c678947cd | |||
| dbd0a7bf5f | |||
| 86247e053d | |||
| 757c6d312c | |||
| e37a974d9c | |||
| 470591cb44 | |||
| 38114769b3 | |||
| b05473a1c6 | |||
| 0fff694e8a | |||
| d8713298c4 | |||
| 763b0fc4bd | |||
| d8e18d7ffc | |||
| 381f1edb80 | |||
| a8e448b8ca | |||
| 9b0e3a5d5b | |||
| 5757a81cb8 | |||
| c4b8e630dc | |||
| 43b0242652 | |||
| 893744a1ab | |||
| 192cc569b4 | |||
| 56e6608537 | |||
| 0053033e32 | |||
| d1ff6a56fc | |||
| 114461c51d | |||
| 117a3e5d90 | |||
| fafc463f2a | |||
| e2bec481f0 | |||
| 89e7d35b5a | |||
| fe662c5355 | |||
| 6767a37704 | |||
| 2ea035a43b | |||
| 2a1ad499ed | |||
| 5cc6ddc8f1 | |||
| 0e070221f8 | |||
| 1ba32aa4c9 | |||
| edb3e07329 | |||
| f5304fae80 | |||
| d9be04541b | |||
| d2e06961b9 | |||
| 28092fcf17 | |||
| a7a20aebcf | |||
| c43c10982c | |||
| 02c4f9e3d1 | |||
| 1db22730aa | |||
| 184675a9b7 | |||
| 197ee7127b | |||
| 869fa48d74 | |||
| 7470f8e001 | |||
| fc70a9482c | |||
| e3afb58634 | |||
| bafb67dabc | |||
| 9ccd97564a | |||
| 08cb989975 | |||
| 455011fd99 | |||
| 4958cb0b93 | |||
| e8832f329a | |||
| 9c14480be7 | |||
| daf3e3f027 | |||
| a8df7d54bd | |||
| 2de82b9d2a | |||
| 00c2d6e205 | |||
| 87d922c558 | |||
| 0be7adefe3 | |||
| 3a9f81f1cb | |||
| 39e1b925e5 | |||
| a97b9f0983 | |||
| 42b4ade782 | |||
| 9edea29983 | |||
| c7f073c779 | |||
| 8e908982a8 | |||
| 8126406e5f | |||
| 6b68d918ba | |||
| fb5db25dce | |||
| f79e922fa0 | |||
| 3216e2620a | |||
| d796958d5e | |||
| f66904d428 | |||
| 22ec115b35 | |||
| 8d70777269 | |||
| 15ead6b0bc | |||
| 5abb3fd2b7 | |||
| 97636ce31d | |||
| ed85918e7f | |||
| c5ec99d3e1 | |||
| 2b4becdb61 | |||
| 4af93b3d7e | |||
| 429bbd1e7a | |||
| afc3a10f51 | |||
| 1c397da77a | |||
| 3834ca2e37 | |||
| 0898213aa8 | |||
| 5502c4d744 | |||
| cb08ecb53a |
37
backup/firebase-jssdk/fb-ga.js
Normal file
37
backup/firebase-jssdk/fb-ga.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Import the functions you need from the SDKs you need
|
||||
import { initializeApp } from './firebase-app.js';
|
||||
import { getAnalytics, logEvent } from './firebase-analytics.js';
|
||||
|
||||
// TODO: Add SDKs for Firebase products that you want to use
|
||||
// https://firebase.google.com/docs/web/setup#available-libraries
|
||||
|
||||
// Your web app's Firebase configuration
|
||||
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
||||
const firebaseConfig = {
|
||||
apiKey: "{{.FBA_apiKey}}",
|
||||
authDomain: "{{.FBA_authDomain}}",
|
||||
databaseURL: "{{.FBA_databaseURL}}",
|
||||
projectId: "{{.FBA_projectId}}",
|
||||
storageBucket: "{{.FBA_storageBucket}}",
|
||||
messagingSenderId: "{{.FBA_messagingSenderId}}",
|
||||
appId: "{{.FBA_appId}}",
|
||||
measurementId: "{{.FBA_measurementId}}"
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Initialize Firebase
|
||||
const app = initializeApp(firebaseConfig);
|
||||
const analytics = getAnalytics(app);
|
||||
|
||||
// LogEvent('DESKTOP_TEST8');
|
||||
|
||||
export function LogEvent(args){
|
||||
|
||||
if ( arguments.length == 1) {
|
||||
logEvent(analytics, arguments[0]);
|
||||
} else {
|
||||
logEvent(analytics, arguments[0], arguments[1]);
|
||||
}
|
||||
|
||||
}
|
||||
1
backup/firebase-jssdk/fb-ga.min.js
vendored
Normal file
1
backup/firebase-jssdk/fb-ga.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2376
backup/firebase-jssdk/fb-ga.rollup.js
Normal file
2376
backup/firebase-jssdk/fb-ga.rollup.js
Normal file
File diff suppressed because one or more lines are too long
3
backup/firebase-jssdk/firebase-analytics.js
Normal file
3
backup/firebase-jssdk/firebase-analytics.js
Normal file
File diff suppressed because one or more lines are too long
2522
backup/firebase-jssdk/firebase-app.js
Normal file
2522
backup/firebase-jssdk/firebase-app.js
Normal file
File diff suppressed because it is too large
Load Diff
537
backup/firebase-jssdk/js.js
Normal file
537
backup/firebase-jssdk/js.js
Normal file
File diff suppressed because one or more lines are too long
3
backup/firebase-jssdk/original/firebase-analytics.js
Normal file
3
backup/firebase-jssdk/original/firebase-analytics.js
Normal file
File diff suppressed because one or more lines are too long
2522
backup/firebase-jssdk/original/firebase-app.js
Normal file
2522
backup/firebase-jssdk/original/firebase-app.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,8 @@
|
||||
{
|
||||
"maingate_mongodb_url": "mongodb://...",
|
||||
"autologin_ttl": 604800,
|
||||
"acc_del_ttl": 7776000,
|
||||
"maximum_num_link_account": 10,
|
||||
"redirect_base_url": "",
|
||||
"google_client_id" : "",
|
||||
"google_client_secret" : "",
|
||||
@ -24,6 +26,15 @@
|
||||
|
||||
"firebase_admin_sdk_credentialfile": "",
|
||||
|
||||
"firebase_google_analytics_jssdk_apikey": "",
|
||||
"firebase_google_analytics_jssdk_authdomain": "",
|
||||
"firebase_google_analytics_jssdk_databaseurl": "",
|
||||
"firebase_google_analytics_jssdk_projectid": "",
|
||||
"firebase_google_analytics_jssdk_storagebucket": "",
|
||||
"firebase_google_analytics_jssdk_messagingsenderid": "",
|
||||
"firebase_google_analytics_jssdk_apiid": "",
|
||||
"firebase_google_analytics_jssdk_measurementid": "",
|
||||
|
||||
"maingate_global_admins" : [
|
||||
"mountain@action2quare.com"
|
||||
]
|
||||
|
||||
336
core/api.go
336
core/api.go
@ -2,9 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -18,7 +16,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
common "repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@ -27,7 +25,6 @@ import (
|
||||
)
|
||||
|
||||
type FileDocumentDesc struct {
|
||||
Service string `bson:"service" json:"service"`
|
||||
Key string `bson:"key" json:"key"`
|
||||
Src string `bson:"src" json:"src"`
|
||||
Link string `bson:"link" json:"link"`
|
||||
@ -66,48 +63,16 @@ func (fd *FileDocumentDesc) Save() error {
|
||||
if fd.Extract {
|
||||
switch path.Ext(destFile) {
|
||||
case ".zip":
|
||||
err = common.Unzip(destFile)
|
||||
err = gocommon.Unzip(destFile)
|
||||
case ".tar":
|
||||
err = common.Untar(destFile)
|
||||
err = gocommon.Untar(destFile)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (caller apiCaller) isAdmin() bool {
|
||||
if *devflag {
|
||||
return true
|
||||
}
|
||||
|
||||
v, ok := caller.userinfo["email"]
|
||||
if !ok {
|
||||
logger.Println("isVaidUser failed. email is missing :", caller.userinfo)
|
||||
return false
|
||||
}
|
||||
|
||||
email := v.(string)
|
||||
if _, ok := caller.globalAdmins[email]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return caller.mg.service().isAdmin(email)
|
||||
}
|
||||
|
||||
func (caller apiCaller) isAdminOrValidToken() bool {
|
||||
if caller.isAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
return caller.mg.service().isValidToken(caller.apiToken)
|
||||
}
|
||||
|
||||
func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
if r.Method == "GET" {
|
||||
// if !caller.isAdminOrValidToken() {
|
||||
// w.WriteHeader(http.StatusUnauthorized)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
allfiles, err := caller.mg.mongoClient.All(CollectionFile, options.Find().SetProjection(bson.M{
|
||||
"contents": 0,
|
||||
}).SetReturnKey(false))
|
||||
@ -126,11 +91,6 @@ func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if !caller.isAdminOrValidToken() {
|
||||
// w.WriteHeader(http.StatusUnauthorized)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
_, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{
|
||||
"key": key,
|
||||
})
|
||||
@ -147,11 +107,6 @@ var seq = uint32(0)
|
||||
|
||||
func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
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")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@ -167,17 +122,16 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
var b [5]byte
|
||||
binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix()))
|
||||
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
|
||||
if extract {
|
||||
link = path.Join("static", subfolder, rf)
|
||||
link = path.Join("static", rf)
|
||||
} else {
|
||||
link = path.Join("static", subfolder, rf, header.Filename)
|
||||
link = path.Join("static", rf, header.Filename)
|
||||
}
|
||||
|
||||
newdoc := FileDocumentDesc{
|
||||
@ -188,12 +142,10 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
Link: link,
|
||||
Desc: desc,
|
||||
Key: rf,
|
||||
Service: servicename,
|
||||
}
|
||||
_, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{
|
||||
"_id": newidobj,
|
||||
"service": servicename,
|
||||
"key": rf,
|
||||
"_id": newidobj,
|
||||
"key": rf,
|
||||
}, newdoc)
|
||||
|
||||
if err == nil {
|
||||
@ -206,46 +158,81 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
|
||||
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
|
||||
queryvals := r.URL.Query()
|
||||
if r.Method == "GET" {
|
||||
// if !caller.isAdminOrValidToken() {
|
||||
// logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
|
||||
// w.WriteHeader(http.StatusUnauthorized)
|
||||
// return nil
|
||||
// }
|
||||
json.NewEncoder(w).Encode(mg.bl.all())
|
||||
} else if r.Method == "PUT" {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(all) > 0 {
|
||||
var notexp []primitive.M
|
||||
for _, v := range all {
|
||||
if _, exp := v["_ts"]; !exp {
|
||||
notexp = append(notexp, v)
|
||||
}
|
||||
}
|
||||
allraw, _ := json.Marshal(notexp)
|
||||
w.Write(allraw)
|
||||
bi := blockinfo{
|
||||
Start: primitive.NewDateTimeFromTime(time.Unix(bipl.StartUnix, 0)),
|
||||
End: primitive.NewDateTimeFromTime(time.Unix(bipl.EndUnix, 0)),
|
||||
Reason: bipl.Reason,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
} else if r.Method == "DELETE" {
|
||||
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" {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
var member whitelistmember
|
||||
if err := json.Unmarshal(body, &member); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if !caller.isAdminOrValidToken() {
|
||||
// logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
|
||||
// w.WriteHeader(http.StatusUnauthorized)
|
||||
// return nil
|
||||
// }
|
||||
|
||||
member.Expired = 0
|
||||
|
||||
member.ExpiredAt = 0
|
||||
member.Id = primitive.NilObjectID
|
||||
_, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{
|
||||
"_id": primitive.NewObjectID(),
|
||||
}, bson.M{
|
||||
@ -256,7 +243,8 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
|
||||
return err
|
||||
}
|
||||
} else if r.Method == "DELETE" {
|
||||
id := queryvals.Get("id")
|
||||
id := r.URL.Query().Get("id")
|
||||
|
||||
if len(id) == 0 {
|
||||
return errors.New("id param is missing")
|
||||
}
|
||||
@ -297,9 +285,7 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
||||
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(&newService))
|
||||
}
|
||||
|
||||
serptr := atomic.LoadPointer(&mg.service().serviceSerialized)
|
||||
logger.Println(" loadPointer :", string(*(*[]byte)(serptr)))
|
||||
w.Write(*(*[]byte)(serptr))
|
||||
w.Write(mg.service().serviceSerialized)
|
||||
} else if r.Method == "POST" {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
var service serviceDescription
|
||||
@ -334,8 +320,7 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
||||
func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
mg := caller.mg
|
||||
if r.Method == "GET" {
|
||||
serptr := atomic.LoadPointer(&mg.service().divisionsSerialized)
|
||||
w.Write(*(*[]byte)(serptr))
|
||||
w.Write(mg.service().divisionsSerialized)
|
||||
} else if r.Method == "POST" {
|
||||
var divs map[string]*Division
|
||||
dec := json.NewDecoder(r.Body)
|
||||
@ -359,6 +344,43 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
|
||||
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)
|
||||
}
|
||||
|
||||
case "DELETE":
|
||||
// 쿠폰 삭제
|
||||
logger.Println("begin deleteCoupon")
|
||||
deleteCoupon(caller.mg.mongoClient, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errApiTokenMissing = errors.New("mg-x-api-token is missing")
|
||||
|
||||
func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
@ -379,6 +401,40 @@ func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (caller apiCaller) lockcreatecharAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
mg, err := caller.mg.mongoClient.FindAll(CollectionService, bson.M{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
haschr, _ := gocommon.ReadStringFormValue(r.Form, "haschr")
|
||||
|
||||
locked := make(map[string]any)
|
||||
if haschr == "true" {
|
||||
locked["lock"] = false
|
||||
} else {
|
||||
curregion, _ := gocommon.ReadStringFormValue(r.Form, "region")
|
||||
|
||||
for _, regioninfo := range mg {
|
||||
region := regioninfo["divisions"].(primitive.M)
|
||||
for idx, rl := range region {
|
||||
if idx == curregion {
|
||||
if rl.(primitive.M)["lockcreatechar"].(bool) {
|
||||
locked["lock"] = true
|
||||
} else {
|
||||
locked["lock"] = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create, _ := json.Marshal(locked)
|
||||
w.Write(create)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type apiCaller struct {
|
||||
userinfo map[string]any
|
||||
globalAdmins map[string]bool
|
||||
@ -399,62 +455,68 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body.Close()
|
||||
}()
|
||||
|
||||
r.ParseMultipartForm(32 << 20)
|
||||
|
||||
var userinfo map[string]any
|
||||
|
||||
if !*devflag {
|
||||
authheader := r.Header.Get("Authorization")
|
||||
if len(authheader) == 0 {
|
||||
logger.Println("Authorization header is not valid :", authheader)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://graph.microsoft.com/oidc/userinfo", nil)
|
||||
req.Header.Add("Authorization", authheader)
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Println("graph microsoft api call failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
if err = json.Unmarshal(raw, &userinfo); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, expired := userinfo["error"]; expired {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ptr := atomic.LoadPointer(&mg.admins)
|
||||
adminsptr := (*globalAdmins)(ptr)
|
||||
|
||||
if adminsptr.modtime != common.ConfigModTime() {
|
||||
var config globalAdmins
|
||||
if err := common.LoadConfig(&config); err == nil {
|
||||
config.parse()
|
||||
adminsptr = &config
|
||||
atomic.StorePointer(&mg.admins, unsafe.Pointer(adminsptr))
|
||||
}
|
||||
}
|
||||
|
||||
var apiTokenObj primitive.ObjectID
|
||||
if !*devflag {
|
||||
apiToken := r.Header.Get("MG-X-API-TOKEN")
|
||||
if len(apiToken) > 0 {
|
||||
if apiToken != mg.maingateConfig.ApiToken {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
obj, err := primitive.ObjectIDFromHex(apiToken)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
apiTokenObj = obj
|
||||
} else {
|
||||
authheader := r.Header.Get("Authorization")
|
||||
if len(authheader) == 0 {
|
||||
logger.Println("Authorization header is not valid :", authheader)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://graph.microsoft.com/oidc/userinfo", nil)
|
||||
req.Header.Add("Authorization", authheader)
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Println("graph microsoft api call failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
if err = json.Unmarshal(raw, &userinfo); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, expired := userinfo["error"]; expired {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr := atomic.LoadPointer(&mg.admins)
|
||||
adminsptr := (*globalAdmins)(ptr)
|
||||
|
||||
if adminsptr.modtime != gocommon.ConfigModTime() {
|
||||
var config globalAdmins
|
||||
if err := gocommon.LoadConfig(&config); err == nil {
|
||||
config.parse()
|
||||
adminsptr = &config
|
||||
atomic.StorePointer(&mg.admins, unsafe.Pointer(adminsptr))
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,6 +541,12 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
|
||||
err = caller.maintenanceAPI(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/files") {
|
||||
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)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/lockcreatechar") {
|
||||
err = caller.lockcreatecharAPI(w, r)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
407
core/api_coupon.go
Normal file
407
core/api_coupon.go
Normal file
@ -0,0 +1,407 @@
|
||||
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"`
|
||||
Expire int64 `json:"expire" bson:"expire"`
|
||||
}
|
||||
|
||||
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")
|
||||
expire, _ := gocommon.ReadIntegerFormValue(r.Form, "expire")
|
||||
|
||||
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,
|
||||
Expire: expire,
|
||||
},
|
||||
}, 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,
|
||||
Expire: expire,
|
||||
},
|
||||
}, 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 {
|
||||
decUid, err := hex.DecodeString(uid)
|
||||
if err != nil {
|
||||
logger.Println("downloadCoupons Fail", err)
|
||||
continue
|
||||
}
|
||||
coupons = append(coupons, makeCouponKey(roundnum, decUid))
|
||||
}
|
||||
|
||||
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, "expire": 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)
|
||||
}
|
||||
|
||||
// 쿠폰 사용 유무 검사
|
||||
alreadyused, err := mongoClient.Exists(CollectionCouponUse, bson.M{
|
||||
"_id": acc,
|
||||
"rounds": round,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if alreadyused {
|
||||
// 이미 이 라운드의 쿠폰을 사용한 적이 있다.
|
||||
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, "total": 1, "expire": 1})); err != nil {
|
||||
logger.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if coupon.Total > 0 {
|
||||
// 무한 쿠폰 아니네?
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 쿠폰을 하나 꺼냄
|
||||
matched, _, err := mongoClient.Update(CollectionCoupon, bson.M{
|
||||
"_id": roundObj,
|
||||
"remains": key,
|
||||
}, 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
|
||||
}
|
||||
|
||||
// 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, "expire": 1})); err != nil {
|
||||
logger.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if coupon.Expire < time.Now().Unix() {
|
||||
// 쿠폰 만료시간 경과
|
||||
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))
|
||||
}
|
||||
|
||||
func deleteCoupon(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
|
||||
code, _ := gocommon.ReadStringFormValue(r.Form, "name")
|
||||
if len(code) == 0 {
|
||||
logger.Println("coupon delete code error")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := mongoClient.Delete(CollectionCoupon, bson.M{
|
||||
"name": code,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Println("coupon delete error")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
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/mountain-maingate?replicaSet=rs0&retrywrites=true", "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))
|
||||
}
|
||||
189
core/maingate.go
189
core/maingate.go
@ -13,8 +13,10 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"text/template"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
@ -51,6 +53,7 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
AuthPlatformSteamSDK = "steam"
|
||||
AuthPlatformFirebaseAuth = "firebase"
|
||||
AuthPlatformGoogle = "google"
|
||||
AuthPlatformMicrosoft = "microsoft"
|
||||
@ -120,7 +123,10 @@ func makeAuthCollection(mongoClient gocommon.MongoClient, sessionTTL time.Durati
|
||||
type maingateConfig struct {
|
||||
Mongo string `json:"maingate_mongodb_url"`
|
||||
SessionTTL int64 `json:"maingate_session_ttl"`
|
||||
ApiToken string `json:"maingate_api_token"`
|
||||
Autologin_ttl int64 `json:"autologin_ttl"`
|
||||
AccDelTTL int64 `json:"acc_del_ttl"`
|
||||
MaximumNumLinkAccount int64 `json:"maximum_num_link_account"`
|
||||
RedirectBaseUrl string `json:"redirect_base_url"`
|
||||
GoogleClientId string `json:"google_client_id"`
|
||||
GoogleClientSecret string `json:"google_client_secret"`
|
||||
@ -138,6 +144,20 @@ type maingateConfig struct {
|
||||
GamepotProjectId string `json:"gamepot_project_id"`
|
||||
GamepotLoginCheckAPIURL string `json:"gamepot_logincheckapi_url"`
|
||||
FirebaseAdminSDKCredentialFile string `json:"firebase_admin_sdk_credentialfile"`
|
||||
SteamAppId string `json:"steam_app_id"`
|
||||
SteamPublisherAuthKey string `json:"steam_publisher_authkey"`
|
||||
Firebase_Google_Analytics_JS_SDK_Config
|
||||
}
|
||||
|
||||
type Firebase_Google_Analytics_JS_SDK_Config struct {
|
||||
FGA_apiKey string `json:"firebase_google_analytics_jssdk_apikey"`
|
||||
FGA_authDomain string `json:"firebase_google_analytics_jssdk_authdomain"`
|
||||
FGA_databaseURL string `json:"firebase_google_analytics_jssdk_databaseurl"`
|
||||
FGA_projectId string `json:"firebase_google_analytics_jssdk_projectid"`
|
||||
FGA_storageBucket string `json:"firebase_google_analytics_jssdk_storagebucket"`
|
||||
FGA_messagingSenderId string `json:"firebase_google_analytics_jssdk_messagingsenderid"`
|
||||
FGA_appId string `json:"firebase_google_analytics_jssdk_apiid"`
|
||||
FGA_measurementId string `json:"ffirebase_google_analytics_jssdk_measurementid"`
|
||||
}
|
||||
|
||||
type globalAdmins struct {
|
||||
@ -165,7 +185,8 @@ type Maingate struct {
|
||||
//services servicelist
|
||||
serviceptr unsafe.Pointer
|
||||
admins unsafe.Pointer
|
||||
wl whitelist
|
||||
wl memberContainerPtr[string, *whitelistmember]
|
||||
bl memberContainerPtr[primitive.ObjectID, *blockinfo]
|
||||
|
||||
tokenEndpoints map[string]string
|
||||
authorizationEndpoints map[string]string
|
||||
@ -202,7 +223,6 @@ func New(ctx context.Context) (*Maingate, error) {
|
||||
|
||||
err := mg.prepare(ctx)
|
||||
if err != nil {
|
||||
logger.Error("mg.prepare() failed :", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -282,100 +302,107 @@ 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) {
|
||||
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 {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
// redis에서 env를 가져온 후에
|
||||
mg.mongoClient, err = gocommon.NewMongoClient(context, mg.Mongo, "maingate")
|
||||
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
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{
|
||||
"skonly": {{Key: "sk", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
||||
"platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
|
||||
"emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeIndices(CollectionWhitelist, map[string]bson.D{
|
||||
"service": {{Key: "service", Value: 1}},
|
||||
if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{
|
||||
"accid": {{Key: "accid", 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
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeExpireIndex(CollectionAccount, int32(mg.AccDelTTL)); err != nil {
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeExpireIndex(CollectionLink, int32(mg.AccDelTTL)); err != nil {
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
// Delete대신 _ts로 expire시킴. pipeline에 삭제 알려주기 위함
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionBlock, map[string]bson.D{
|
||||
"codeaccid": {{Key: "code", Value: 1}, {Key: "accid", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
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{
|
||||
"platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
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{
|
||||
"platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{
|
||||
"gamepotuserid": {{Key: "gamepotuserid", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{
|
||||
"firebaseuserid": {{Key: "firebaseuserid", Value: 1}},
|
||||
}); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second)))
|
||||
@ -387,7 +414,7 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
|
||||
if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{
|
||||
"link": 1,
|
||||
})); err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
|
||||
for _, pre := range preall {
|
||||
@ -402,35 +429,33 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
|
||||
"_id": pre.Id,
|
||||
}, &fulldoc)
|
||||
if err != nil {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
err = fulldoc.Save()
|
||||
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 {
|
||||
return err
|
||||
return makeErrorWithStack(err)
|
||||
}
|
||||
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 mg.watchWhitelistCollection(context)
|
||||
go mg.wl.watchCollection(context, CollectionWhitelist, mg.mongoClient)
|
||||
go mg.bl.watchCollection(context, CollectionBlock, mg.mongoClient)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func whitelistKey(email string) string {
|
||||
if strings.HasPrefix(email, "*@") {
|
||||
// 도메인 전체 허용
|
||||
return email[2:]
|
||||
}
|
||||
|
||||
return email
|
||||
}
|
||||
|
||||
func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
|
||||
var allServices []*serviceDescription
|
||||
if err := mg.mongoClient.AllAs(CollectionService, &allServices, options.Find().SetReturnKey(false)); err != nil {
|
||||
@ -467,8 +492,9 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
|
||||
empty.Divisions = map[string]*Division{
|
||||
host: {
|
||||
DivisionForUser: DivisionForUser{
|
||||
Priority: 0,
|
||||
State: DivisionState_FullOpen,
|
||||
Priority: 0,
|
||||
State: DivisionState_FullOpen,
|
||||
LockCreateChar: false,
|
||||
},
|
||||
|
||||
Url: fmt.Sprintf("http://%s/warehouse", ipaddr),
|
||||
@ -490,7 +516,9 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
|
||||
}
|
||||
|
||||
logger.Println("Service is registered :", mg.service().ServiceCode)
|
||||
serveMux.Handle(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), mg.service())
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), func(w http.ResponseWriter, r *http.Request) {
|
||||
mg.service().serveHTTP(w, r)
|
||||
})
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "api/"), mg.api)
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "query/"), mg.query)
|
||||
|
||||
@ -534,10 +562,22 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
|
||||
}
|
||||
|
||||
cfsx := http.FileServer(http.Dir("console"))
|
||||
serveMux.Handle(prefix+"/console/", http.StripPrefix(prefix+"/console/", cfsx))
|
||||
pattern := gocommon.MakeHttpHandlerPattern(prefix, "console", "/")
|
||||
serveMux.Handle(pattern, http.StripPrefix(pattern, cfsx))
|
||||
logger.Println("maingate console registered :", pattern)
|
||||
|
||||
staticfs := http.FileServer(http.Dir("static"))
|
||||
serveMux.Handle(prefix+"/static/", http.StripPrefix(prefix+"/static/", staticfs))
|
||||
pattern = gocommon.MakeHttpHandlerPattern(prefix, "static", "/")
|
||||
serveMux.Handle(pattern, http.StripPrefix(pattern, staticfs))
|
||||
logger.Println("maingate static registered :", pattern)
|
||||
|
||||
fbafs := http.FileServer(http.Dir("fba"))
|
||||
pattern = gocommon.MakeHttpHandlerPattern(prefix, "fba", "/")
|
||||
serveMux.Handle(pattern, http.StripPrefix(pattern, fbafs))
|
||||
logger.Println("google_analytics static registered :", pattern)
|
||||
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "fba", "fb-ga.min.js"), mg.google_analytics_js)
|
||||
logger.Println("google_analytics.js static registered :", pattern)
|
||||
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformGoogle), mg.platform_google_get_login_url)
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformGoogle), mg.platform_google_authorize)
|
||||
@ -558,6 +598,8 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformFirebaseAuth), mg.platform_firebaseauth_get_login_url)
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_sdk", AuthPlatformFirebaseAuth), mg.platform_firebaseauth_authorize_sdk)
|
||||
|
||||
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_sdk", AuthPlatformSteamSDK), mg.platform_steamsdk_authorize)
|
||||
|
||||
go mg.watchServiceCollection(ctx, serveMux, prefix)
|
||||
go mg.watchFileCollection(ctx, serveMux, prefix)
|
||||
// fsx := http.FileServer(http.Dir("console"))
|
||||
@ -727,6 +769,8 @@ func (mg *Maingate) updateUserinfo(info usertokeninfo) (bool, string, string) {
|
||||
success, userid, email = mg.platform_microsoft_getuserinfo(info)
|
||||
case AuthPlatformGoogle:
|
||||
success, userid, email = mg.platform_google_getuserinfo(info)
|
||||
case AuthPlatformSteamSDK:
|
||||
success, userid, email = mg.platform_steamsdk_getuserinfo(info)
|
||||
case AuthPlatformFirebaseAuth:
|
||||
success, userid, email = mg.platform_firebase_getuserinfo(info)
|
||||
}
|
||||
@ -767,13 +811,18 @@ func (mg *Maingate) getProviderInfo(platform string, uid string) (string, string
|
||||
if provider == "" || providerid == "" {
|
||||
return "", "", errors.New("getProviderInfo - firebase info not found: " + provider + " / " + providerid)
|
||||
}
|
||||
case "":
|
||||
//guest auth
|
||||
providerid = uid
|
||||
if providerid == "" {
|
||||
return "", "", errors.New("getProviderInfo - guest provider id not found: " + provider + " / " + providerid)
|
||||
}
|
||||
default:
|
||||
provider = platform
|
||||
providerid = uid
|
||||
}
|
||||
|
||||
if provider == "" || providerid == "" {
|
||||
return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid)
|
||||
if provider == "" || providerid == "" {
|
||||
return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid)
|
||||
}
|
||||
}
|
||||
|
||||
return provider, providerid, nil
|
||||
@ -936,3 +985,31 @@ func JWTparseCode(keyurl string, code string) (string, string, string) {
|
||||
//--- nonce 체크 필요하다.
|
||||
return claims["sub"].(string), email, nonce
|
||||
}
|
||||
|
||||
func (mg *Maingate) google_analytics_html(w http.ResponseWriter, r *http.Request) {
|
||||
parsedTemplate, _ := template.ParseFiles("template/track-event.html")
|
||||
err := parsedTemplate.Execute(w, nil)
|
||||
if err != nil {
|
||||
logger.Error("Error executing template :", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (mg *Maingate) google_analytics_js(w http.ResponseWriter, r *http.Request) {
|
||||
fgaconfig := Firebase_Google_Analytics_JS_SDK_Config{
|
||||
FGA_apiKey: mg.FGA_apiKey,
|
||||
FGA_authDomain: mg.FGA_authDomain,
|
||||
FGA_databaseURL: mg.FGA_databaseURL,
|
||||
FGA_projectId: mg.FGA_projectId,
|
||||
FGA_storageBucket: mg.FGA_storageBucket,
|
||||
FGA_messagingSenderId: mg.FGA_messagingSenderId,
|
||||
FGA_appId: mg.FGA_appId,
|
||||
FGA_measurementId: mg.FGA_measurementId,
|
||||
}
|
||||
parsedTemplate, _ := template.ParseFiles("template/fb-ga.min.js")
|
||||
err := parsedTemplate.Execute(w, fgaconfig)
|
||||
if err != nil {
|
||||
logger.Error("Error executing template :", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
177
core/member_container.go
Normal file
177
core/member_container.go
Normal file
@ -0,0 +1,177 @@
|
||||
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]) 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
126
core/platformsteam.go
Normal file
126
core/platformsteam.go
Normal file
@ -0,0 +1,126 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
)
|
||||
|
||||
type SteamSDKAuthInfo struct {
|
||||
UserSteamId string `json:"steamid"`
|
||||
UserAuthToken string `json:"authtoken"`
|
||||
}
|
||||
|
||||
func (mg *Maingate) platform_steamsdk_authorize(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
brinfo, err := mg.GetUserBrowserInfo(r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var authinfo SteamSDKAuthInfo
|
||||
|
||||
err = json.NewDecoder(r.Body).Decode(&authinfo)
|
||||
if err != nil {
|
||||
logger.Println("authinfo decoding fail:", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if !*noauth {
|
||||
err = authenticateSteamUser(mg.SteamPublisherAuthKey, mg.SteamAppId, authinfo.UserSteamId, authinfo.UserAuthToken)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
acceestoken_expire_time := time.Date(2999, 1, int(time.January), 0, 0, 0, 0, time.UTC).Unix()
|
||||
|
||||
var info usertokeninfo
|
||||
info.platform = AuthPlatformSteamSDK
|
||||
info.userid = authinfo.UserSteamId
|
||||
info.token = authinfo.UserAuthToken
|
||||
info.brinfo = brinfo
|
||||
//info.accesstoken = respReferesh.AccessToken
|
||||
info.accesstoken_expire_time = acceestoken_expire_time
|
||||
mg.setUserToken(info)
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("id", authinfo.UserSteamId)
|
||||
params.Add("authtype", AuthPlatformSteamSDK)
|
||||
w.Write([]byte("?" + params.Encode()))
|
||||
//http.Redirect(w, r, "actionsquare://login?"+Result, http.StatusSeeOther)
|
||||
} else {
|
||||
logger.Println(err)
|
||||
http.Redirect(w, r, "actionsquare://error", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func authenticateSteamUser(pubkey, appid, playerid, ticket string) error {
|
||||
// Returns: The user's 64-bit SteamID if the user's ticket is valid
|
||||
url := fmt.Sprintf("https://partner.steam-api.com/ISteamUserAuth/AuthenticateUserTicket/v1/?key=%s&appid=%s&ticket=%s", pubkey, appid, ticket)
|
||||
resp, e := http.Get(url)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
|
||||
body, e := ioutil.ReadAll(resp.Body)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
// fmt.Println(url)
|
||||
// fmt.Println(string(body))
|
||||
|
||||
var doc map[string]interface{}
|
||||
if err := json.Unmarshal(body, &doc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, ok := doc["response"]; ok {
|
||||
response := v.(map[string]interface{})
|
||||
if v, ok = response["params"]; ok {
|
||||
paramsnode := v.(map[string]interface{})
|
||||
if v, ok = paramsnode["result"]; ok {
|
||||
if v.(string) == "OK" {
|
||||
if v, ok = paramsnode["steamid"]; ok {
|
||||
// playerid에는 빌드 구성 suffix가 붙어있는 상태이므로 == 비교가 아니라 HasPrefix로 비교
|
||||
if strings.HasPrefix(playerid, v.(string)) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else if v.(string) == "Invalid ticket" {
|
||||
return errors.New("steam: invalid ticket")
|
||||
}
|
||||
}
|
||||
} else if errdocraw, ok := response["error"]; ok {
|
||||
errdoc := errdocraw.(map[string]interface{})
|
||||
desc := errdoc["errordesc"].(string)
|
||||
return errors.New(desc)
|
||||
}
|
||||
}
|
||||
return errors.New("steam: response is not expected")
|
||||
}
|
||||
|
||||
func (mg *Maingate) platform_steamsdk_getuserinfo(info usertokeninfo) (bool, string, string) {
|
||||
|
||||
// Steam은 이메일 정보를 받을수 없기 때문에 dummy임시 주소 할당하여 리턴한다.
|
||||
dummyEmail := fmt.Sprintf("__dummy_%s@steamtemp__", info.userid)
|
||||
return true, info.userid, dummyEmail
|
||||
|
||||
}
|
||||
743
core/service.go
743
core/service.go
@ -1,20 +1,17 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
common "repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@ -24,21 +21,45 @@ import (
|
||||
|
||||
type blockinfo struct {
|
||||
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"`
|
||||
Accid primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
|
||||
}
|
||||
|
||||
type whitelistMemberTag = string
|
||||
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 {
|
||||
Email string `bson:"email" json:"email"`
|
||||
Platform string `bson:"platform" json:"platform"`
|
||||
Desc string `bson:"desc" json:"desc"`
|
||||
Expired primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
|
||||
Id primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
|
||||
Email string `bson:"email" json:"email"`
|
||||
Platform string `bson:"platform" json:"platform"`
|
||||
Desc string `bson:"desc" json:"desc"`
|
||||
ExpiredAt primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
|
||||
}
|
||||
|
||||
type whitelist struct {
|
||||
emailptr unsafe.Pointer
|
||||
func (wh *whitelistmember) Key() string {
|
||||
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 {
|
||||
@ -51,56 +72,6 @@ type usertokeninfo struct {
|
||||
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
|
||||
}
|
||||
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
|
||||
atomic.StorePointer(to, unsafe.Pointer(&next))
|
||||
}
|
||||
|
||||
func removeFromUnsafePointer(from *unsafe.Pointer, email 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))
|
||||
atomic.StorePointer(from, unsafe.Pointer(&next))
|
||||
}
|
||||
|
||||
func (wl *whitelist) add(m *whitelistmember) {
|
||||
addToUnsafePointer(&wl.emailptr, m)
|
||||
}
|
||||
|
||||
func (wl *whitelist) remove(email string) {
|
||||
removeFromUnsafePointer(&wl.emailptr, email)
|
||||
}
|
||||
|
||||
func (wl *whitelist) isMember(email string, platform string) bool {
|
||||
ptr := atomic.LoadPointer(&wl.emailptr)
|
||||
src := *(*map[string]*whitelistmember)(ptr)
|
||||
|
||||
if member, exists := src[whitelistKey(email)]; exists {
|
||||
return member.Platform == platform
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type DivisionStateName string
|
||||
|
||||
const (
|
||||
@ -117,9 +88,10 @@ type Maintenance struct {
|
||||
}
|
||||
|
||||
type DivisionForUser struct {
|
||||
Priority int `bson:"priority" json:"priority"`
|
||||
State DivisionStateName `bson:"state" json:"state"`
|
||||
Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"`
|
||||
Priority int `bson:"priority" json:"priority"`
|
||||
State DivisionStateName `bson:"state" json:"state"`
|
||||
LockCreateChar bool `bson:"lockcreatechar" json:"lockcreatechar"`
|
||||
Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"`
|
||||
}
|
||||
|
||||
type Division struct {
|
||||
@ -136,23 +108,24 @@ type serviceDescription struct {
|
||||
ServiceDescriptionSummary `bson:",inline" json:",inline"`
|
||||
Divisions map[string]*Division `bson:"divisions" json:"divisions"`
|
||||
ServerApiTokens []primitive.ObjectID `bson:"api_tokens" json:"api_tokens"`
|
||||
Admins []string `bson:"admins" json:"admins"`
|
||||
MaximumNumLinkAccount int64
|
||||
VersionSplits map[string]string `bson:"version_splits" json:"version_splits"`
|
||||
|
||||
auths *gocommon.AuthCollection
|
||||
wl *memberContainerPtr[string, *whitelistmember]
|
||||
bl *memberContainerPtr[primitive.ObjectID, *blockinfo]
|
||||
mongoClient gocommon.MongoClient
|
||||
sessionTTL time.Duration
|
||||
|
||||
auths *common.AuthCollection
|
||||
wl *whitelist
|
||||
mongoClient common.MongoClient
|
||||
sessionTTL time.Duration
|
||||
serviceCodeBytes []byte
|
||||
getUserBrowserInfo func(r *http.Request) (string, error)
|
||||
getUserTokenWithCheck func(platform string, userid string, brinfo string) (usertokeninfo, error)
|
||||
updateUserinfo func(info usertokeninfo) (bool, string, string)
|
||||
getProviderInfo func(platform string, uid string) (string, string, error)
|
||||
|
||||
admins unsafe.Pointer
|
||||
divisionsForUsersSerialized unsafe.Pointer
|
||||
divisionsSerialized unsafe.Pointer
|
||||
serviceSerialized unsafe.Pointer
|
||||
serviceSummarySerialized unsafe.Pointer
|
||||
divisionsSerialized []byte
|
||||
serviceSerialized []byte
|
||||
divisionsSplits map[string][]byte
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) isValidToken(apiToken primitive.ObjectID) bool {
|
||||
@ -189,7 +162,7 @@ func (sh *serviceDescription) readProfile(authtype string, id string, binfo stri
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if userinfo.token == "" {
|
||||
if len(userinfo.token) == 0 {
|
||||
return "", errors.New("refreshtoken token not found")
|
||||
}
|
||||
|
||||
@ -214,9 +187,10 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
}
|
||||
|
||||
divsForUsers := make(map[string]*DivisionForUser)
|
||||
defaultDivNames := make(map[string]bool)
|
||||
for dn, div := range divs {
|
||||
if div.State == DivisionState_Closed {
|
||||
continue
|
||||
if div.State != DivisionState_Closed {
|
||||
defaultDivNames[dn] = true
|
||||
}
|
||||
|
||||
divsForUsers[dn] = &div.DivisionForUser
|
||||
@ -235,11 +209,16 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
} else if strings.HasPrefix(div.Maintenance.Notice, "http") {
|
||||
div.Maintenance.link = div.Maintenance.Notice
|
||||
} else {
|
||||
hasher := md5.New()
|
||||
hasher.Write(sh.Id[:])
|
||||
subfolder := hex.EncodeToString(hasher.Sum(nil))[:8]
|
||||
var fd FileDocumentDesc
|
||||
if err := mg.mongoClient.FindOneAs(CollectionFile, bson.M{
|
||||
"key": div.Maintenance.Notice,
|
||||
}, &fd, options.FindOne().SetProjection(bson.M{"link": 1})); err != nil {
|
||||
logger.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
div.Maintenance.link = path.Join("static", subfolder, div.Maintenance.Notice)
|
||||
div.Maintenance.link = fd.Link
|
||||
logger.Println("div.Maintenance.link :", fd.Link)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -247,14 +226,32 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
}
|
||||
}
|
||||
|
||||
divmarshaled, _ := json.Marshal(divs)
|
||||
devstr := string(divmarshaled)
|
||||
sh.divisionsSerialized = unsafe.Pointer(&devstr)
|
||||
sh.divisionsSerialized, _ = json.Marshal(divs)
|
||||
sh.divisionsSplits = make(map[string][]byte)
|
||||
for ver, divnamesT := range sh.VersionSplits {
|
||||
if ver == "default" {
|
||||
continue
|
||||
}
|
||||
divnames := strings.Split(divnamesT, ",")
|
||||
split := make(map[string]*DivisionForUser)
|
||||
for _, divname := range divnames {
|
||||
split[divname] = divsForUsers[divname]
|
||||
|
||||
divmarshaled2, _ := json.Marshal(divsForUsers)
|
||||
devstr2 := string(divmarshaled2)
|
||||
sh.divisionsForUsersSerialized = unsafe.Pointer(&devstr2)
|
||||
// 스플릿 된 버전은 default에서 제거해야 한다.
|
||||
delete(defaultDivNames, divname)
|
||||
}
|
||||
splitMarshaled, _ := json.Marshal(split)
|
||||
sh.divisionsSplits[ver] = splitMarshaled
|
||||
}
|
||||
|
||||
defaultsDivs := make(map[string]*DivisionForUser)
|
||||
for divname := range defaultDivNames {
|
||||
defaultsDivs[divname] = divsForUsers[divname]
|
||||
}
|
||||
defaultMarshaled, _ := json.Marshal(defaultsDivs)
|
||||
sh.divisionsSplits["default"] = defaultMarshaled
|
||||
|
||||
sh.MaximumNumLinkAccount = mg.maingateConfig.MaximumNumLinkAccount
|
||||
sh.mongoClient = mg.mongoClient
|
||||
sh.auths = mg.auths
|
||||
sh.sessionTTL = time.Duration(mg.SessionTTL * int64(time.Second))
|
||||
@ -264,19 +261,11 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
sh.updateUserinfo = mg.updateUserinfo
|
||||
sh.getProviderInfo = mg.getProviderInfo
|
||||
|
||||
if sh.Admins == nil {
|
||||
sh.Admins = []string{}
|
||||
}
|
||||
sh.admins = unsafe.Pointer(&sh.Admins)
|
||||
|
||||
sh.wl = &mg.wl
|
||||
bt, _ := json.Marshal(sh)
|
||||
atomic.StorePointer(&sh.serviceSerialized, unsafe.Pointer(&bt))
|
||||
sh.bl = &mg.bl
|
||||
sh.serviceSerialized, _ = json.Marshal(sh)
|
||||
|
||||
btsum, _ := json.Marshal(sh.ServiceDescriptionSummary)
|
||||
atomic.StorePointer(&sh.serviceSummarySerialized, unsafe.Pointer(&btsum))
|
||||
|
||||
logger.Println("service is ready :", sh.ServiceCode, sh.Admins, string(divmarshaled))
|
||||
logger.Println("service is ready :", sh.ServiceCode, string(sh.serviceSerialized))
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -322,13 +311,6 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
// fmt.Println(oldAuth.Uid)
|
||||
// fmt.Println("=================")
|
||||
|
||||
//if oldAuth.Token != oldToken || oldAuth.Uid != oldId || oldAuth.Platform != oldType {
|
||||
if oldAuth.Uid != oldId || oldAuth.Platform != oldType {
|
||||
logger.Println("link failed. session key is not correct :", *oldAuth, queryvals)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
bfinfo, err := sh.getUserBrowserInfo(r)
|
||||
if err != nil {
|
||||
logger.Error("getUserBrowserInfo failed :", err)
|
||||
@ -336,16 +318,35 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sh.readProfile(oldType, oldId, bfinfo)
|
||||
guestlink := (len(oldType) == 0)
|
||||
if !guestlink {
|
||||
_, err = sh.readProfile(oldType, oldId, bfinfo)
|
||||
if err != nil {
|
||||
logger.Println("readProfile(old) failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.Println("from guest acc to real acc link : ", oldId, bfinfo, newType, newId, bfinfo)
|
||||
}
|
||||
|
||||
oldType, oldId, err = sh.getProviderInfo(oldType, oldId)
|
||||
if err != nil {
|
||||
logger.Error("readProfile(old) failed :", err)
|
||||
logger.Println("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
//if oldAuth.Token != oldToken || oldAuth.Uid != oldId || oldAuth.Platform != oldType {
|
||||
if oldAuth.Uid != oldId || oldAuth.Platform != oldType {
|
||||
logger.Println("link failed. session key is not correct :", *oldAuth, queryvals)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
email, err := sh.readProfile(newType, newId, bfinfo)
|
||||
if err != nil {
|
||||
logger.Error("readProfile(new) failed :", err)
|
||||
logger.Println("readProfile(new) failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -358,8 +359,9 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
newType, newId, err = sh.getProviderInfo(newType, newId)
|
||||
if err != nil {
|
||||
logger.Error("getProviderInfo failed :", err)
|
||||
logger.Println("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
createtime := primitive.NewDateTimeFromTime(time.Now().UTC())
|
||||
@ -388,7 +390,7 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
}, options.Update().SetUpsert(true))
|
||||
if err != nil {
|
||||
logger.Error("link failed. Update ServiceName err :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@ -400,19 +402,255 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if guestlink {
|
||||
//기존 게스트 링크 삭제
|
||||
link, err = sh.mongoClient.FindOneAndDelete(CollectionLink, bson.M{
|
||||
"platform": oldType,
|
||||
"uid": oldId,
|
||||
}, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1}))
|
||||
|
||||
if err == nil {
|
||||
sh.mongoClient.Delete(CollectionAccount, bson.M{
|
||||
"_id": link["_id"].(primitive.ObjectID),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
logger.Println("link success :", r.URL.Query())
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) isAdmin(email string) bool {
|
||||
ptr := atomic.LoadPointer(&sh.admins)
|
||||
admins := *(*[]string)(ptr)
|
||||
|
||||
for _, a := range admins {
|
||||
if a == email {
|
||||
return true
|
||||
// == link된 계정을 해제 한다. but, 최소 1개 계정은 연결되어 있어야 한다.
|
||||
func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
logger.Error(s)
|
||||
}
|
||||
}()
|
||||
|
||||
if r.Method != "GET" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
return false
|
||||
|
||||
queryvals := r.URL.Query()
|
||||
sType := queryvals.Get("stype")
|
||||
sId := queryvals.Get("sid")
|
||||
sk := queryvals.Get("sk")
|
||||
targetType := queryvals.Get("ttype")
|
||||
|
||||
authInfo := sh.auths.Find(sk)
|
||||
if authInfo == nil {
|
||||
// 잘못된 세션
|
||||
logger.Println("linkinfo failed. session key is not valid :", sk)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Println("=================")
|
||||
// fmt.Println(sType)
|
||||
// fmt.Println(sId)
|
||||
// fmt.Println("=================")
|
||||
// fmt.Println(authInfo.Platform)
|
||||
// fmt.Println(authInfo.Uid)
|
||||
// fmt.Println("=================")
|
||||
|
||||
sType, sId, err := sh.getProviderInfo(sType, sId)
|
||||
if err != nil {
|
||||
logger.Println("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if authInfo.Uid != sId || authInfo.Platform != sType {
|
||||
logger.Println("unlink failed. session key is not correct :", *authInfo, queryvals)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
accDocs, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{
|
||||
"accid": authInfo.Accid,
|
||||
}, options.Find().SetProjection(bson.M{
|
||||
"_id": 1,
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
logger.Error("unlink failed, fail to count accounts :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if len(accDocs) <= 1 {
|
||||
logger.Println("unlink failed. At least one link must be maintained. :", r.URL.Query())
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var ids primitive.A
|
||||
for _, accDoc := range accDocs {
|
||||
ids = append(ids, accDoc["_id"].(primitive.ObjectID))
|
||||
}
|
||||
|
||||
link, err := sh.mongoClient.FindOneAndDelete(CollectionLink, bson.M{
|
||||
"platform": targetType,
|
||||
"_id": bson.M{"$in": ids},
|
||||
}, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1}))
|
||||
if err != nil {
|
||||
logger.Error("unlink failed. FindOneAndDelete link err:", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
preid, err := sh.mongoClient.FindOneAndDelete(CollectionAccount, bson.M{
|
||||
"_id": link["_id"].(primitive.ObjectID),
|
||||
}, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1}))
|
||||
if err != nil {
|
||||
logger.Error("unlink failed. Delete ServiceName err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if preid == nil {
|
||||
logger.Println("unlink failed. service account not found:", r.URL.Query())
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("unlink success :", r.URL.Query())
|
||||
}
|
||||
|
||||
// == 연결된 계정 정보(숫자) 전달하는 API
|
||||
func (sh *serviceDescription) linkinfo(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
logger.Error(s)
|
||||
}
|
||||
}()
|
||||
|
||||
if r.Method != "GET" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
queryvals := r.URL.Query()
|
||||
sType := queryvals.Get("stype")
|
||||
sId := queryvals.Get("sid")
|
||||
sk := queryvals.Get("sk")
|
||||
|
||||
authInfo := sh.auths.Find(sk)
|
||||
if authInfo == nil {
|
||||
// 잘못된 세션
|
||||
logger.Println("linkinfo failed. session key is not valid :", sk)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Println("=================")
|
||||
// fmt.Println(sType)
|
||||
// fmt.Println(sId)
|
||||
// fmt.Println("=================")
|
||||
// fmt.Println(authInfo.Platform)
|
||||
// fmt.Println(authInfo.Uid)
|
||||
// fmt.Println("=================")
|
||||
|
||||
sType, sId, err := sh.getProviderInfo(sType, sId)
|
||||
if err != nil {
|
||||
logger.Println("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
//if oldAuth.Token != oldToken || oldAuth.Uid != oldId || oldAuth.Platform != oldType {
|
||||
if authInfo.Uid != sId || authInfo.Platform != sType {
|
||||
logger.Println("linkinfo failed. session key is not correct :", *authInfo, queryvals)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
platformName := "platform"
|
||||
accDocs, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{
|
||||
"accid": authInfo.Accid,
|
||||
}, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{
|
||||
"_id": 1,
|
||||
}))
|
||||
if err != nil {
|
||||
logger.Error("linkinfo failed. CountDocuments err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var ids primitive.A
|
||||
for _, accDoc := range accDocs {
|
||||
ids = append(ids, accDoc["_id"].(primitive.ObjectID))
|
||||
}
|
||||
|
||||
links, err := sh.mongoClient.FindAll(CollectionLink, bson.M{
|
||||
"_id": bson.M{"$in": ids},
|
||||
}, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{
|
||||
platformName: 1,
|
||||
}))
|
||||
if err != nil {
|
||||
logger.Error("linkinfo failed. FindAll returns err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var linkstrs []string
|
||||
for _, link := range links {
|
||||
linkstrs = append(linkstrs, link[platformName].(string))
|
||||
}
|
||||
|
||||
linkbytes, err := json.Marshal(linkstrs)
|
||||
if err != nil {
|
||||
logger.Error("linkinfo failed. json marshal fail :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("linkinfo :", linkstrs)
|
||||
w.Write(linkbytes)
|
||||
}
|
||||
|
||||
// == 계정 이메일 조회
|
||||
func (sh *serviceDescription) emailinfo(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
logger.Error(s)
|
||||
}
|
||||
}()
|
||||
|
||||
if r.Method != "GET" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
queryvals := r.URL.Query()
|
||||
sk := queryvals.Get("sk")
|
||||
|
||||
authInfo := sh.auths.Find(sk)
|
||||
if authInfo == nil {
|
||||
logger.Println(" session key is not valid :", sk)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
email := authInfo.Email
|
||||
|
||||
if strings.HasPrefix(email, "__dummy_") && strings.HasSuffix(email, "temp__") {
|
||||
email = ""
|
||||
}
|
||||
|
||||
if strings.HasSuffix(email, "@noauth.flag") || strings.HasSuffix(email, "@guest.flag") {
|
||||
email = ""
|
||||
}
|
||||
|
||||
// fmt.Println("=================")
|
||||
// fmt.Println(email)
|
||||
// fmt.Println("=================")
|
||||
//logger.Println("Email :", email)
|
||||
|
||||
w.Write([]byte(fmt.Sprintf(`{"email":"%s"}`, email)))
|
||||
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request) {
|
||||
@ -436,33 +674,36 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
var email string
|
||||
|
||||
if !*noauth {
|
||||
//email, err := sh.readProfile(authtype, uid, accesstoken)
|
||||
bfinfo, err := sh.getUserBrowserInfo(r)
|
||||
if err != nil {
|
||||
logger.Error("getUserBrowserInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(authtype) > 0 {
|
||||
//email, err := sh.readProfile(authtype, uid, accesstoken)
|
||||
bfinfo, err := sh.getUserBrowserInfo(r)
|
||||
if err != nil {
|
||||
logger.Println("getUserBrowserInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
email, err = sh.readProfile(authtype, uid, bfinfo)
|
||||
if err != nil {
|
||||
logger.Error("readProfile failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
email, err = sh.readProfile(authtype, uid, bfinfo)
|
||||
if err != nil {
|
||||
logger.Println("readProfile failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("auth success :", authtype, uid, email, session)
|
||||
newType, newId, err := sh.getProviderInfo(authtype, uid)
|
||||
if err != nil {
|
||||
logger.Println("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
newType, newId, err := sh.getProviderInfo(authtype, uid)
|
||||
if err != nil {
|
||||
logger.Error("getProviderInfo failed :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if authtype != newType || uid != newId {
|
||||
authtype = newType
|
||||
uid = newId
|
||||
logger.Println("auth success ( redirect ) :", authtype, uid, email, session)
|
||||
if authtype != newType || uid != newId {
|
||||
logger.Printf("auth success ( redirect ) : %s->%s, %s->%s, %s, %s", authtype, newType, uid, newId, email, session)
|
||||
authtype = newType
|
||||
uid = newId
|
||||
}
|
||||
} else {
|
||||
email = fmt.Sprintf("%s@guest.flag", uid)
|
||||
}
|
||||
} else {
|
||||
email = fmt.Sprintf("%s@noauth.flag", uid)
|
||||
@ -480,13 +721,15 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
"create": createtime,
|
||||
"email": email,
|
||||
},
|
||||
}, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true).SetProjection(bson.M{"_id": 1}))
|
||||
}, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true).SetProjection(bson.M{
|
||||
"_id": 1,
|
||||
"_ts": 1,
|
||||
}))
|
||||
if err != nil {
|
||||
logger.Error("authorize failed :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
linkid := link["_id"].(primitive.ObjectID)
|
||||
newaccid := primitive.NewObjectID()
|
||||
for i := 0; i < len(sh.serviceCodeBytes); i++ {
|
||||
@ -510,31 +753,19 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
oldcreate := account["create"].(primitive.DateTime)
|
||||
newaccount := oldcreate == createtime
|
||||
|
||||
var bi blockinfo
|
||||
if err := sh.mongoClient.FindOneAs(CollectionBlock, bson.M{
|
||||
"code": sh.ServiceCode,
|
||||
"accid": accid,
|
||||
}, &bi); err != nil {
|
||||
logger.Error("authorize failed. find blockinfo in CollectionBlock err:", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
var bi *blockinfo
|
||||
if sh.bl.contains(accid, &bi) {
|
||||
// 블럭된 계정. 블락 정보를 알려준다.
|
||||
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))
|
||||
w.Header().Add("MG-ACCOUNTBLOCK-REASON", bi.Reason)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
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()
|
||||
expired := primitive.NewDateTimeFromTime(time.Now().UTC().Add(sh.sessionTTL))
|
||||
newauth := common.Authinfo{
|
||||
newauth := gocommon.Authinfo{
|
||||
Accid: accid,
|
||||
ServiceCode: sh.ServiceCode,
|
||||
Platform: authtype,
|
||||
@ -552,12 +783,25 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("session created :", accid, authtype, uid, email, newsession)
|
||||
|
||||
output := map[string]any{
|
||||
"sk": newsession.Hex(),
|
||||
"expirein": sh.sessionTTL.Seconds(),
|
||||
"newAccount": newaccount,
|
||||
"accid": newauth.Accid.Hex(),
|
||||
}
|
||||
if *noauth {
|
||||
output["noauth"] = true
|
||||
}
|
||||
|
||||
if link["_ts"] != nil {
|
||||
delts := link["_ts"].(primitive.DateTime)
|
||||
if !delts.Time().IsZero() {
|
||||
// 삭제된 계정. 삭제 되었다고 알려주자
|
||||
w.Header().Add("MG-ACCOUNT-DELETED", "TRUE")
|
||||
}
|
||||
}
|
||||
bt, _ := json.Marshal(output)
|
||||
w.Write(bt)
|
||||
} else if len(session) > 0 {
|
||||
@ -573,8 +817,7 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
},
|
||||
}, options.Update().SetUpsert(false))
|
||||
if err != nil {
|
||||
logger.Error("update auth collection failed")
|
||||
logger.Error(err)
|
||||
logger.Error("update auth collection failed :", err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -589,6 +832,31 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
"sk": session,
|
||||
"expirein": sh.sessionTTL.Seconds(),
|
||||
}
|
||||
|
||||
logger.Println("session updated :", authtype, uid, session)
|
||||
|
||||
authInfo := sh.auths.Find(session)
|
||||
if authInfo == nil {
|
||||
// 잘못된 세션
|
||||
logger.Println("authorize failed. fail to find authInfo :", session)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
//혹시 삭제 된 계정 아닌지 확인해본다.
|
||||
link, err := sh.mongoClient.FindOne(CollectionLink, bson.M{
|
||||
"platform": authtype,
|
||||
"uid": uid,
|
||||
}, options.FindOne().SetProjection(bson.M{
|
||||
"_ts": 1,
|
||||
}))
|
||||
if link["_ts"] != nil {
|
||||
delts := link["_ts"].(primitive.DateTime)
|
||||
if !delts.Time().IsZero() {
|
||||
// 삭제된 계정. 삭제 되었다고 알려주자
|
||||
w.Header().Add("MG-ACCOUNT-DELETED", "TRUE")
|
||||
}
|
||||
}
|
||||
bt, _ := json.Marshal(output)
|
||||
w.Write(bt)
|
||||
} else {
|
||||
@ -601,7 +869,123 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (sh *serviceDescription) findVersionSplit(version string) []byte {
|
||||
if len(version) > 0 {
|
||||
for k, v := range sh.divisionsSplits {
|
||||
if strings.HasPrefix(version, k) {
|
||||
if version == k || version[len(k)] == '.' {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sh.divisionsSplits["default"]
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) delacc(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
logger.Error(s)
|
||||
}
|
||||
}()
|
||||
|
||||
if r.Method != "GET" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
queryvals := r.URL.Query()
|
||||
sType := queryvals.Get("stype")
|
||||
sId := queryvals.Get("sid")
|
||||
sk := queryvals.Get("sk")
|
||||
cancel := queryvals.Has("cancel")
|
||||
|
||||
authInfo := sh.auths.Find(sk)
|
||||
if authInfo == nil {
|
||||
// 잘못된 세션
|
||||
logger.Println("delacc failed. session key is not valid :", sk)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
originAuthType := sType
|
||||
sType, sId, err := sh.getProviderInfo(sType, sId)
|
||||
if err != nil {
|
||||
logger.Error("delacc failed. getProviderInfo err :", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if authInfo.Uid != sId || authInfo.Platform != sType {
|
||||
logger.Println("delacc failed. session key is not correct :", *authInfo, queryvals)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
linkidMap, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{
|
||||
"accid": authInfo.Accid,
|
||||
}, options.Find().SetProjection(bson.M{
|
||||
"_id": 1,
|
||||
}))
|
||||
if err != nil {
|
||||
logger.Error("delacc failed. FindAll account err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var linkidAry primitive.A
|
||||
for _, linkid := range linkidMap {
|
||||
linkidAry = append(linkidAry, linkid["_id"].(primitive.ObjectID))
|
||||
}
|
||||
|
||||
delfilter := primitive.M{"_id": bson.M{"$in": linkidAry}}
|
||||
var delop primitive.M
|
||||
if !cancel {
|
||||
curtime := primitive.NewDateTimeFromTime(time.Now().UTC())
|
||||
delop = primitive.M{
|
||||
"$set": primitive.M{"_ts": curtime},
|
||||
}
|
||||
|
||||
if originAuthType == AuthPlatformFirebaseAuth {
|
||||
sh.mongoClient.Delete(CollectionFirebaseUserInfo, bson.M{
|
||||
"firebaseuserid": sId,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
delfilter["platform"] = sType
|
||||
targetLinkId, err := sh.mongoClient.FindAll(CollectionLink, delfilter, options.Find().SetProjection(bson.M{
|
||||
"_id": 1,
|
||||
}))
|
||||
if len(targetLinkId) != 1 {
|
||||
logger.Error("delacc failed. FindAll link err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
delfilter = primitive.M{"_id": targetLinkId[0]["_id"].(primitive.ObjectID)}
|
||||
delop = primitive.M{
|
||||
"$unset": primitive.M{"_ts": true},
|
||||
}
|
||||
}
|
||||
updated, _, err := sh.mongoClient.Update(CollectionAccount, delfilter, delop, options.Update().SetUpsert(false))
|
||||
if !updated || err != nil {
|
||||
logger.Error("delacc failed. Update CollectionAccount timestamp err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
updated, _, err = sh.mongoClient.Update(CollectionLink, delfilter, delop, options.Update().SetUpsert(false))
|
||||
if !updated || err != nil {
|
||||
logger.Error("delacc failed. Update CollectionLink timestamp err :", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Println("delacc success :", linkidMap)
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
@ -614,13 +998,20 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
r.Body.Close()
|
||||
}()
|
||||
|
||||
logger.Println("serviceDesc :", sh.ServiceCode, r.URL.Path)
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/auth") {
|
||||
sh.authorize(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/link") {
|
||||
sh.link(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/unlink") {
|
||||
sh.unlink(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/linkinfo") {
|
||||
sh.linkinfo(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/emailinfo") {
|
||||
sh.emailinfo(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/delacc") {
|
||||
sh.delacc(w, r)
|
||||
} else if strings.HasSuffix(r.URL.Path, "/divs") {
|
||||
// TODO : 세션키와 authtoken을 헤더로 받아서 accid 조회
|
||||
queryvals := r.URL.Query()
|
||||
sk := queryvals.Get("sk")
|
||||
|
||||
@ -639,9 +1030,8 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
divstrptr := atomic.LoadPointer(&sh.divisionsForUsersSerialized)
|
||||
divstr := *(*string)(divstrptr)
|
||||
w.Write([]byte(divstr))
|
||||
version := queryvals.Get("version")
|
||||
w.Write(sh.findVersionSplit(version))
|
||||
} else if strings.HasSuffix(r.URL.Path, "/addr") {
|
||||
queryvals := r.URL.Query()
|
||||
sk := queryvals.Get("sk")
|
||||
@ -677,7 +1067,8 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
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 권한이면 입장 가능
|
||||
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
|
||||
} else if div.Maintenance != nil {
|
||||
@ -696,7 +1087,7 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Println("div is not found :", divname)
|
||||
logger.Println("div is not found :", divname, sh.Divisions)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
} else {
|
||||
|
||||
110
core/watch.go
110
core/watch.go
@ -10,7 +10,7 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
common "repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@ -24,7 +24,7 @@ type authPipelineDocument struct {
|
||||
DocumentKey struct {
|
||||
Id primitive.ObjectID `bson:"_id"`
|
||||
} `bson:"documentKey"`
|
||||
Authinfo *common.Authinfo `bson:"fullDocument"`
|
||||
Authinfo *gocommon.Authinfo `bson:"fullDocument"`
|
||||
}
|
||||
|
||||
type servicePipelineDocument struct {
|
||||
@ -43,102 +43,6 @@ type filePipelineDocument struct {
|
||||
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)
|
||||
} 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) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
@ -285,11 +189,9 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
|
||||
logger.Error("service cannot be prepared :", data.Service, err)
|
||||
} else {
|
||||
// 내가 임시로 가지고 있던 서비스일 수 있다.
|
||||
already := mg.service().Id == data.Service.Id
|
||||
logger.Println("service is on the board! :", data.Service)
|
||||
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(data.Service))
|
||||
if !already {
|
||||
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, data.Service.ServiceCode, "/"), mg.service())
|
||||
if mg.service().Id == data.Service.Id {
|
||||
logger.Println("service is on the board! :", data.Service)
|
||||
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(data.Service))
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +223,7 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
|
||||
}
|
||||
}
|
||||
|
||||
func watchAuthCollection(parentctx context.Context, ac *common.AuthCollection, mongoClient common.MongoClient) {
|
||||
func watchAuthCollection(parentctx context.Context, ac *gocommon.AuthCollection, mongoClient gocommon.MongoClient) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s != nil {
|
||||
|
||||
11
fba/track-event.html
Normal file
11
fba/track-event.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" src="./fb-ga.min.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<!-- <body> -->
|
||||
<!-- <body onload="window.FBA.TrackLogEvent('DESKTOP-TEST');"> -->
|
||||
<!-- <script type="cjs" src="./fb-ga.rollup.js"> -->
|
||||
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
go.mongodb.org/mongo-driver v1.11.7
|
||||
google.golang.org/api v0.128.0
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20240205060841-c31f838ba8a9
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
6
go.sum
6
go.sum
@ -268,5 +268,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22 h1:DImSGNxZrc+Q4WlS1OKMsLAScEfDYLX4XMJdjAaVnXc=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230621052811-06ef97f11d22/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb h1:Rdf6uhBIWunRLZ2LIT1hSovYXxZoOzx9mdSK5bjWpos=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20240205060841-c31f838ba8a9 h1:5cQ60XjlI7k0qld0rIpd6gy7+a9csv3ijz1EVKTzsy8=
|
||||
repositories.action2quare.com/ayo/gocommon v0.0.0-20240205060841-c31f838ba8a9/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
|
||||
|
||||
4
main.go
4
main.go
@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
common "repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon"
|
||||
"repositories.action2quare.com/ayo/gocommon/flagx"
|
||||
"repositories.action2quare.com/ayo/gocommon/logger"
|
||||
"repositories.action2quare.com/ayo/maingate/core"
|
||||
@ -36,7 +36,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
server := common.NewHTTPServer(serveMux)
|
||||
server := gocommon.NewHTTPServer(serveMux)
|
||||
logger.Println("maingate is started")
|
||||
if err := server.Start(); err != nil {
|
||||
logger.Error("maingate is stopped with error :", err)
|
||||
|
||||
@ -4,27 +4,11 @@ $CurBranch = git branch --show-current
|
||||
|
||||
Remove-Item maingate.zip -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
cd ..
|
||||
cd maingate-console
|
||||
|
||||
|
||||
git switch $CurBranch
|
||||
git pull
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
Remove-Item console -Force -Recurse -ErrorAction SilentlyContinue
|
||||
Copy-Item build console -Recurse
|
||||
|
||||
Compress-Archive -Path console -DestinationPath ..\maingate\maingate.zip -Force
|
||||
Remove-Item console -Force -Recurse
|
||||
|
||||
cd ..
|
||||
cd maingate
|
||||
|
||||
$Env:GOOS="linux"
|
||||
$Env:GOARCH="amd64"
|
||||
go build -ldflags="-s -w" .
|
||||
|
||||
Compress-Archive -Path maingate -Update -DestinationPath maingate.zip
|
||||
Compress-Archive -Path *-firebase-*.json -Update -DestinationPath maingate.zip
|
||||
Compress-Archive -Path fba -Update -DestinationPath maingate.zip
|
||||
Compress-Archive -Path template -Update -DestinationPath maingate.zip
|
||||
|
||||
1
template/fb-ga.min.js
vendored
Normal file
1
template/fb-ga.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user