Compare commits

...

145 Commits

Author SHA1 Message Date
e37a974d9c [이민권] 계정 삭제
- 계정 삭제 시, Firebase 정보 삭제
2024-01-12 12:31:21 +09:00
470591cb44 [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-11 19:11:43 +09:00
38114769b3 [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-11 12:54:48 +09:00
b05473a1c6 [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-10 18:40:38 +09:00
0fff694e8a [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-10 18:10:18 +09:00
d8713298c4 [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-10 17:35:00 +09:00
763b0fc4bd [이민권] 계정 삭제
- 계정 삭제 취소 기능 추가
2024-01-10 17:28:24 +09:00
d8e18d7ffc [이민권] 서비스 준비
- 계정 삭제 시, TTL 적용
2024-01-09 19:56:57 +09:00
381f1edb80 [이민우] 정보가 아니라 주소갑이 나와서 수정 (대리 커밋) 2023-12-08 17:08:14 +09:00
a8e448b8ca 로그 정리 2023-10-30 17:25:39 +09:00
9b0e3a5d5b 모듈 정리 2023-10-30 17:25:31 +09:00
5757a81cb8 fba, template 폴더를 package 에 추가 2023-10-24 14:05:42 +09:00
c4b8e630dc [이민권] 에러 수정 2023-10-23 15:17:07 +09:00
43b0242652 Firebase-Google Analaytics Desktop 버전 연동을 위해서 JavaScript SDK( JS-SDk ) 관련 코드 추가 2023-10-23 15:08:48 +09:00
893744a1ab Revert "모듈 업데이트"
This reverts commit 192cc569b4.
2023-10-12 12:04:49 +09:00
192cc569b4 모듈 업데이트 2023-10-12 12:04:31 +09:00
56e6608537 화이트리스트 추가,삭제 반영 안되는 문제 수정 2023-10-05 11:08:09 +09:00
0053033e32 현재 block된 정보를 조회 2023-09-25 12:29:41 +09:00
d1ff6a56fc gocommon 업데이트 반영 2023-09-21 13:18:41 +09:00
114461c51d 화이트리스트 추가 안되는 문제 수정 2023-09-12 17:07:16 +09:00
117a3e5d90 [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-09-08 18:24:17 +09:00
fafc463f2a [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-09-06 19:34:47 +09:00
e2bec481f0 [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-09-06 17:11:47 +09:00
89e7d35b5a [이민권] 오타 수정 2023-09-06 11:35:21 +09:00
fe662c5355 [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-09-06 11:23:06 +09:00
6767a37704 version split 수정 2023-09-05 17:14:56 +09:00
2ea035a43b version split 수정 2023-09-04 11:15:23 +09:00
2a1ad499ed [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-08-31 17:19:18 +09:00
5cc6ddc8f1 [이민우] 캐릭터 생성 제한 구현 (대신 커밋) 2023-08-31 11:55:31 +09:00
0e070221f8 Merge branch 'master' into kd-live 2023-08-29 18:41:22 +09:00
1ba32aa4c9 쿠폰 조회 오류 수정 2023-08-29 11:06:14 +09:00
edb3e07329 parsemuiltipartform 추가 . 에러는 무시 2023-08-28 13:53:59 +09:00
f5304fae80 쿠폰 api를 maingate 로 옮김 2023-08-25 12:31:32 +09:00
d9be04541b prepare 로그 자세히 2023-08-25 11:39:17 +09:00
d2e06961b9 [이민권] guest 계정에 link 걸면 guest link는 제거하도록 수정 2023-08-24 16:45:01 +09:00
28092fcf17 [이민권] 게스트 계정 링크 시 삭제 기능 추가 2023-08-24 15:39:11 +09:00
a7a20aebcf 모듈 업데이트 2023-08-23 22:46:07 +09:00
c43c10982c [이민권] link 수정 2023-08-23 19:03:28 +09:00
02c4f9e3d1 [이민권] link, unlink 이슈 수정 2023-08-23 18:53:24 +09:00
1db22730aa [이민권] linkinfo 이슈 수정 2023-08-23 18:46:23 +09:00
184675a9b7 계정 제재 동작 오류 수정 2023-08-23 17:48:47 +09:00
197ee7127b 서버 noauth 플래그를 알림 2023-08-22 19:53:30 +09:00
869fa48d74 Revert "[이민권] 임시 로그 삽입"
This reverts commit 7470f8e001.
2023-08-22 18:32:37 +09:00
7470f8e001 [이민권] 임시 로그 삽입 2023-08-22 18:27:04 +09:00
fc70a9482c [이민권] getProviderInfo 실패시 리턴하지 않는 부분 수정 2023-08-22 18:22:31 +09:00
e3afb58634 file 경로에서 servicecode 제거 2023-08-22 18:06:14 +09:00
bafb67dabc 스팀 로그인 noauth 추가 처리 2023-08-22 17:51:41 +09:00
9ccd97564a [이민권] Unlink 수정 2023-08-22 15:38:51 +09:00
08cb989975 서비스 json 수정 2023-08-22 11:10:59 +09:00
455011fd99 계정 제재 api추가 2023-08-22 10:16:09 +09:00
4958cb0b93 [이민권] linkinfo 로그 수정 2023-08-21 19:18:27 +09:00
e8832f329a 안쓰는 인덱스 제거 2023-08-21 11:01:21 +09:00
9c14480be7 [이민권] 게스트 상태에서 링크하는 경우 수정 2023-08-18 17:06:21 +09:00
daf3e3f027 version split 디폴트 이름 변경 2023-08-18 13:56:51 +09:00
a8df7d54bd version split 디폴트 이름 변경 2023-08-18 13:56:34 +09:00
2de82b9d2a [이민권] 계정 정보 받아오는 함수 수정 2023-08-17 19:13:01 +09:00
00c2d6e205 [이민권] 언더바 빼먹은거 수정 2023-08-17 18:31:41 +09:00
87d922c558 [이민권] 크래쉬 수정 2023-08-17 18:21:01 +09:00
0be7adefe3 [이민권] api 수정
- unlink시 link collection에 doc 남겨지는 이슈 수정
- linkinfo api가 단순 link 갯수가 아니라 어떤 platform에 연동 하였는지 반환하도록 수정
- 계정 삭제 api인 delacc api 추가
2023-08-17 14:23:00 +09:00
3a9f81f1cb divisionsSerialized가 잘못 덮어씌어진 문제 수정 2023-08-17 12:36:04 +09:00
39e1b925e5 divisionsSerialized가 잘못 덮어씌어진 문제 수정 2023-08-17 12:35:48 +09:00
a97b9f0983 Merge branch 'master' into kd-live 2023-08-16 21:44:47 +09:00
42b4ade782 sh를 통으로 atomic하게 교체 2023-08-16 21:44:19 +09:00
9edea29983 Merge branch 'master' into kd-live 2023-08-09 17:39:30 +09:00
c7f073c779 guest 계정 지원 2023-08-09 17:39:16 +09:00
8e908982a8 Merge branch 'master' into kd-live 2023-08-09 17:24:40 +09:00
8126406e5f 모듈 업데이트 2023-08-02 11:41:18 +09:00
6b68d918ba 모듈 업데이트 2023-08-01 14:18:38 +09:00
fb5db25dce 불필요한 로그 삭제 2023-07-31 15:54:03 +09:00
f79e922fa0 SteamSDK - 스팀 인증 관련 기능 추가 2023-07-31 12:43:02 +09:00
3216e2620a Merge branch 'master' into kd-live 2023-07-10 18:12:57 +09:00
d796958d5e 화이트리스트 삭제 문제 수정 2023-07-10 18:12:43 +09:00
f66904d428 Merge branch 'master' into kd-live 2023-07-07 15:33:18 +09:00
22ec115b35 whitelist에서 email과 platform을 모두 비교 2023-07-07 15:32:54 +09:00
8d70777269 안쓰는 로그 제거 2023-06-30 18:04:25 +09:00
15ead6b0bc Merge branch 'master' into kd-live 2023-06-30 18:01:43 +09:00
5abb3fd2b7 중복 로그 제거 2023-06-30 18:01:34 +09:00
97636ce31d Merge branch 'master' into kd-live 2023-06-29 21:32:19 +09:00
ed85918e7f maingate console은 이제 빌드 안함 2023-06-29 21:32:05 +09:00
c5ec99d3e1 mongoClient 변경 2023-06-29 21:31:51 +09:00
2b4becdb61 Merge branch 'master' into kd-live 2023-06-29 21:22:19 +09:00
4af93b3d7e maintenance 링크를 잘못 만들던 문제 수정 2023-06-29 21:21:49 +09:00
429bbd1e7a Merge branch 'master' into kd-live 2023-06-29 16:09:40 +09:00
afc3a10f51 serviceptr이 변경됐을 때 처리 2023-06-29 16:09:27 +09:00
1c397da77a Merge branch 'master' into kd-live 2023-06-28 16:45:34 +09:00
3834ca2e37 Merge branch 'master' of https://repositories.action2quare.com/ayo/maingate 2023-06-28 16:45:20 +09:00
0898213aa8 로그 추가 2023-06-28 16:45:18 +09:00
5502c4d744 Merge commit 'cb08ecb53a4d472919f24baaf4e8069412c2870c' into kd-live 2023-06-28 16:18:48 +09:00
cb08ecb53a maingate - 연결된 계정 숫자 조회, 연결 끊는 API 추가 2023-06-28 16:14:38 +09:00
cb793092e9 Merge branch 'master' into kd-live 2023-06-28 15:28:11 +09:00
70d775e0fc 파일 서비스 경로 수정 2023-06-28 15:28:01 +09:00
13f6492e5f Merge branch 'master' into kd-live 2023-06-28 15:17:21 +09:00
92c5bf5b46 서비스 업데이트 로직 수정 2023-06-28 15:17:11 +09:00
f739eb3a8b Merge branch 'master' into kd-live 2023-06-27 19:13:19 +09:00
6eed2be7f4 save 도 public으로 변경 2023-06-27 19:13:10 +09:00
c95d7747cd Merge branch 'master' into kd-live 2023-06-27 19:04:01 +09:00
31782578f4 패키지 이름 정리 2023-06-27 19:03:53 +09:00
841dad4992 Merge branch 'master' into kd-live 2023-06-27 18:51:12 +09:00
265ae0ac8b FileDocumentDesc를 public으로 변경 2023-06-27 18:51:00 +09:00
ff7d56152e Merge branch 'master' into kd-live 2023-06-26 17:34:42 +09:00
cd126d2c59 noauth인 경우 ServiceCode를 000000000000으로 고정 2023-06-26 17:34:30 +09:00
2acca6dbe8 Merge branch 'master' into kd-live 2023-06-23 17:58:56 +09:00
d3332f530f noauth와 devflag 분리 2023-06-23 17:58:41 +09:00
484db90037 로그 추가 2023-06-23 17:35:25 +09:00
b06eb86578 되돌림을 되돌림 2023-06-23 17:21:36 +09:00
9704decb7a 되돌림 2023-06-23 17:05:19 +09:00
767d6bf002 Merge branch 'master' into kd-live 2023-06-23 16:53:16 +09:00
79e00de9f6 코드 변경 2023-06-23 16:53:03 +09:00
1f7421dd2e Merge branch 'master' into kd-live 2023-06-23 16:43:32 +09:00
7e5da4948c div 쿼리 주소 변경 2023-06-23 16:43:20 +09:00
53701d61f8 Merge branch 'master' into kd-live 2023-06-23 16:27:35 +09:00
6d02eb092c 로그 추가 2023-06-23 16:27:31 +09:00
21d4080baa 서비스가 없을 때 빈 서비스 만들어 줌 2023-06-23 15:07:17 +09:00
7bee78a875 live용 설정 추가 2023-06-22 20:20:50 +09:00
2cd2a20065 안쓰는 함수 제거 2023-06-22 20:20:36 +09:00
74c0a215ed noauth을 때 사설 IP대역만 허용 2023-06-22 20:17:30 +09:00
e65fbafd36 noauth일 때 로컬 아이피를 읽어서 주소를 만듬
This reverts commit 4ae86f0a57.
2023-06-22 16:50:04 +09:00
f0f5011e10 noauth 처리 추가 - config에 division 누락 문제 수정 2023-06-22 15:31:10 +09:00
4ae86f0a57 noauth 처리 추가 2023-06-22 14:53:17 +09:00
a55a435811 noauth 처리 추가 2023-06-22 11:56:45 +09:00
718c54f796 트위터 - Userinfo 얻어오는 API 변경, verify_credentials로 변경한다. email 정보를 얻기 위해서.. 2023-06-22 10:49:44 +09:00
1b90e12fec 빈 디비에서 첫 실행할 때 문제 수정 2023-06-21 18:14:44 +09:00
9fd2316eeb 빈 디비에서 최소 실행할 때 문제 수정 2023-06-21 17:56:27 +09:00
3c9b959ad6 config.json파일은 별도로 배포됨 2023-06-21 17:16:45 +09:00
879d4b7092 배포용 패키지 제작 스크립트 2023-06-21 17:14:10 +09:00
562da693f1 게임팟 제거 2023-06-21 17:13:55 +09:00
7cd5211779 flag를 flagx로 대체 2023-06-21 14:33:29 +09:00
2399f8cd27 noauth 처리 추가 2023-06-21 09:33:48 +09:00
b554a8783b noauth 처리 추가 2023-06-20 17:46:41 +09:00
37080b80f2 게임팟 제거 2023-06-20 17:42:19 +09:00
04c8f0094b flag 에러 메시지 무시 2023-06-20 17:32:27 +09:00
a034ea5ceb flag 에러 표시만 하고 계속 실행 2023-06-20 17:16:35 +09:00
c47353565b flag.parse 문제 해결 2023-06-20 17:08:57 +09:00
7d4d4049e8 noauth 플래그 처리 2023-06-20 15:50:18 +09:00
6db196df28 api 호출 권한 일단 보류 2023-06-20 15:49:50 +09:00
8d9b975234 계정 collection 추가 2023-06-20 12:02:29 +09:00
efcc847405 admins null 리턴 수정 2023-06-20 11:29:17 +09:00
4f6f9bf531 whitelist를 mg로 이동 2023-06-20 11:18:32 +09:00
140da79f7f flag를 내재화함 2023-06-20 11:07:53 +09:00
09328575ad maingate는 admin 계정으로 통일 또는 ApiToken 2023-06-19 21:19:45 +09:00
4bb25a1eff 화이트리스트 멤버 태그 지움, Closed, Use Whitelist 제거 2023-06-19 14:56:47 +09:00
91790330d2 화이트리스트 멤버에 tag 추가 2023-06-19 14:31:34 +09:00
40a5b4e878 모듈 업데이트 2023-06-16 14:42:39 +09:00
50536caa00 config 알려주는 api 추가 2023-06-16 14:42:28 +09:00
bf12ba76b6 함수 호출 잘못된 거 수정 2023-06-16 14:41:45 +09:00
30 changed files with 10471 additions and 2127 deletions

View 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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

537
backup/firebase-jssdk/js.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
{ {
"maingate_mongodb_url": "mongodb://...", "maingate_mongodb_url": "mongodb://...",
"autologin_ttl": 604800, "autologin_ttl": 604800,
"acc_del_ttl": 7776000,
"maximum_num_link_account": 10,
"redirect_base_url": "", "redirect_base_url": "",
"google_client_id" : "", "google_client_id" : "",
"google_client_secret" : "", "google_client_secret" : "",
@ -24,6 +26,15 @@
"firebase_admin_sdk_credentialfile": "", "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" : [ "maingate_global_admins" : [
"mountain@action2quare.com" "mountain@action2quare.com"
] ]

View File

@ -2,25 +2,21 @@ package core
import ( import (
"bytes" "bytes"
"crypto/md5"
"encoding/binary" "encoding/binary"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"flag"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
"path" "path"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe" "unsafe"
common "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
@ -28,8 +24,7 @@ import (
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
type fileDocumentDesc struct { type FileDocumentDesc struct {
Service string `bson:"service" json:"service"`
Key string `bson:"key" json:"key"` Key string `bson:"key" json:"key"`
Src string `bson:"src" json:"src"` Src string `bson:"src" json:"src"`
Link string `bson:"link" json:"link"` Link string `bson:"link" json:"link"`
@ -39,7 +34,7 @@ type fileDocumentDesc struct {
Contents []byte `bson:"contents,omitempty" json:"contents,omitempty"` Contents []byte `bson:"contents,omitempty" json:"contents,omitempty"`
} }
func (fd *fileDocumentDesc) save() error { func (fd *FileDocumentDesc) Save() error {
// 새 파일 올라옴 // 새 파일 올라옴
if len(fd.Contents) == 0 { if len(fd.Contents) == 0 {
return nil return nil
@ -68,168 +63,35 @@ func (fd *fileDocumentDesc) save() error {
if fd.Extract { if fd.Extract {
switch path.Ext(destFile) { switch path.Ext(destFile) {
case ".zip": case ".zip":
err = common.Unzip(destFile) err = gocommon.Unzip(destFile)
case ".tar": case ".tar":
err = common.Untar(destFile) err = gocommon.Untar(destFile)
} }
} }
return err return err
} }
func (caller apiCaller) isGlobalAdmin() bool {
if *noauth {
return true
}
email, ok := caller.userinfo["email"]
if !ok {
return false
}
if _, ok := caller.admins[email.(string)]; ok {
return true
}
return false
}
func (caller apiCaller) writeAccessableServices(w http.ResponseWriter) {
services, editable := caller.getAccessableServices()
for _, r := range editable {
w.Header().Add("MG-X-SERVICE-EDITABLE", r)
}
w.Write([]byte("{"))
start := true
for _, v := range services {
if !start {
w.Write([]byte(","))
}
w.Write([]byte(fmt.Sprintf(`"%s":`, v.ServiceName)))
serptr := atomic.LoadPointer(&v.serviceSummarySerialized)
w.Write(*(*[]byte)(serptr))
start = false
}
w.Write([]byte("}"))
}
func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string) {
allservices := caller.mg.services.all()
admin := caller.isGlobalAdmin()
var email string
if !*noauth {
v, ok := caller.userinfo["email"]
if !ok {
return nil, nil
}
email = v.(string)
_, admin = caller.admins[email]
}
var output []*serviceDescription
var editable []string
for _, desc := range allservices {
if admin {
output = append(output, desc)
editable = append(editable, desc.ServiceName)
} else if desc.isValidAPIUser("*", email) {
output = append(output, desc)
if desc.isValidAPIUser("service", email) {
editable = append(editable, desc.ServiceName)
}
}
}
sort.Slice(output, func(i, j int) bool {
return output[i].ServiceName < output[j].ServiceName
})
return output, editable
}
func (caller apiCaller) isValidUser(service any, category string) (valid bool, admin bool) {
if *noauth {
return true, true
}
v, ok := caller.userinfo["email"]
if !ok {
logger.Println("isVaidUser failed. email is missing :", caller.userinfo)
return false, false
}
email := v.(string)
if _, ok := caller.admins[email]; ok {
return true, true
}
svcdesc := caller.mg.services.get(service)
if svcdesc == nil {
logger.Println("isVaidUser failed. service is missing :", service)
return false, false
}
return svcdesc.isValidAPIUser(category, email), false
}
func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
if r.Method == "GET" { if r.Method == "GET" {
hasAuth := caller.isGlobalAdmin() allfiles, err := caller.mg.mongoClient.All(CollectionFile, options.Find().SetProjection(bson.M{
var email string
if !*noauth {
v, ok := caller.userinfo["email"]
if !ok {
return nil
}
email = v.(string)
_, hasAuth = caller.admins[email]
}
servicename := r.FormValue("service")
sh := caller.mg.services.get(servicename)
if sh == nil {
w.WriteHeader(http.StatusBadRequest)
return nil
}
if !hasAuth {
if hasAuth = sh.isValidAPIUser("maintenance", email); !hasAuth {
hasAuth = sh.isValidAPIUser("service", email)
}
}
if !hasAuth {
w.WriteHeader(http.StatusBadRequest)
return nil
}
var files []fileDocumentDesc
err := caller.mg.mongoClient.FindAllAs(CollectionFile, bson.M{
"service": servicename,
}, &files, options.Find().SetProjection(bson.M{
"contents": 0, "contents": 0,
})) }).SetReturnKey(false))
if err != nil { if err != nil {
return err return err
} }
if len(files) > 0 { if len(allfiles) > 0 {
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
return enc.Encode(files) return enc.Encode(allfiles)
} }
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
servicename := r.FormValue("service")
key := r.FormValue("key") key := r.FormValue("key")
if len(servicename) == 0 || len(key) == 0 { if len(key) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return nil return nil
} }
_, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{ _, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{
"service": servicename,
"key": key, "key": key,
}) })
@ -245,11 +107,6 @@ var seq = uint32(0)
func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error {
if r.Method == "PUT" { if r.Method == "PUT" {
servicename := r.FormValue("service")
hasher := md5.New()
hasher.Write([]byte(servicename))
subfolder := hex.EncodeToString(hasher.Sum(nil))[:8]
infile, header, err := r.FormFile("file") infile, header, err := r.FormFile("file")
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -265,20 +122,19 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
var b [5]byte var b [5]byte
binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix())) binary.BigEndian.PutUint32(b[0:4], uint32(time.Now().Unix()))
b[4] = byte(atomic.AddUint32(&seq, 1) % 255) b[4] = byte(atomic.AddUint32(&seq, 1) % 255)
rf := hex.EncodeToString(b[1:])
newidstr := subfolder + rf
newidbt, _ := hex.DecodeString(newidstr)
newidobj := primitive.NewObjectID()
copy(newidobj[:], newidbt[:8])
newidobj := primitive.NewObjectID()
copy(newidobj[:], b[1:])
rf := newidobj.Hex()
var link string var link string
if extract { if extract {
link = path.Join("static", subfolder, rf) link = path.Join("static", rf)
} else { } else {
link = path.Join("static", subfolder, rf, header.Filename) link = path.Join("static", rf, header.Filename)
} }
newdoc := fileDocumentDesc{ newdoc := FileDocumentDesc{
Contents: contents, Contents: contents,
Src: header.Filename, Src: header.Filename,
Timestamp: time.Now().UTC().Unix(), Timestamp: time.Now().UTC().Unix(),
@ -286,11 +142,9 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
Link: link, Link: link,
Desc: desc, Desc: desc,
Key: rf, Key: rf,
Service: servicename,
} }
_, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{ _, _, err = caller.mg.mongoClient.UpsertOne(CollectionFile, bson.M{
"_id": newidobj, "_id": newidobj,
"service": servicename,
"key": rf, "key": rf,
}, newdoc) }, newdoc)
@ -304,48 +158,88 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
return nil return nil
} }
func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg mg := caller.mg
queryvals := r.URL.Query()
if r.Method == "GET" { if r.Method == "GET" {
service := queryvals.Get("service") target, ok := gocommon.ReadObjectIDFormValue(r.Form, "accid")
if ok {
json.NewEncoder(w).Encode(mg.bl.all())
} else if !target.IsZero() {
if blocked, ok := mg.bl.get(target); ok && blocked != nil {
json.NewEncoder(w).Encode(blocked)
}
}
} else if r.Method == "PUT" {
body, _ := io.ReadAll(r.Body)
if valid, _ := caller.isValidUser(service, "whitelist"); !valid { var bipl blockinfoWithStringId
logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo) if err := json.Unmarshal(body, &bipl); err != nil {
w.WriteHeader(http.StatusBadRequest) return err
return nil
} }
if len(service) > 0 { accid, err := primitive.ObjectIDFromHex(bipl.StrId)
all, err := mg.mongoClient.FindAll(CollectionWhitelist, bson.M{
"service": service,
})
if err != nil { if err != nil {
return err return err
} }
if len(all) > 0 { bi := blockinfo{
allraw, _ := json.Marshal(all) Start: primitive.NewDateTimeFromTime(time.Unix(bipl.StartUnix, 0)),
w.Write(allraw) End: primitive.NewDateTimeFromTime(time.Unix(bipl.EndUnix, 0)),
Reason: bipl.Reason,
} }
} else {
logger.Println("service param is missing") 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" { } else if r.Method == "PUT" {
body, _ := io.ReadAll(r.Body) body, _ := io.ReadAll(r.Body)
var member whitelistmember var member whitelistmember
if err := json.Unmarshal(body, &member); err != nil { if err := json.Unmarshal(body, &member); err != nil {
return err return err
} }
if valid, _ := caller.isValidUser(member.Service, "whitelist"); !valid { member.ExpiredAt = 0
logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo) member.Id = primitive.NilObjectID
w.WriteHeader(http.StatusBadRequest)
return nil
}
member.Expired = 0
_, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{ _, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{
"_id": primitive.NewObjectID(), "_id": primitive.NewObjectID(),
}, bson.M{ }, bson.M{
@ -356,7 +250,8 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
return err return err
} }
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
id := queryvals.Get("id") id := r.URL.Query().Get("id")
if len(id) == 0 { if len(id) == 0 {
return errors.New("id param is missing") return errors.New("id param is missing")
} }
@ -381,25 +276,23 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg mg := caller.mg
queryvals := r.URL.Query()
if r.Method == "GET" { if r.Method == "GET" {
name := queryvals.Get("name") logger.Println("serviceAPI :", r.URL.Path)
if len(name) > 0 { if mg.service().Id.IsZero() {
if valid, _ := caller.isValidUser(name, "*"); !valid { logger.Println(" id is zero")
logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo) newService := serviceDescription{
w.WriteHeader(http.StatusBadRequest) ServiceDescriptionSummary: ServiceDescriptionSummary{
return nil Id: primitive.NewObjectID(),
},
}
if err := newService.prepare(caller.mg); err != nil {
logger.Println(" prepare failed :", err)
return err
}
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(&newService))
} }
if valid, admin := caller.isValidUser(name, "service"); valid || admin { w.Write(mg.service().serviceSerialized)
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
}
serptr := atomic.LoadPointer(&mg.services.get(name).serviceSerialized)
w.Write(*(*[]byte)(serptr))
} else {
caller.writeAccessableServices(w)
}
} else if r.Method == "POST" { } else if r.Method == "POST" {
body, _ := io.ReadAll(r.Body) body, _ := io.ReadAll(r.Body)
var service serviceDescription var service serviceDescription
@ -407,31 +300,13 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
return err return err
} }
if service.Id.IsZero() {
if caller.isGlobalAdmin() {
service.Id = primitive.NewObjectID()
} else {
logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest)
return nil
}
} else if valid, _ := caller.isValidUser(service.Id, "service"); !valid {
logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest)
return nil
}
if len(service.ServerApiTokens) == 0 { if len(service.ServerApiTokens) == 0 {
service.ServerApiTokens = []primitive.ObjectID{ service.ServerApiTokens = []primitive.ObjectID{
primitive.NewObjectID(), primitive.NewObjectIDFromTimestamp(time.Now().Add(-time.Hour * 24 * 30 * 465)),
} }
} }
filter := bson.M{"_id": service.Id} filter := bson.M{"_id": service.Id}
if len(service.ServiceCode) == 0 {
service.ServiceCode = hex.EncodeToString(service.Id[6:])
}
success, _, err := mg.mongoClient.Update(CollectionService, filter, bson.M{ success, _, err := mg.mongoClient.Update(CollectionService, filter, bson.M{
"$set": &service, "$set": &service,
}, options.Update().SetUpsert(true)) }, options.Update().SetUpsert(true))
@ -451,32 +326,9 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg mg := caller.mg
queryvals := r.URL.Query()
if r.Method == "GET" { if r.Method == "GET" {
name := queryvals.Get("name") w.Write(mg.service().divisionsSerialized)
if len(name) > 0 {
if valid, _ := caller.isValidUser(name, "*"); !valid {
w.WriteHeader(http.StatusBadRequest)
return nil
}
if valid, admin := caller.isValidUser(name, "maintenance"); valid || admin {
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
}
serptr := atomic.LoadPointer(&mg.services.get(name).divisionsSerialized)
w.Write(*(*[]byte)(serptr))
} else {
caller.writeAccessableServices(w)
}
} else if r.Method == "POST" { } else if r.Method == "POST" {
servicename := queryvals.Get("name")
if valid, _ := caller.isValidUser(servicename, "service"); !valid {
logger.Println("maintenanceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest)
return nil
}
var divs map[string]*Division var divs map[string]*Division
dec := json.NewDecoder(r.Body) dec := json.NewDecoder(r.Body)
if err := dec.Decode(&divs); err != nil { if err := dec.Decode(&divs); err != nil {
@ -485,7 +337,7 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
} }
_, _, err := mg.mongoClient.Update(CollectionService, bson.M{ _, _, err := mg.mongoClient.Update(CollectionService, bson.M{
"service": servicename, "_id": mg.service().Id,
}, bson.M{ }, bson.M{
"$set": bson.M{"divisions": divs}, "$set": bson.M{"divisions": divs},
}, options.Update().SetUpsert(false)) }, options.Update().SetUpsert(false))
@ -499,118 +351,33 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
return nil return nil
} }
func (caller apiCaller) accountAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) couponAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg switch r.Method {
queryvals := r.URL.Query() case "PUT":
if r.Method == "GET" { // 쿠폰 생성
service := queryvals.Get("service") logger.Println("begin generateCoupons")
if len(service) == 0 { generateCoupons(caller.mg.mongoClient, w, r)
return nil
}
if valid, _ := caller.isValidUser(service, "account"); !valid { case "POST":
logger.Println("accountAPI failed. not vaild user :", r.Method, caller.userinfo) // TODO : 쿠폰 사용
w.WriteHeader(http.StatusBadRequest) // 쿠폰 사용 표시 해주고 내용을 응답
return nil logger.Println("begin useCoupon")
} useCoupon(caller.mg.mongoClient, w, r)
var accdoc primitive.M case "GET":
if v := queryvals.Get("accid"); len(v) == 0 { // 쿠폰 조회
email := queryvals.Get("email") if r.Form.Has("code") {
platform := queryvals.Get("platform") // 쿠폰 코드 조회
if len(email) == 0 || len(platform) == 0 { logger.Println("begin queryCoupon")
return nil queryCoupon(caller.mg.mongoClient, w, r)
} } else if r.Form.Has("name") {
// 쿠폰 코드 다운
found, err := mg.mongoClient.FindOne(CollectionLink, bson.M{ logger.Println("begin downloadCoupons")
"email": email, downloadCoupons(caller.mg.mongoClient, w, r)
"platform": platform,
})
if err != nil {
return err
}
if found == nil {
return nil
}
if idobj, ok := found["_id"]; ok {
svcdoc, err := mg.mongoClient.FindOne(common.CollectionName(service), bson.M{
"_id": idobj,
})
if err != nil {
return err
}
if svcdoc != nil {
found["accid"] = svcdoc["accid"]
}
accdoc = found
}
} else { } else {
accid, err := primitive.ObjectIDFromHex(v) // 쿠폰 이름 목록
if err != nil { logger.Println("begin listAllCouponNames")
return err listAllCouponNames(caller.mg.mongoClient, w, r)
}
svcdoc, err := mg.mongoClient.FindOne(common.CollectionName(service), bson.M{
"accid": accid,
})
if err != nil {
return err
}
found, err := mg.mongoClient.FindOne(CollectionLink, bson.M{
"_id": svcdoc["_id"],
})
if err != nil {
return err
}
if found != nil {
found["accid"] = accid
}
accdoc = found
}
if accdoc != nil {
accdoc["code"] = service
delete(accdoc, "uid")
delete(accdoc, "_id")
var bi blockinfo
if err := mg.mongoClient.FindOneAs(CollectionBlock, bson.M{
"code": service,
"accid": accdoc["accid"],
}, &bi); err != nil {
return err
}
if !bi.Start.Time().IsZero() && bi.End.Time().After(time.Now().UTC()) {
accdoc["blocked"] = bi
}
return json.NewEncoder(w).Encode(accdoc)
}
} else if r.Method == "POST" {
var account struct {
Code string
Accid string
Blocked blockinfo
}
body, _ := io.ReadAll(r.Body)
if err := json.Unmarshal(body, &account); err != nil {
return err
}
accid, _ := primitive.ObjectIDFromHex(account.Accid)
if !account.Blocked.Start.Time().IsZero() && account.Blocked.Start.Time().After(time.Now().UTC()) {
if _, _, err := mg.mongoClient.Update(CollectionBlock, bson.M{
"code": account.Code,
"accid": accid,
}, bson.M{
"$set": account.Blocked,
}, options.Update().SetUpsert(true)); err != nil {
return err
}
} }
} }
return nil return nil
@ -620,24 +387,61 @@ var errApiTokenMissing = errors.New("mg-x-api-token is missing")
func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg mg := caller.mg
if !*devflag {
apitoken := r.Header.Get("MG-X-API-TOKEN") apitoken := r.Header.Get("MG-X-API-TOKEN")
if len(apitoken) == 0 { if len(apitoken) == 0 {
return errApiTokenMissing return errApiTokenMissing
} }
if _, exists := mg.apiTokenToService.get(apitoken); !exists { apitokenObj, _ := primitive.ObjectIDFromHex(apitoken)
if !mg.service().isValidToken(apitokenObj) {
return fmt.Errorf("mg-x-api-token is not valid : %s", apitoken) return fmt.Errorf("mg-x-api-token is not valid : %s", apitoken)
} }
}
return nil return nil
} }
var noauth = flag.Bool("noauth", false, "") 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 { type apiCaller struct {
userinfo map[string]any userinfo map[string]any
admins map[string]bool globalAdmins map[string]bool
mg *Maingate mg *Maingate
apiToken primitive.ObjectID
} }
func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) { func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
@ -653,9 +457,11 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
r.Body.Close() r.Body.Close()
}() }()
r.ParseMultipartForm(32 << 20)
var userinfo map[string]any var userinfo map[string]any
if !*noauth { if !*devflag {
authheader := r.Header.Get("Authorization") authheader := r.Header.Get("Authorization")
if len(authheader) == 0 { if len(authheader) == 0 {
logger.Println("Authorization header is not valid :", authheader) logger.Println("Authorization header is not valid :", authheader)
@ -689,20 +495,35 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
ptr := atomic.LoadPointer(&mg.admins) ptr := atomic.LoadPointer(&mg.admins)
adminsptr := (*globalAdmins)(ptr) adminsptr := (*globalAdmins)(ptr)
if adminsptr.modtime != common.ConfigModTime() { if adminsptr.modtime != gocommon.ConfigModTime() {
var config globalAdmins var config globalAdmins
if err := common.LoadConfig(&config); err == nil { if err := gocommon.LoadConfig(&config); err == nil {
config.parse() config.parse()
adminsptr = &config adminsptr = &config
atomic.StorePointer(&mg.admins, unsafe.Pointer(adminsptr)) 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 {
obj, err := primitive.ObjectIDFromHex(apiToken)
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
}
apiTokenObj = obj
}
}
logger.Println("api call :", r.URL.Path, r.Method, r.URL.Query(), userinfo) logger.Println("api call :", r.URL.Path, r.Method, r.URL.Query(), userinfo)
caller := apiCaller{ caller := apiCaller{
userinfo: userinfo, userinfo: userinfo,
admins: adminsptr.emails, globalAdmins: adminsptr.emails,
mg: mg, mg: mg,
apiToken: apiTokenObj,
} }
var err error var err error
@ -712,14 +533,18 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
err = caller.whitelistAPI(w, r) err = caller.whitelistAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/config") { } else if strings.HasSuffix(r.URL.Path, "/config") {
err = caller.configAPI(w, r) err = caller.configAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/account") {
err = caller.accountAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/upload") { } else if strings.HasSuffix(r.URL.Path, "/upload") {
err = caller.uploadAPI(w, r) err = caller.uploadAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/maintenance") { } else if strings.HasSuffix(r.URL.Path, "/maintenance") {
err = caller.maintenanceAPI(w, r) err = caller.maintenanceAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/files") { } else if strings.HasSuffix(r.URL.Path, "/files") {
err = caller.filesAPI(w, r) err = caller.filesAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/block") {
err = caller.blockAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/coupon") {
err = caller.couponAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/lockcreatechar") {
err = caller.lockcreatecharAPI(w, r)
} }
if err != nil { if err != nil {

372
core/api_coupon.go Normal file
View File

@ -0,0 +1,372 @@
package core
import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
"repositories.action2quare.com/ayo/gocommon"
coupon "repositories.action2quare.com/ayo/gocommon/coupon"
"repositories.action2quare.com/ayo/gocommon/logger"
)
const (
CollectionCoupon = gocommon.CollectionName("coupon")
CollectionCouponUse = gocommon.CollectionName("coupon_use")
)
type couponDoc struct {
Name string `json:"name" bson:"name"`
Effect string `json:"effect" bson:"effect"`
Desc string `json:"desc" bson:"desc"`
Total int64 `json:"total" bson:"total"`
Remains []string `json:"remains,omitempty" bson:"remains,omitempty"`
Used []string `json:"used,omitempty" bson:"used,omitempty"`
}
func makeCouponKey(roundnum uint32, uid []byte) string {
left := binary.BigEndian.Uint16(uid[0:2])
right := binary.BigEndian.Uint16(uid[2:4])
multi := uint32(left) * uint32(right)
xor := roundnum ^ multi
final := make([]byte, 8)
binary.LittleEndian.PutUint32(final, xor)
copy(final[4:], uid)
return fmt.Sprintf("%s-%s-%s-%s", hex.EncodeToString(final[0:2]), hex.EncodeToString(final[2:4]), hex.EncodeToString(final[4:6]), hex.EncodeToString(final[6:8]))
}
func makeCouponCodes(name string, count int) (string, map[string]string) {
checkunique := make(map[string]bool)
keys := make(map[string]string)
uid := make([]byte, 4)
roundHash, roundnum := coupon.MakeCouponRoundHash(name)
seed := time.Now().UnixNano()
for len(keys) < count {
rand.Seed(seed)
rand.Read(uid)
code := makeCouponKey(roundnum, uid)
if _, ok := checkunique[code]; !ok {
checkunique[code] = true
keys[hex.EncodeToString(uid)] = code
}
seed = int64(binary.BigEndian.Uint32(uid))
}
return roundHash, keys
}
func generateCoupons(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
name, _ := gocommon.ReadStringFormValue(r.Form, "name")
effect, _ := gocommon.ReadStringFormValue(r.Form, "effect")
count, _ := gocommon.ReadIntegerFormValue(r.Form, "count")
desc, _ := gocommon.ReadStringFormValue(r.Form, "desc")
if count == 0 {
logger.Println("[generateCoupons] count == 0")
w.WriteHeader(http.StatusBadRequest)
return
}
roundHash, _ := coupon.MakeCouponRoundHash(name)
roundObj, _ := primitive.ObjectIDFromHex(roundHash + roundHash + roundHash)
if count < 0 {
// 무한 쿠폰이므로 그냥 문서 생성해 주고 끝
if _, _, err := mongoClient.Update(CollectionCoupon, bson.M{
"_id": roundObj,
}, bson.M{
"$set": &couponDoc{
Name: name,
Effect: effect,
Desc: desc,
Total: -1,
},
}, options.Update().SetUpsert(true)); err != nil {
logger.Println("[generateCoupons] Update failed :", err)
w.WriteHeader(http.StatusInternalServerError)
}
return
}
// effect가 비어있으면 기존의 roundName에 갯수를 추가해 준다
// effect가 비어있지 않으면 roundName이 겹쳐서는 안된다.
coupondoc, err := mongoClient.FindOne(CollectionCoupon, bson.M{"_id": roundObj})
if err != nil {
logger.Println("[generateCoupons] FindOne failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
lastKeys := make(map[string]bool)
if coupondoc != nil {
if r, ok := coupondoc["remains"]; ok {
remains := r.(primitive.A)
for _, uid := range remains {
lastKeys[uid.(string)] = true
}
}
}
issuedKeys := make(map[string]string)
for len(issuedKeys) < int(count) {
_, vs := makeCouponCodes(name, int(count)-len(issuedKeys))
for k, v := range vs {
if _, ok := lastKeys[k]; !ok {
// 기존 키와 중복되지 않는 것만
issuedKeys[k] = v
}
}
}
var coupons []string
var uids []string
for uid, code := range issuedKeys {
uids = append(uids, uid)
coupons = append(coupons, code)
}
if coupondoc != nil {
_, _, err = mongoClient.Update(CollectionCoupon, bson.M{
"_id": roundObj,
}, bson.M{
"$push": bson.M{"remains": bson.M{"$each": uids}},
"$inc": bson.M{"total": count},
}, options.Update().SetUpsert(true))
} else {
_, _, err = mongoClient.Update(CollectionCoupon, bson.M{
"_id": roundObj,
}, bson.M{
"$push": bson.M{"remains": bson.M{"$each": uids}},
"$set": couponDoc{
Name: name,
Effect: effect,
Desc: desc,
Total: count,
},
}, options.Update().SetUpsert(true))
}
if err != nil {
logger.Println("[generateCoupons] Update failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
enc := json.NewEncoder(w)
enc.Encode(coupons)
}
func downloadCoupons(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
name, _ := gocommon.ReadStringFormValue(r.Form, "name")
if len(name) == 0 {
logger.Println("[downloadCoupons] name is empty")
w.WriteHeader(http.StatusBadRequest)
return
}
round, _ := coupon.MakeCouponRoundHash(name)
roundObj, err := primitive.ObjectIDFromHex(round + round + round)
if err != nil {
// 유효하지 않은 형식의 code
logger.Println("[downloadCoupons] ObjectIDFromHex failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
var coupon couponDoc
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
"_id": roundObj,
}, &coupon, options.FindOne().SetProjection(bson.M{"_id": 0, "remains": 1})); err != nil {
logger.Println("[downloadCoupons] FindOne failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
roundnum := binary.BigEndian.Uint32(roundObj[:])
var coupons []string
for _, uid := range coupon.Remains {
coupons = append(coupons, makeCouponKey(roundnum, []byte(uid)))
}
enc := json.NewEncoder(w)
enc.Encode(coupons)
}
func queryCoupon(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
code, _ := gocommon.ReadStringFormValue(r.Form, "code")
if len(code) == 0 {
logger.Println("[queryCoupon] code is empty")
w.WriteHeader(http.StatusBadRequest)
return
}
round, _ := coupon.DisolveCouponCode(code)
if len(round) == 0 {
// 유효하지 않은 형식의 code
// 쿠폰 이름일 수 있으므로 round hash를 계산한다.
round, _ = coupon.MakeCouponRoundHash(code)
}
roundObj, err := primitive.ObjectIDFromHex(round + round + round)
if err != nil {
// 유효하지 않은 형식의 code
logger.Println("[queryCoupon] ObjectIDFromHex failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
var coupon couponDoc
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
"_id": roundObj,
}, &coupon, options.FindOne().SetProjection(bson.M{"effect": 1, "name": 1, "reason": 1, "total": 1, "desc": 1}).SetReturnKey(false)); err != nil {
logger.Println("[queryCoupon] FindOneAs failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
enc := json.NewEncoder(w)
enc.Encode(coupon)
}
func listAllCouponNames(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
all, err := mongoClient.FindAll(CollectionCoupon, bson.M{}, options.Find().SetProjection(bson.M{"name": 1}))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
var names []string
for _, doc := range all {
names = append(names, doc["name"].(string))
}
enc := json.NewEncoder(w)
enc.Encode(names)
}
func useCoupon(mongoClient gocommon.MongoClient, w http.ResponseWriter, r *http.Request) {
acc, ok := gocommon.ReadObjectIDFormValue(r.Form, "accid")
if !ok || acc.IsZero() {
w.WriteHeader(http.StatusBadRequest)
return
}
code, _ := gocommon.ReadStringFormValue(r.Form, "code")
code = strings.TrimSpace(code)
if len(code) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
round, key := coupon.DisolveCouponCode(code)
if len(round) == 0 {
// couponId가 쿠폰 이름일 수도 있다. 무한 쿠폰
round, _ = coupon.MakeCouponRoundHash(code)
}
// 1. 내가 이 라운드의 쿠폰을 쓴 적이 있나
already, err := mongoClient.Exists(CollectionCouponUse, bson.M{
"_id": acc,
"rounds": round,
})
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if already {
// 이미 이 라운드의 쿠폰을 사용한 적이 있다.
w.WriteHeader(http.StatusConflict)
return
}
var coupon couponDoc
roundObj, _ := primitive.ObjectIDFromHex(round + round + round)
if len(key) == 0 {
// 무한 쿠폰일 수 있으므로 존재하는지 확인
if err := mongoClient.FindOneAs(CollectionCoupon, bson.M{
"_id": roundObj,
}, &coupon, options.FindOne().SetProjection(bson.M{"_id": 0, "effect": 1, "name": 1, "reason": 1, "total": 1})); err != nil {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if coupon.Total > 0 {
// 무한 쿠폰 아니네?
w.WriteHeader(http.StatusBadRequest)
return
}
} else {
// 2. 쿠폰을 하나 꺼냄
matched, _, err := mongoClient.Update(CollectionCoupon, bson.M{
"_id": roundObj,
}, bson.M{
"$pull": bson.M{"remains": key},
})
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if !matched {
// 쿠폰이 없다.
w.WriteHeader(http.StatusBadRequest)
return
}
// 3. round의 효과 읽기
if err := mongoClient.FindOneAndUpdateAs(CollectionCoupon, bson.M{
"_id": roundObj,
}, bson.M{
"$push": bson.M{"used": key},
}, &coupon, options.FindOneAndUpdate().SetProjection(bson.M{"effect": 1})); err != nil {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
if len(coupon.Effect) == 0 {
// 쿠폰이 없네?
w.WriteHeader(http.StatusBadRequest)
return
}
// 4. 쿠폰은 사용한 것으로 표시
// 이제 이 아래에서 실패하면 이 쿠폰은 못쓴다.
updated, _, err := mongoClient.Update(CollectionCouponUse, bson.M{
"_id": acc,
}, bson.M{
"$push": bson.M{"rounds": round},
"$set": bson.M{round + ".id": code},
"$currentDate": bson.M{round + ".ts": true},
}, options.Update().SetUpsert(true))
if err != nil {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if !updated {
logger.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write([]byte(coupon.Effect))
}

39
core/api_test.go Normal file
View 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))
}

View File

@ -7,19 +7,21 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"html/template"
"io" "io"
"math/big" "math/big"
"math/rand" "math/rand"
"net"
"net/http" "net/http"
"os" "os"
"runtime/debug"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"text/template"
"time" "time"
"unsafe" "unsafe"
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/gocommon/logger"
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
@ -33,22 +35,26 @@ import (
"google.golang.org/api/option" "google.golang.org/api/option"
) )
var devflag = flagx.Bool("dev", false, "")
var noauth = flagx.Bool("noauth", false, "")
var ( var (
CollectionLink = common.CollectionName("link") CollectionLink = gocommon.CollectionName("link")
CollectionAuth = common.CollectionName("auth") CollectionAuth = gocommon.CollectionName("auth")
CollectionWhitelist = common.CollectionName("whitelist") CollectionWhitelist = gocommon.CollectionName("whitelist")
CollectionService = common.CollectionName("service") CollectionService = gocommon.CollectionName("service")
CollectionFile = common.CollectionName("file") CollectionAccount = gocommon.CollectionName("account")
CollectionBlock = common.CollectionName("block") CollectionFile = gocommon.CollectionName("file")
CollectionPlatformLoginToken = common.CollectionName("platform_login_token") //-- 각 플랫폼에 로그인 및 권한 받아오는 과정에 사용하는 Key CollectionBlock = gocommon.CollectionName("block")
CollectionUserToken = common.CollectionName("usertoken") CollectionPlatformLoginToken = gocommon.CollectionName("platform_login_token") //-- 각 플랫폼에 로그인 및 권한 받아오는 과정에 사용하는 Key
CollectionGamepotUserInfo = common.CollectionName("gamepot_userinfo") //-- 클라로부터 수집된 gamepot 정보 - server to server로 유효성이 검증되진 않았지만 수집은 한다. CollectionUserToken = gocommon.CollectionName("usertoken")
CollectionFirebaseUserInfo = common.CollectionName("firebase_userinfo") //-- Firebase UserInfo CollectionGamepotUserInfo = gocommon.CollectionName("gamepot_userinfo") //-- 클라로부터 수집된 gamepot 정보 - server to server로 유효성이 검증되진 않았지만 수집은 한다.
CollectionFirebaseUserInfo = gocommon.CollectionName("firebase_userinfo") //-- Firebase UserInfo
) )
const ( const (
AuthPlatformSteamSDK = "steam"
AuthPlatformFirebaseAuth = "firebase" AuthPlatformFirebaseAuth = "firebase"
AuthPlatformGamepot = "gamepot"
AuthPlatformGoogle = "google" AuthPlatformGoogle = "google"
AuthPlatformMicrosoft = "microsoft" AuthPlatformMicrosoft = "microsoft"
AuthPlatformApple = "apple" AuthPlatformApple = "apple"
@ -61,7 +67,7 @@ const (
) )
func SessionTTL() time.Duration { func SessionTTL() time.Duration {
if *common.Devflag { if *devflag {
return sessionTTLDev return sessionTTLDev
} }
@ -69,25 +75,10 @@ func SessionTTL() time.Duration {
} }
type mongoAuthCell struct { type mongoAuthCell struct {
src *common.Authinfo src *gocommon.Authinfo
} }
func init() { func (ac *mongoAuthCell) ToAuthinfo() *gocommon.Authinfo {
if *common.Devflag {
hostname, _ := os.Hostname()
CollectionLink = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionLink)))
CollectionAuth = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionAuth)))
CollectionWhitelist = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionWhitelist)))
CollectionService = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionService)))
CollectionBlock = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionBlock)))
CollectionPlatformLoginToken = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionPlatformLoginToken)))
CollectionUserToken = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionUserToken)))
CollectionGamepotUserInfo = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionGamepotUserInfo)))
CollectionFirebaseUserInfo = common.CollectionName(fmt.Sprintf("%s-%s", hostname, string(CollectionFirebaseUserInfo)))
}
}
func (ac *mongoAuthCell) ToAuthinfo() *common.Authinfo {
if ac.src == nil { if ac.src == nil {
logger.Error("mongoAuthCell ToAuthinfo failed. ac.src is nil") logger.Error("mongoAuthCell ToAuthinfo failed. ac.src is nil")
} }
@ -99,15 +90,15 @@ func (ac *mongoAuthCell) ToBytes() []byte {
return bt return bt
} }
func makeAuthCollection(mongoClient common.MongoClient, sessionTTL time.Duration) *common.AuthCollection { func makeAuthCollection(mongoClient gocommon.MongoClient, sessionTTL time.Duration) *gocommon.AuthCollection {
authcoll := common.MakeAuthCollection(sessionTTL) authcoll := gocommon.MakeAuthCollection(sessionTTL)
authcoll.SessionRemoved = func(sk string) { authcoll.SessionRemoved = func(sk string) {
skid, _ := primitive.ObjectIDFromHex(sk) skid, _ := primitive.ObjectIDFromHex(sk)
mongoClient.Delete(CollectionAuth, bson.M{ mongoClient.Delete(CollectionAuth, bson.M{
"sk": skid, "sk": skid,
}) })
} }
authcoll.QuerySession = func(sk string, token string) common.AuthinfoCell { authcoll.QuerySession = func(sk string, token string) gocommon.AuthinfoCell {
skid, _ := primitive.ObjectIDFromHex(sk) skid, _ := primitive.ObjectIDFromHex(sk)
var outcell mongoAuthCell var outcell mongoAuthCell
err := mongoClient.FindOneAs(CollectionAuth, bson.M{ err := mongoClient.FindOneAs(CollectionAuth, bson.M{
@ -129,37 +120,12 @@ func makeAuthCollection(mongoClient common.MongoClient, sessionTTL time.Duration
return authcoll return authcoll
} }
type apiTokenMap struct {
sync.Mutex
tokenToService map[string]string
}
func (tm *apiTokenMap) add(token string, serviceCode string) {
tm.Lock()
defer tm.Unlock()
tm.tokenToService[token] = serviceCode
}
func (tm *apiTokenMap) remove(token string) {
tm.Lock()
defer tm.Unlock()
delete(tm.tokenToService, token)
}
func (tm *apiTokenMap) get(token string) (code string, exists bool) {
tm.Lock()
defer tm.Unlock()
code, exists = tm.tokenToService[token]
return
}
type maingateConfig struct { type maingateConfig struct {
Mongo string `json:"maingate_mongodb_url"` Mongo string `json:"maingate_mongodb_url"`
SessionTTL int64 `json:"maingate_session_ttl"` SessionTTL int64 `json:"maingate_session_ttl"`
Autologin_ttl int64 `json:"autologin_ttl"` 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"` RedirectBaseUrl string `json:"redirect_base_url"`
GoogleClientId string `json:"google_client_id"` GoogleClientId string `json:"google_client_id"`
GoogleClientSecret string `json:"google_client_secret"` GoogleClientSecret string `json:"google_client_secret"`
@ -177,6 +143,20 @@ type maingateConfig struct {
GamepotProjectId string `json:"gamepot_project_id"` GamepotProjectId string `json:"gamepot_project_id"`
GamepotLoginCheckAPIURL string `json:"gamepot_logincheckapi_url"` GamepotLoginCheckAPIURL string `json:"gamepot_logincheckapi_url"`
FirebaseAdminSDKCredentialFile string `json:"firebase_admin_sdk_credentialfile"` 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 { type globalAdmins struct {
@ -191,100 +171,26 @@ func (ga *globalAdmins) parse() {
parsed[admin] = true parsed[admin] = true
} }
ga.emails = parsed ga.emails = parsed
ga.modtime = common.ConfigModTime() ga.modtime = gocommon.ConfigModTime()
}
type servicelist struct {
services unsafe.Pointer
}
func (sl *servicelist) init(total []*serviceDescription) error {
next := make(map[string]*serviceDescription)
for _, service := range total {
next[service.ServiceName] = service
}
atomic.StorePointer(&sl.services, unsafe.Pointer(&next))
return nil
}
func (sl *servicelist) add(s *serviceDescription) {
ptr := atomic.LoadPointer(&sl.services)
src := (*map[string]*serviceDescription)(ptr)
next := map[string]*serviceDescription{}
for k, v := range *src {
next[k] = v
}
next[s.ServiceName] = s
atomic.StorePointer(&sl.services, unsafe.Pointer(&next))
}
func (sl *servicelist) get(sn any) *serviceDescription {
ptr := atomic.LoadPointer(&sl.services)
src := *(*map[string]*serviceDescription)(ptr)
switch sn := sn.(type) {
case string:
return src[sn]
case primitive.ObjectID:
for _, desc := range src {
if desc.Id == sn {
return desc
}
}
}
return nil
}
func (sl *servicelist) all() map[string]*serviceDescription {
ptr := atomic.LoadPointer(&sl.services)
src := (*map[string]*serviceDescription)(ptr)
next := map[string]*serviceDescription{}
for k, v := range *src {
next[k] = v
}
return next
}
func (sl *servicelist) remove(uid primitive.ObjectID) (out *serviceDescription) {
ptr := atomic.LoadPointer(&sl.services)
src := (*map[string]*serviceDescription)(ptr)
next := map[string]*serviceDescription{}
var targetkey string
out = nil
for k, v := range *src {
next[k] = v
if v.Id == uid {
targetkey = k
out = v
}
}
delete(next, targetkey)
atomic.StorePointer(&sl.services, unsafe.Pointer(&next))
return
} }
// Maingate : // Maingate :
type Maingate struct { type Maingate struct {
maingateConfig maingateConfig
mongoClient common.MongoClient mongoClient gocommon.MongoClient
auths *common.AuthCollection auths *gocommon.AuthCollection
services servicelist //services servicelist
serviceptr unsafe.Pointer
admins unsafe.Pointer admins unsafe.Pointer
apiTokenToService apiTokenMap wl memberContainerPtr[string, *whitelistmember]
bl memberContainerPtr[primitive.ObjectID, *blockinfo]
tokenEndpoints map[string]string tokenEndpoints map[string]string
authorizationEndpoints map[string]string authorizationEndpoints map[string]string
userinfoEndpoint map[string]string userinfoEndpoint map[string]string
jwksUri map[string]string jwksUri map[string]string
webTemplate map[string]*template.Template
firebaseAppClient *auth.Client firebaseAppClient *auth.Client
firebaseAppContext context.Context firebaseAppContext context.Context
} }
@ -292,12 +198,12 @@ type Maingate struct {
// New : // New :
func New(ctx context.Context) (*Maingate, error) { func New(ctx context.Context) (*Maingate, error) {
var config maingateConfig var config maingateConfig
if err := common.LoadConfig(&config); err != nil { if err := gocommon.LoadConfig(&config); err != nil {
return nil, err return nil, err
} }
var admins globalAdmins var admins globalAdmins
if err := common.LoadConfig(&admins); err == nil { if err := gocommon.LoadConfig(&admins); err == nil {
admins.parse() admins.parse()
} }
@ -307,11 +213,7 @@ func New(ctx context.Context) (*Maingate, error) {
mg := Maingate{ mg := Maingate{
maingateConfig: config, maingateConfig: config,
services: servicelist{},
admins: unsafe.Pointer(&admins), admins: unsafe.Pointer(&admins),
apiTokenToService: apiTokenMap{
tokenToService: make(map[string]string),
},
tokenEndpoints: make(map[string]string), tokenEndpoints: make(map[string]string),
authorizationEndpoints: make(map[string]string), authorizationEndpoints: make(map[string]string),
userinfoEndpoint: make(map[string]string), userinfoEndpoint: make(map[string]string),
@ -320,10 +222,10 @@ func New(ctx context.Context) (*Maingate, error) {
err := mg.prepare(ctx) err := mg.prepare(ctx)
if err != nil { if err != nil {
logger.Error("mg.prepare() failed :", err)
return nil, err return nil, err
} }
if !*noauth {
opt := option.WithCredentialsFile(mg.FirebaseAdminSDKCredentialFile) opt := option.WithCredentialsFile(mg.FirebaseAdminSDKCredentialFile)
firebaseApp, err := firebase.NewApp(context.Background(), nil, opt) firebaseApp, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil { if err != nil {
@ -336,10 +238,16 @@ func New(ctx context.Context) (*Maingate, error) {
if err != nil { if err != nil {
logger.Println("FirebaseAppClient error getting Auth client:", err) logger.Println("FirebaseAppClient error getting Auth client:", err)
} }
}
return &mg, nil return &mg, nil
} }
func (mg *Maingate) service() *serviceDescription {
valptr := atomic.LoadPointer(&mg.serviceptr)
return (*serviceDescription)(valptr)
}
func (mg *Maingate) Destructor() { func (mg *Maingate) Destructor() {
logger.Println("maingate.Destructor") logger.Println("maingate.Destructor")
mg.mongoClient.Close() mg.mongoClient.Close()
@ -393,103 +301,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) { func (mg *Maingate) prepare(context context.Context) (err error) {
if err := mg.discoverOpenIdConfiguration(AuthPlatformMicrosoft, "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"); err != nil { if err := mg.discoverOpenIdConfiguration(AuthPlatformMicrosoft, "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"); err != nil {
return err return makeErrorWithStack(err)
} }
if err := mg.discoverOpenIdConfiguration("google", "https://accounts.google.com/.well-known/openid-configuration"); err != nil { if err := mg.discoverOpenIdConfiguration("google", "https://accounts.google.com/.well-known/openid-configuration"); err != nil {
return err return makeErrorWithStack(err)
} }
mg.webTemplate = make(map[string]*template.Template)
mg.webTemplate[AuthPlatformGamepot] = template.Must(template.ParseFiles("www/gamepot.html"))
// redis에서 env를 가져온 후에 // redis에서 env를 가져온 후에
mg.mongoClient, err = common.NewMongoClient(context, mg.Mongo, "maingate") mg.mongoClient, err = gocommon.NewMongoClient(context, mg.Mongo, "maingate")
if err != nil { if err != nil {
return makeErrorWithStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionCouponUse, map[string]bson.D{
"idrounds": {{Key: "_id", Value: 1}, {Key: "rounds", Value: 1}},
}); err != nil {
return err return err
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{
"skonly": {{Key: "sk", Value: 1}}, "skonly": {{Key: "sk", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
"platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}}, "platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
"emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}}, "emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeIndices(CollectionWhitelist, map[string]bson.D{ if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{
"service": {{Key: "service", Value: 1}}, "accid": {{Key: "accid", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
}
if err = mg.mongoClient.MakeIndices(CollectionFile, map[string]bson.D{
"service": {{Key: "service", Value: 1}},
}); err != nil {
return err
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{
"sk": {{Key: "service", Value: 1}, {Key: "key", Value: 1}}, "keyonly": {{Key: "key", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
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 { if err = mg.mongoClient.MakeExpireIndex(CollectionWhitelist, 10); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeExpireIndex(CollectionAuth, int32(mg.SessionTTL+300)); err != nil { if err = mg.mongoClient.MakeExpireIndex(CollectionAuth, int32(mg.SessionTTL+300)); err != nil {
return err return makeErrorWithStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionBlock, map[string]bson.D{
"codeaccid": {{Key: "code", Value: 1}, {Key: "accid", Value: 1}},
}); err != nil {
return err
} }
if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil { if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionPlatformLoginToken, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionPlatformLoginToken, map[string]bson.D{
"platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}}, "platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(mg.SessionTTL+300)); err != nil { if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(mg.SessionTTL+300)); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionUserToken, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionUserToken, map[string]bson.D{
"platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}}, "platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{
"gamepotuserid": {{Key: "gamepotuserid", Value: 1}}, "gamepotuserid": {{Key: "gamepotuserid", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{ if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{
"firebaseuserid": {{Key: "firebaseuserid", Value: 1}}, "firebaseuserid": {{Key: "firebaseuserid", Value: 1}},
}); err != nil { }); err != nil {
return err return makeErrorWithStack(err)
} }
mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second))) mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second)))
@ -501,7 +413,7 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{ if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{
"link": 1, "link": 1,
})); err != nil { })); err != nil {
return err return makeErrorWithStack(err)
} }
for _, pre := range preall { for _, pre := range preall {
@ -511,64 +423,119 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
} }
logger.Println("saving files :", pre.Link) logger.Println("saving files :", pre.Link)
var fulldoc fileDocumentDesc var fulldoc FileDocumentDesc
err = mg.mongoClient.FindOneAs(CollectionFile, bson.M{ err = mg.mongoClient.FindOneAs(CollectionFile, bson.M{
"_id": pre.Id, "_id": pre.Id,
}, &fulldoc) }, &fulldoc)
if err != nil { if err != nil {
return makeErrorWithStack(err)
}
err = fulldoc.Save()
if err != nil {
return makeErrorWithStack(err)
}
}
var whites []*whitelistmember
if err := mg.mongoClient.AllAs(CollectionWhitelist, &whites, options.Find().SetReturnKey(false)); err != nil {
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.wl.watchCollection(context, CollectionWhitelist, mg.mongoClient)
go mg.bl.watchCollection(context, CollectionBlock, mg.mongoClient)
return nil
}
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 {
return err return err
} }
err = fulldoc.save()
if len(allServices) > 0 {
only := allServices[0]
only.prepare(mg)
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(only))
} else {
empty := serviceDescription{
ServiceDescriptionSummary: ServiceDescriptionSummary{
Id: primitive.NewObjectID(),
},
}
if *devflag {
host, _ := os.Hostname()
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
}
ipaddr := "127.0.0.1"
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil && ipnet.IP.IsPrivate() {
ipaddr = ipnet.IP.String()
}
}
}
empty.Divisions = map[string]*Division{
host: {
DivisionForUser: DivisionForUser{
Priority: 0,
State: DivisionState_FullOpen,
LockCreateChar: false,
},
Url: fmt.Sprintf("http://%s/warehouse", ipaddr),
},
}
}
empty.prepare(mg)
atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(&empty))
filter := bson.M{"_id": empty.Id}
_, _, err := mg.mongoClient.Update(CollectionService, filter, bson.M{
"$set": &empty,
}, options.Update().SetUpsert(true))
if err != nil { if err != nil {
return err return err
} }
} }
go watchAuthCollection(context, mg.auths, mg.mongoClient) logger.Println("Service is registered :", mg.service().ServiceCode)
go mg.watchWhitelistCollection(context) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), func(w http.ResponseWriter, r *http.Request) {
mg.service().serveHTTP(w, r)
return nil })
} serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "api/"), mg.api)
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "query/"), mg.query)
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
logger.Println(CollectionService)
if err := mg.mongoClient.FindAllAs(CollectionService, bson.M{}, &allServices, options.Find().SetReturnKey(false)); err != nil {
return err
}
for _, service := range allServices {
if err := service.prepare(mg); err != nil {
return err
}
}
logger.Println("RegisterHandlers...")
mg.services.init(allServices)
for _, service := range allServices {
if service.Closed {
continue
}
logger.Println("ServiceCode:", service.ServiceCode)
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, service.ServiceCode, "/"), service)
}
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "api", "/"), mg.api)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "query", "/"), mg.query)
configraw, _ := json.Marshal(mg.maingateConfig) configraw, _ := json.Marshal(mg.maingateConfig)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "config"), func(w http.ResponseWriter, r *http.Request) { var convertedConfig map[string]any
if err := json.Unmarshal(configraw, &convertedConfig); err != nil {
return err
}
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "config"), func(w http.ResponseWriter, r *http.Request) {
defer func() {
s := recover()
if s != nil {
logger.Error(s)
}
}()
if !*devflag {
apitoken := r.Header.Get("MG-X-API-TOKEN") apitoken := r.Header.Get("MG-X-API-TOKEN")
if len(apitoken) == 0 { if len(apitoken) == 0 {
logger.Println("MG-X-API-TOKEN is missing") logger.Println("MG-X-API-TOKEN is missing")
@ -576,14 +543,16 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
return return
} }
_, exists := mg.apiTokenToService.get(apitoken) apitokenObj, _ := primitive.ObjectIDFromHex(apitoken)
if !exists { if mg.service().isValidToken(apitokenObj) {
logger.Println("MG-X-API-TOKEN is invalid :", apitoken) convertedConfig["divisions"] = mg.service().Divisions
w.WriteHeader(http.StatusBadRequest) }
return } else {
convertedConfig["divisions"] = mg.service().Divisions
} }
w.Write(configraw) enc := json.NewEncoder(w)
enc.Encode(convertedConfig)
}) })
if err := os.MkdirAll("static", os.ModePerm); err != nil { if err := os.MkdirAll("static", os.ModePerm); err != nil {
@ -591,34 +560,44 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
return err return err
} }
fsx := http.FileServer(http.Dir("./console")) cfsx := http.FileServer(http.Dir("console"))
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, "console", "/"), http.StripPrefix("/console/", fsx)) pattern := gocommon.MakeHttpHandlerPattern(prefix, "console", "/")
serveMux.Handle(pattern, http.StripPrefix(pattern, cfsx))
logger.Println("maingate console registered :", pattern)
ssx := http.FileServer(http.Dir("./static")) staticfs := http.FileServer(http.Dir("static"))
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, "static", "/"), http.StripPrefix("/static/", ssx)) pattern = gocommon.MakeHttpHandlerPattern(prefix, "static", "/")
serveMux.Handle(pattern, http.StripPrefix(pattern, staticfs))
logger.Println("maingate static registered :", pattern)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformGoogle), mg.platform_google_get_login_url) fbafs := http.FileServer(http.Dir("fba"))
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformGoogle), mg.platform_google_authorize) pattern = gocommon.MakeHttpHandlerPattern(prefix, "fba", "/")
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformGoogle), mg.platform_google_authorize_result) serveMux.Handle(pattern, http.StripPrefix(pattern, fbafs))
logger.Println("google_analytics static registered :", pattern)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformMicrosoft), mg.platform_microsoft_get_login_url) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "fba", "fb-ga.min.js"), mg.google_analytics_js)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformMicrosoft), mg.platform_microsoft_authorize) logger.Println("google_analytics.js static registered :", pattern)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformMicrosoft), mg.platform_microsoft_authorize_result)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformTwitter), mg.platform_twitter_get_login_url) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformGoogle), mg.platform_google_get_login_url)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformTwitter), mg.platform_twitter_authorize) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformGoogle), mg.platform_google_authorize)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformTwitter), mg.platform_twitter_authorize_result) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformGoogle), mg.platform_google_authorize_result)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformApple), mg.platform_apple_get_login_url) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformMicrosoft), mg.platform_microsoft_get_login_url)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformApple), mg.platform_apple_authorize) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformMicrosoft), mg.platform_microsoft_authorize)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformApple), mg.platform_apple_authorize_result) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformMicrosoft), mg.platform_microsoft_authorize_result)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformGamepot), mg.platform_gamepot_get_login_url) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformTwitter), mg.platform_twitter_get_login_url)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformGamepot), mg.platform_gamepot_authorize) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformTwitter), mg.platform_twitter_authorize)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_sdk", AuthPlatformGamepot), mg.platform_gamepot_authorize_sdk) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformTwitter), mg.platform_twitter_authorize_result)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformFirebaseAuth), mg.platform_firebaseauth_get_login_url) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "request_login_url", AuthPlatformApple), mg.platform_apple_get_login_url)
serveMux.HandleFunc(common.MakeHttpHandlerPattern(prefix, "authorize_sdk", AuthPlatformFirebaseAuth), mg.platform_firebaseauth_authorize_sdk) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize", AuthPlatformApple), mg.platform_apple_authorize)
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "authorize_result", AuthPlatformApple), mg.platform_apple_authorize_result)
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.watchServiceCollection(ctx, serveMux, prefix)
go mg.watchFileCollection(ctx, serveMux, prefix) go mg.watchFileCollection(ctx, serveMux, prefix)
@ -657,6 +636,7 @@ func (mg *Maingate) query(w http.ResponseWriter, r *http.Request) {
return return
} }
if !*devflag {
apitoken := r.Header.Get("MG-X-API-TOKEN") apitoken := r.Header.Get("MG-X-API-TOKEN")
if len(apitoken) == 0 { if len(apitoken) == 0 {
logger.Println("MG-X-API-TOKEN is missing") logger.Println("MG-X-API-TOKEN is missing")
@ -664,17 +644,12 @@ func (mg *Maingate) query(w http.ResponseWriter, r *http.Request) {
return return
} }
servicecode, exists := mg.apiTokenToService.get(apitoken) apitokenObj, _ := primitive.ObjectIDFromHex(apitoken)
if !exists { if !mg.service().isValidToken(apitokenObj) {
logger.Println("MG-X-API-TOKEN is invalid :", apitoken) logger.Println("MG-X-API-TOKEN is invalid :", apitoken)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
if info.ServiceCode != servicecode {
logger.Println("session is not for this service :", info.ServiceCode, servicecode)
w.WriteHeader(http.StatusBadRequest)
return
} }
bt, _ := json.Marshal(info) bt, _ := json.Marshal(info)
@ -793,8 +768,8 @@ func (mg *Maingate) updateUserinfo(info usertokeninfo) (bool, string, string) {
success, userid, email = mg.platform_microsoft_getuserinfo(info) success, userid, email = mg.platform_microsoft_getuserinfo(info)
case AuthPlatformGoogle: case AuthPlatformGoogle:
success, userid, email = mg.platform_google_getuserinfo(info) success, userid, email = mg.platform_google_getuserinfo(info)
case AuthPlatformGamepot: case AuthPlatformSteamSDK:
success, userid, email = mg.platform_gamepot_getuserinfo(info) success, userid, email = mg.platform_steamsdk_getuserinfo(info)
case AuthPlatformFirebaseAuth: case AuthPlatformFirebaseAuth:
success, userid, email = mg.platform_firebase_getuserinfo(info) success, userid, email = mg.platform_firebase_getuserinfo(info)
} }
@ -835,14 +810,19 @@ func (mg *Maingate) getProviderInfo(platform string, uid string) (string, string
if provider == "" || providerid == "" { if provider == "" || providerid == "" {
return "", "", errors.New("getProviderInfo - firebase info not found: " + 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: default:
provider = platform provider = platform
providerid = uid providerid = uid
}
if provider == "" || providerid == "" { if provider == "" || providerid == "" {
return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid) return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid)
} }
}
return provider, providerid, nil return provider, providerid, nil
@ -1004,3 +984,31 @@ func JWTparseCode(keyurl string, code string) (string, string, string) {
//--- nonce 체크 필요하다. //--- nonce 체크 필요하다.
return claims["sub"].(string), email, 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
View 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)
}
}
}

View File

@ -289,7 +289,7 @@ func (mg *Maingate) platform_apple_getuserinfo(refreshToken string) (bool, strin
} }
if respReferesh.Error != "" { if respReferesh.Error != "" {
logger.Error("apple returned an error: %s - %s\n", respReferesh.Error, respReferesh.ErrorDescription) logger.Errorf("apple returned an error: %s - %s\n", respReferesh.Error, respReferesh.ErrorDescription)
return false, "", "" return false, "", ""
} }

View File

@ -1,344 +0,0 @@
package core
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"time"
"repositories.action2quare.com/ayo/gocommon/logger"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
type GamepotTemplate struct {
RedirectBaseUrl string
State string
}
type Gamepot_LoginValidationResponse struct {
Message string `json:"message"`
Status int `json:"status"`
}
func (mg *Maingate) platform_gamepot_get_login_url(w http.ResponseWriter, r *http.Request) {
browserinfo, err := mg.GetUserBrowserInfo(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
logger.Error(err)
return
}
existid := r.URL.Query().Get("existid")
withSDK := r.URL.Query().Get("withSDK")
//fmt.Println("existid =>", existid)
if existid != "" {
//기존 계정이 있는 경우에는 그 계정 부터 조회한다.
info, err := mg.getUserTokenWithCheck(AuthPlatformGamepot, existid, browserinfo)
if err == nil {
if info.token != "" {
params := url.Values{}
params.Add("id", existid)
params.Add("authtype", AuthPlatformGamepot)
if withSDK == "1" {
w.Write([]byte("?" + params.Encode()))
} else {
http.Redirect(w, r, "actionsquare://login?"+params.Encode(), http.StatusSeeOther)
}
return
}
}
}
sessionkey := mg.GeneratePlatformLoginNonceKey()
nonce := mg.GeneratePlatformLoginNonceKey()
mg.mongoClient.Delete(CollectionPlatformLoginToken, bson.M{
"platform": AuthPlatformGamepot,
"key": sessionkey,
})
_, _, err = mg.mongoClient.Update(CollectionPlatformLoginToken, bson.M{
"_id": primitive.NewObjectID(),
}, bson.M{
"$setOnInsert": bson.M{
"platform": AuthPlatformGamepot,
"key": sessionkey,
"nonce": nonce,
"brinfo": browserinfo,
},
}, options.Update().SetUpsert(true))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
logger.Error(err)
return
}
// set cookie for storing token
cookie := http.Cookie{
Name: "LoginFlowContext_SessionKey",
Value: sessionkey,
Expires: time.Now().Add(1 * time.Hour),
//SameSite: http.SameSiteStrictMode,
SameSite: http.SameSiteLaxMode,
// HttpOnly: false,
Secure: true,
Path: "/",
}
http.SetCookie(w, &cookie)
if withSDK == "1" {
params := url.Values{}
params.Add("nonce", nonce)
w.Write([]byte("?" + params.Encode()))
} else {
var templateVar GamepotTemplate
templateVar.RedirectBaseUrl = mg.RedirectBaseUrl
templateVar.State = nonce
mg.webTemplate[AuthPlatformGamepot].Execute(w, templateVar)
}
}
func (mg *Maingate) platform_gamepot_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
}
cookie, err := r.Cookie("LoginFlowContext_SessionKey")
if err != nil {
logger.Println("Session not found", err)
w.WriteHeader(http.StatusBadRequest)
return
}
r.ParseForm()
code := r.Form.Get("code")
state := r.Form.Get("state")
gamepotmemberId := r.Form.Get("id")
gamepotnickname := r.Form.Get("nickname")
gamepotprovider := r.Form.Get("provider")
gamepotproviderId := r.Form.Get("providerId")
// gamepotverify := r.Form.Get("verify")
// gamepotagree := r.Form.Get("agree")
bSuccess, Result := mg.platform_gamepot_authorize_raw(w, brinfo, code, state, cookie.Value, gamepotmemberId, gamepotnickname, gamepotprovider, gamepotproviderId)
if bSuccess {
http.Redirect(w, r, "actionsquare://login?"+Result, http.StatusSeeOther)
} else {
http.Redirect(w, r, "actionsquare://error", http.StatusSeeOther)
}
}
type GamePotSDKAuthInfo struct {
Code string `json:"code"`
State string `json:"state"`
MemberId string `json:"id"`
Nickname string `json:"nickname"`
Provider string `json:"provider"`
ProviderId string `json:"providerId"`
// Verify string `json:"verify"`
// Agree string `json:"agree"`
}
func (mg *Maingate) platform_gamepot_authorize_sdk(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
}
cookie, err := r.Cookie("LoginFlowContext_SessionKey")
if err != nil {
logger.Println("Session not found", err)
w.WriteHeader(http.StatusBadRequest)
return
}
var authinfo GamePotSDKAuthInfo
err = json.NewDecoder(r.Body).Decode(&authinfo)
if err != nil {
logger.Println("authinfo decoding fail:", err)
w.WriteHeader(http.StatusBadRequest)
return
}
bSuccess, Result := mg.platform_gamepot_authorize_raw(w, brinfo, authinfo.Code, authinfo.State,
cookie.Value, authinfo.MemberId, authinfo.Nickname, authinfo.Provider, authinfo.ProviderId)
if bSuccess {
w.Write([]byte("?" + Result))
//http.Redirect(w, r, "actionsquare://login?"+Result, http.StatusSeeOther)
} else {
http.Redirect(w, r, "actionsquare://error", http.StatusSeeOther)
}
}
func (mg *Maingate) platform_gamepot_authorize_raw(w http.ResponseWriter, brinfo, code, state, cookieSessionKey, gamepotmemberId, gamepotnickname, gamepotprovider, gamepotproviderId string) (bool, string) {
found, err := mg.mongoClient.FindOne(CollectionPlatformLoginToken, bson.M{
"platform": AuthPlatformGamepot,
"key": cookieSessionKey,
})
if err != nil {
logger.Println("LoginFlowContext_SessionKey find key :", err)
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
if found == nil {
logger.Println("LoginFlowContext_SessionKey not found")
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
if cookieSessionKey != found["key"] {
logger.Println("LoginFlowContext_SessionKey key not match")
logger.Println(cookieSessionKey)
logger.Println(found["key"])
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
if state != found["nonce"] {
logger.Println("LoginFlowContext_SessionKey nonce not match")
logger.Println(state)
logger.Println(found["nonce"])
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
if brinfo != found["brinfo"] { //-- 로그인 시작점과 인증점의 브라우저 혹은 접속지 정보가 다르다?
logger.Println("LoginFlowContext_SessionKey brinfo not match ")
logger.Println(brinfo)
logger.Println(found["brinfo"])
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
//=================
params := url.Values{}
params.Add("projectId", mg.GamepotProjectId)
params.Add("memberId", gamepotmemberId)
params.Add("token", code)
var respLoginCheck Gamepot_LoginValidationResponse
content := params.Encode()
resp, _ := http.Post(mg.GamepotLoginCheckAPIURL, "application/json", bytes.NewBuffer([]byte(content)))
if resp != nil {
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &respLoginCheck)
} else {
logger.Println("gamepot logincheck fail.")
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
// fmt.Println("==============================")
// fmt.Println("respLoginCheck.Status:", respLoginCheck.Status)
// fmt.Println("respLoginCheck.Message:", respLoginCheck.Message)
// fmt.Println("==============================")
if respLoginCheck.Status != 0 {
logger.Errorf("gamepot login fail:", respLoginCheck.Message)
w.WriteHeader(http.StatusBadRequest)
return false, ""
}
acceestoken_expire_time := time.Date(2999, 1, int(time.January), 0, 0, 0, 0, time.UTC).Unix()
if gamepotmemberId != "" && gamepotprovider != "" && gamepotproviderId != "" {
var info usertokeninfo
info.platform = AuthPlatformGamepot
info.userid = gamepotmemberId
//== memberid 제외하고는 모두 client로 부터 온 값이기 때문에 유효성이 확인된 값이 아니다. 하지만, 참조용으로 사용은 한다.
// 정확한 정보는 gamepotid를 gamepot dashboard에서 조회해서 확인 할 수 밖에 없다.
info.token = gamepotprovider + "-" + gamepotproviderId
info.brinfo = brinfo
info.accesstoken = ""
info.accesstoken_expire_time = acceestoken_expire_time
mg.setUserToken(info)
mg.mongoClient.Delete(CollectionGamepotUserInfo, bson.M{
"gamepotuserid": info.userid,
})
_, _, err := mg.mongoClient.Update(CollectionGamepotUserInfo, bson.M{
"_id": primitive.NewObjectID(),
}, bson.M{
"$setOnInsert": bson.M{
"gamepotuserid": gamepotmemberId,
"gamepotnickname": gamepotnickname,
"gamepotprovider": gamepotprovider,
"gamepotproviderId": gamepotproviderId,
// "gamepotverify": gamepotverify,
// "gamepotagree": gamepotagree,
"updatetime": time.Now(),
},
}, options.Update().SetUpsert(true))
if err != nil {
logger.Error(err)
}
params := url.Values{}
params.Add("id", gamepotmemberId)
params.Add("authtype", AuthPlatformGamepot)
return true, params.Encode()
}
return false, ""
}
func (mg *Maingate) platform_gamepot_getuserinfo(info usertokeninfo) (bool, string, string) {
found, err := mg.mongoClient.FindOne(CollectionGamepotUserInfo, bson.M{
"gamepotuserid": info.userid,
})
if err != nil {
logger.Error(err)
return false, "", ""
}
if found == nil {
logger.Error(errors.New("gamepot info not found: " + info.userid))
return false, "", ""
}
gamepotprovider := found["gamepotprovider"].(string)
gamepotproviderId := found["gamepotproviderId"].(string)
if gamepotprovider+"-"+gamepotproviderId != info.token {
logger.Println("gamepot info not match..") //-- token은 플랫폼종류+플랫폼ID로 구성했는데... 검증할 방법이 없어서 client로 부터 온값을 쓴다. 그래도 유저가 조작하지 않는 이상 일치해야 된다.
logger.Println(info.token)
logger.Println(gamepotprovider + "-" + gamepotproviderId)
return false, "", ""
}
tempEmail := info.userid + "@gamepot" //-- 게임팟은 email을 안줘서 일단 gamepotid기준으로 임시값을 할당한다.
return true, info.userid, tempEmail
}

126
core/platformsteam.go Normal file
View 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
}

View File

@ -222,14 +222,13 @@ func (mg *Maingate) platform_twitter_authorize_result(w http.ResponseWriter, r *
func (mg *Maingate) platform_twitter_getuserinfo(token, secret string) (bool, string, string) { func (mg *Maingate) platform_twitter_getuserinfo(token, secret string) (bool, string, string) {
result := mg.CallTwitterAPI("https://api.twitter.com/2/users/me", "GET", token, secret, mg.GeneratePlatformLoginNonceKey()) result := mg.CallTwitterAPI("https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true", "GET", token, secret, mg.GeneratePlatformLoginNonceKey())
var TwitterUserInfo struct { var TwitterUserInfo struct {
Data struct { Id string `json:"id_str"`
Id string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Username string `json:"username"` Username string `json:"screen_name"`
} `json:"data"` Email string `json:"email"`
} }
err := json.Unmarshal([]byte(result), &TwitterUserInfo) err := json.Unmarshal([]byte(result), &TwitterUserInfo)
@ -240,12 +239,13 @@ func (mg *Maingate) platform_twitter_getuserinfo(token, secret string) (bool, st
// fmt.Println("=====================") // fmt.Println("=====================")
// fmt.Println(result) // fmt.Println(result)
// fmt.Println(TwitterUserInfo.Data.Id) // fmt.Println(TwitterUserInfo.Id)
// fmt.Println(TwitterUserInfo.Data.Name) // fmt.Println(TwitterUserInfo.Name)
// fmt.Println(TwitterUserInfo.Data.Username) // fmt.Println(TwitterUserInfo.Username)
// fmt.Println(TwitterUserInfo.Email)
// fmt.Println("=====================") // fmt.Println("=====================")
return true, TwitterUserInfo.Data.Id, "" return true, TwitterUserInfo.Id, TwitterUserInfo.Email
} }
func (mg *Maingate) CallTwitterAPI_WithAPPKey(requesturl, method, nonce string) string { func (mg *Maingate) CallTwitterAPI_WithAPPKey(requesturl, method, nonce string) string {

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,16 @@
package core package core
import ( import (
"bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json"
"net/http" "net/http"
"os" "os"
"path" "path"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe"
common "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
@ -25,7 +24,7 @@ type authPipelineDocument struct {
DocumentKey struct { DocumentKey struct {
Id primitive.ObjectID `bson:"_id"` Id primitive.ObjectID `bson:"_id"`
} `bson:"documentKey"` } `bson:"documentKey"`
Authinfo *common.Authinfo `bson:"fullDocument"` Authinfo *gocommon.Authinfo `bson:"fullDocument"`
} }
type servicePipelineDocument struct { type servicePipelineDocument struct {
@ -41,107 +40,7 @@ type filePipelineDocument struct {
DocumentKey struct { DocumentKey struct {
Id primitive.ObjectID `bson:"_id"` Id primitive.ObjectID `bson:"_id"`
} `bson:"documentKey"` } `bson:"documentKey"`
File *fileDocumentDesc `bson:"fullDocument"` File *FileDocumentDesc `bson:"fullDocument"`
}
type whilelistPipelineDocument struct {
OperationType string `bson:"operationType"`
DocumentKey struct {
Id primitive.ObjectID `bson:"_id"`
} `bson:"documentKey"`
Member *whitelistmember `bson:"fullDocument"`
}
func (mg *Maingate) watchWhitelistCollection(parentctx context.Context) {
defer func() {
s := recover()
if s != nil {
logger.Error(s)
}
}()
matchStage := bson.D{
{
Key: "$match", Value: bson.D{
{Key: "operationType", Value: bson.D{
{Key: "$in", Value: bson.A{
"update",
"insert",
}},
}},
},
}}
projectStage := bson.D{
{
Key: "$project", Value: bson.D{
{Key: "documentKey", Value: 1},
{Key: "operationType", Value: 1},
{Key: "fullDocument", Value: 1},
},
},
}
var stream *mongo.ChangeStream
var err error
var ctx context.Context
for {
if stream == nil {
stream, err = mg.mongoClient.Watch(CollectionWhitelist, mongo.Pipeline{matchStage, projectStage})
if err != nil {
logger.Error("watchWhitelistCollection watch failed :", err)
time.Sleep(time.Minute)
continue
}
ctx = context.TODO()
}
changed := stream.TryNext(ctx)
if ctx.Err() != nil {
logger.Error("watchWhitelistCollection stream.TryNext failed. process should be restarted! :", ctx.Err().Error())
break
}
if changed {
var data whilelistPipelineDocument
if err := stream.Decode(&data); err == nil {
ot := data.OperationType
switch ot {
case "insert":
// 새 화이트리스트 멤버
if svc := mg.services.get(data.Member.Service); svc != nil {
svc.wl.add(data.Member)
}
case "update":
if svc := mg.services.get(data.Member.Service); svc != nil {
if data.Member.Expired != 0 {
logger.Println("whitelist member is removed :", *data.Member)
svc.wl.remove(data.Member.Email)
} else {
logger.Println("whitelist member is updated :", *data.Member)
svc.wl.add(data.Member)
}
}
}
} else {
logger.Error("watchWhitelistCollection stream.Decode failed :", err)
}
} else if stream.Err() != nil || stream.ID() == 0 {
select {
case <-ctx.Done():
logger.Println("watchWhitelistCollection is done")
stream.Close(ctx)
return
case <-time.After(time.Second):
logger.Error("watchWhitelistCollection stream error :", stream.Err())
stream.Close(ctx)
stream = nil
}
} else {
time.Sleep(time.Second)
}
}
} }
func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *http.ServeMux, prefix string) { func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *http.ServeMux, prefix string) {
@ -216,7 +115,7 @@ func (mg *Maingate) watchFileCollection(parentctx context.Context, serveMux *htt
if err := stream.Decode(&data); err == nil { if err := stream.Decode(&data); err == nil {
switch data.OperationType { switch data.OperationType {
case "insert": case "insert":
data.File.save() data.File.Save()
case "delete": case "delete":
subfolder := hex.EncodeToString(data.DocumentKey.Id[:4]) subfolder := hex.EncodeToString(data.DocumentKey.Id[:4])
@ -242,7 +141,6 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
Key: "$match", Value: bson.D{ Key: "$match", Value: bson.D{
{Key: "operationType", Value: bson.D{ {Key: "operationType", Value: bson.D{
{Key: "$in", Value: bson.A{ {Key: "$in", Value: bson.A{
"delete",
"insert", "insert",
"update", "update",
"replace", "replace",
@ -290,9 +188,11 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
if err := data.Service.prepare(mg); err != nil { if err := data.Service.prepare(mg); err != nil {
logger.Error("service cannot be prepared :", data.Service, err) logger.Error("service cannot be prepared :", data.Service, err)
} else { } else {
// 내가 임시로 가지고 있던 서비스일 수 있다.
if mg.service().Id == data.Service.Id {
logger.Println("service is on the board! :", data.Service) logger.Println("service is on the board! :", data.Service)
mg.services.add(data.Service) atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(data.Service))
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, data.Service.ServiceCode, "/"), data.Service) }
} }
case "replace": case "replace":
@ -300,63 +200,7 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
case "update": case "update":
data.Service.prepare(mg) data.Service.prepare(mg)
if old := mg.services.get(data.Service.ServiceName); old != nil { atomic.StorePointer(&mg.serviceptr, unsafe.Pointer(data.Service))
atomic.SwapPointer(&old.divisionsForUsersSerialized, data.Service.divisionsForUsersSerialized)
atomic.SwapPointer(&old.divisionsSerialized, data.Service.divisionsSerialized)
atomic.SwapPointer(&old.apiUsers, data.Service.apiUsers)
atomic.SwapPointer(&old.serviceSerialized, data.Service.serviceSerialized)
atomic.SwapPointer(&old.serviceSummarySerialized, data.Service.serviceSummarySerialized)
for _, token := range old.ServerApiTokens {
mg.apiTokenToService.remove(token.Hex())
}
for _, token := range data.Service.ServerApiTokens {
mg.apiTokenToService.add(token.Hex(), data.Service.ServiceCode)
}
if data.Service.UseWhitelist {
atomic.StoreInt32(&old.wl.working, 1)
} else {
atomic.StoreInt32(&old.wl.working, 0)
}
old.Closed = data.Service.Closed
if old.Closed {
atomic.StoreInt32(&old.closed, 1)
} else {
atomic.StoreInt32(&old.closed, 0)
}
atomic.SwapPointer(&old.wl.emailptr, data.Service.wl.emailptr)
old.Divisions = data.Service.Divisions
for _, div := range old.Divisions {
var req *http.Request
if div.State == DivisionState_FullOpen {
req, _ = http.NewRequest("POST", div.Url+"/maingate", nil)
} else if div.Maintenance != nil {
bt, _ := json.Marshal(div.Maintenance)
req, _ = http.NewRequest("POST", div.Url+"/maingate", bytes.NewBuffer(bt))
}
if req != nil {
// MG-X-API-TOKEN
req.Header.Add("MG-X-API-TOKEN", old.ServerApiTokens[0].Hex())
if resp, err := http.DefaultClient.Do(req); err == nil {
resp.Body.Close()
}
}
}
} else if !data.Service.Closed {
logger.Println("service is on the board! :", data.Service)
mg.services.add(data.Service)
serveMux.Handle(common.MakeHttpHandlerPattern(prefix, data.Service.ServiceCode, "/"), data.Service)
}
case "delete":
if deleted := mg.services.remove(data.DocumentKey.Id); deleted != nil {
logger.Println("service is closed :", data.Service)
atomic.AddInt32(&deleted.closed, 1)
}
} }
} else { } else {
logger.Error("watchServiceCollection stream.Decode failed :", err) logger.Error("watchServiceCollection stream.Decode failed :", err)
@ -379,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() { defer func() {
s := recover() s := recover()
if s != nil { if s != nil {

537
fba/js.js Normal file

File diff suppressed because one or more lines are too long

11
fba/track-event.html Normal file
View 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"> -->

48
go.mod
View File

@ -1,23 +1,23 @@
module repositories.action2quare.com/ayo/maingate module repositories.action2quare.com/ayo/maingate
go 1.19 go 1.18
require ( require (
firebase.google.com/go v3.13.0+incompatible firebase.google.com/go v3.13.0+incompatible
github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible
go.mongodb.org/mongo-driver v1.11.6 go.mongodb.org/mongo-driver v1.11.7
google.golang.org/api v0.123.0 google.golang.org/api v0.128.0
repositories.action2quare.com/ayo/gocommon v0.0.0-20230614091557-9b877d9a732c repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb
) )
require ( require (
cloud.google.com/go v0.110.2 // indirect cloud.google.com/go v0.110.2 // indirect
cloud.google.com/go/compute v1.19.0 // indirect cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/firestore v1.10.0 // indirect cloud.google.com/go/firestore v1.10.0 // indirect
cloud.google.com/go/iam v0.13.0 // indirect cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/longrunning v0.4.2 // indirect cloud.google.com/go/longrunning v0.5.1 // indirect
cloud.google.com/go/storage v1.29.0 // indirect cloud.google.com/go/storage v1.30.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect
@ -27,28 +27,30 @@ require (
github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-cmp v0.5.9 // indirect
github.com/google/s2a-go v0.1.4 // indirect github.com/google/s2a-go v0.1.4 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/compress v1.16.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/montanaflynn/stats v0.7.1 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.9.0 // indirect golang.org/x/crypto v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sync v0.2.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.55.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.56.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
) )

90
go.sum
View File

@ -2,18 +2,18 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/firestore v1.10.0 h1:FG5C49ukKKqyljY+XNRZGae1HZaiVe7aoqi2BipnBuM= cloud.google.com/go/firestore v1.10.0 h1:FG5C49ukKKqyljY+XNRZGae1HZaiVe7aoqi2BipnBuM=
cloud.google.com/go/firestore v1.10.0/go.mod h1:eAeoQCV8F35Mcy4k8ZrQbcSYZOayIwoiU7ZJ6xzH1+o= cloud.google.com/go/firestore v1.10.0/go.mod h1:eAeoQCV8F35Mcy4k8ZrQbcSYZOayIwoiU7ZJ6xzH1+o=
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/longrunning v0.4.2 h1:WDKiiNXFTaQ6qz/G8FCOkuY9kJmOJGY67wPUC1M2RbE= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ= cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -86,20 +86,22 @@ github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkj
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=
github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
@ -125,25 +127,29 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o= go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs=
go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -162,20 +168,20 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -186,8 +192,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -196,8 +202,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -212,8 +218,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.123.0 h1:yHVU//vA+qkOhm4reEC9LtzHVUCN/IqqNRl1iQ9xE20= google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg=
google.golang.org/api v0.123.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@ -222,8 +228,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
@ -232,8 +242,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= google.golang.org/grpc v1.56.0 h1:+y7Bs8rtMd07LeXmL3NxcTLn7mUkbKZqEpPhMNkwJEE=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -258,7 +268,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/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-20230612013915-5950ff4bb82e h1:m0jo1r+2NtBfxwj92e6EVaBZpzTDT6Hq7D93vWO4h9Y= repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb h1:Rdf6uhBIWunRLZ2LIT1hSovYXxZoOzx9mdSK5bjWpos=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230612013915-5950ff4bb82e/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns= repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230614091557-9b877d9a732c h1:fhCuu0jFps8T1sN8hO0fGnatvNDW6VwM96PV26EA3T4=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230614091557-9b877d9a732c/go.mod h1:ng62uGMGXyQSeuxePG5gJAMtip4Rnspu5Tu7hgvaXns=

16
main.go
View File

@ -2,25 +2,23 @@ package main
import ( import (
"context" "context"
"flag"
"math/rand" "math/rand"
"net/http" "net/http"
"time" "time"
common "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/maingate/core" "repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/maingate/core"
) )
// linux : go build --ldflags="-X 'main.revision=$(git rev-parse --short HEAD)'" // linux : go build --ldflags="-X 'main.revision=$(git rev-parse --short HEAD)'"
// windows : for /f usebackq %F in (`git rev-parse --short HEAD`) do go build --ldflags="-X 'main.revision=%F'" // windows : for /f usebackq %F in (`git rev-parse --short HEAD`) do go build --ldflags="-X 'main.revision=%F'"
var revision = "0000000" var revision = "0000000"
var prefix = flagx.String("prefix", "", "")
func main() { func main() {
if !flag.Parsed() { flagx.Parse()
flag.Parse()
}
logger.Println("build revision =", revision) logger.Println("build revision =", revision)
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
@ -33,12 +31,12 @@ func main() {
} }
serveMux := http.NewServeMux() serveMux := http.NewServeMux()
if err := mg.RegisterHandlers(ctx, serveMux, *common.PrefixPtr); err != nil { if err := mg.RegisterHandlers(ctx, serveMux, *prefix); err != nil {
logger.Error("RegisterHandlers failed :", err) logger.Error("RegisterHandlers failed :", err)
panic(err) panic(err)
} }
server := common.NewHTTPServer(serveMux) server := gocommon.NewHTTPServer(serveMux)
logger.Println("maingate is started") logger.Println("maingate is started")
if err := server.Start(); err != nil { if err := server.Start(); err != nil {
logger.Error("maingate is stopped with error :", err) logger.Error("maingate is stopped with error :", err)

View File

@ -1,27 +1,14 @@
# $ErrorActionPreference = 'SilentlyContinue' # $ErrorActionPreference = 'SilentlyContinue'
del maingate.zip $CurBranch = git branch --show-current
cd .. Remove-Item maingate.zip -Force -Recurse -ErrorAction SilentlyContinue
cd maingate-console
Copy-Item .env.example.prod .env
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:GOOS="linux"
$Env:GOARCH="amd64" $Env:GOARCH="amd64"
go build -ldflags="-s -w" . go build -ldflags="-s -w" .
Compress-Archive -Path config.json -Update -DestinationPath maingate.zip
Compress-Archive -Path maingate -Update -DestinationPath maingate.zip Compress-Archive -Path maingate -Update -DestinationPath maingate.zip
Compress-Archive -Path www -Update -DestinationPath maingate.zip
Compress-Archive -Path *-firebase-*.json -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

File diff suppressed because one or more lines are too long

View File

@ -1,223 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>GamePot JS SDK Sandbox</title>
<meta charset="utf-8" />
<!-- <meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/> -->
<!-- <link
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/> -->
<!-- <script src="https://gamepot.gcdn.ntruss.com/gamepot-sdk-javascript-lastest.min.js"></script> -->
<script src="https://cdn.gamepot.io/dev/gamepot-sdk-javascript-1.0.19-b1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body class="container-fluid" style="margin-bottom: 200px;">
<script type="text/javascript">
function onLoginsuccess(user) {
// -- 로그인 성공
// id: 회원 아이디
// token: 로그인 토큰(JWT)
// nickname: 닉네임
// provider: 소셜 로그인 종류
// providerId: 소셜 로그인 ID
// verify: 인증여부
// agree: 약관 동의 여부
//===========================
const formauth = document.createElement('form');
let objs1 = document.createElement('input');
objs1.setAttribute('type', 'hidden');
objs1.setAttribute('name', 'state');
objs1.setAttribute('value', '{{ .State }}');
formauth.appendChild(objs1);
let objs2 = document.createElement('input');
objs2.setAttribute('type', 'hidden');
objs2.setAttribute('name', 'id');
objs2.setAttribute('value', `${user.id}`);
formauth.appendChild(objs2);
let objs3 = document.createElement('input');
objs3.setAttribute('type', 'hidden');
objs3.setAttribute('name', 'code');
objs3.setAttribute('value', `${user.token}`);
formauth.appendChild(objs3);
let objs4 = document.createElement('input');
objs4.setAttribute('type', 'hidden');
objs4.setAttribute('name', 'nickname');
objs4.setAttribute('value', `${user.nickname}`);
formauth.appendChild(objs4);
let objs5 = document.createElement('input');
objs5.setAttribute('type', 'hidden');
objs5.setAttribute('name', 'provider');
objs5.setAttribute('value', `${user.provider}`);
formauth.appendChild(objs5);
let objs6 = document.createElement('input');
objs6.setAttribute('type', 'hidden');
objs6.setAttribute('name', 'providerId');
objs6.setAttribute('value', `${user.providerId}`);
formauth.appendChild(objs6);
let objs7 = document.createElement('input');
objs7.setAttribute('type', 'hidden');
objs7.setAttribute('name', 'verify');
objs7.setAttribute('value', `${user.verify}`);
formauth.appendChild(objs7);
let objs8 = document.createElement('input');
objs8.setAttribute('type', 'hidden');
objs8.setAttribute('name', 'agree');
objs8.setAttribute('value', `${user.agree}`);
formauth.appendChild(objs8);
formauth.setAttribute('method', 'post');
formauth.setAttribute('action', '/authorize/gamepot');
document.body.appendChild(formauth);
formauth.submit();
}
function onLoginFail(error) {
const formauth = document.createElement('form');
formauth.setAttribute('method', 'post');
formauth.setAttribute('action', 'actionsquare://error?errormsg='+ encodeURIComponent(`${error.code}-${error.message}`));
document.body.appendChild(formauth);
formauth.submit();
}
function onSignInGoogle(error, user) {
if (error) {
// 로그인 실패
onLoginFail(error);
} else {
onLoginsuccess(user);
}
}
function SignIn(channeltype) {
GP.login(channeltype, function (user, error) {
if (error) {
// 로그아웃 실패
onLoginFail(error);
} else {
// 로그인 성공
onLoginsuccess(user)
}
});
}
function Logout() {
// GP.logout(function (error) {});
GP.logout(function (result, error) {
if (error) {
// 로그아웃 실패
alert(error); // 오류 메세지
} else {
// 로그아웃 아웃 완료
alert("로그아웃 아웃 완료");
}
});
}
function deleteMember() {
if (!userId) {
alert("로그인을 먼저 해 주세요.");
return;
}
GP.deleteMember(userId, function (result, error) {
if (error) {
// 회원탈퇴 실패
alert(error); // 오류 메세지
} else {
// 회원탈퇴 아웃 완료
alert("회원탈퇴 성공!");
}
});
}
window.onload = function () {
// 프로젝트 ID는 게임팟 대시보드에서 확인할 수 있습니다.
var project_id = "dbfe1334-6dde-43e0-b8a9-cc0733d4c60e";
var gamepotConfig = {
api_url: "https://gpapps.gamepot.ntruss.com",
google_signin_client_id:
"46698421246-aeg0c2pmsgifr3fi06jgnqag5u8ph3kn.apps.googleusercontent.com",
google: {
callback: onSignInGoogle, // callback 버튼
renderButton: "googleRenderButton", // 버튼 DIV 이름
option: {
// google button option
size: "large",
theme: "outline",
width: "375",
text: "signup_with",
shape: "rectangular",
logo_alignment: "left",
locale: "ko_kr"
}
},
facebook_app_id: "2930531180541185",
apple_client_id: "auth.service.action2quare.com",
apple_redirect_uri: "{{.RedirectBaseUrl}}/authorize/apple",
api_key: "b94615af2a956facd2add44ea50529154b35f520de85673d",
};
GP.initialize(project_id, gamepotConfig);
};
</script>
<div align="center">
<br />
<div id="googleRenderButton"></div>
<br />
<div>
<table class="table" border="0" width="400" bgcolor="white" style="table-layout: fixed" onclick="SignIn(GP.ChannelType.APPLE)">
<tr>
<td align="center">
<div
id="appleid-signin"
data-mode="center-align"
data-type="sign-in"
data-color="white"
data-border="true"
data-border-radius="15"
data-width="375"
data-height="40"
data-logo-size="medium"
data-logo-position="47"
data-label-position="135"
style="pointer-events: none"
></div>
</td>
</tr>
</table>
</div>
<br />
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long