tavern 분리

This commit is contained in:
2023-05-24 16:10:00 +09:00
commit 1517f59fc3
13 changed files with 4225 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.vscode/
__debug_bin.exe
*.log

66
config_template.json Normal file
View File

@ -0,0 +1,66 @@
{
"region_storage" : {
"default" : {
"mongo" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false",
"redis" : {
"url" : "redis://192.168.8.94:6379",
"offset" : {
"cache" : 0,
"session" : 1,
"ranking" : 2
}
}
},
"dev" : {
"mongo" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false",
"redis" : {
"url" : "redis://192.168.8.94:6379",
"offset" : {
"cache" : 0,
"session" : 1,
"ranking" : 2
}
}
}
},
"maingate_mongodb_url" : "mongodb://192.168.8.94:27017/?replicaSet=repl01&retrywrites=false",
"maingate_service_url" : "http://localhost/maingate",
"maingate_api_token" : "63d08aa34f0162622c11284b",
"tavern_service_url" : "http://localhost/tavern",
"tavern_group_types" : {
"subjugate" : {
"text_search_field" : ["name"],
"unique_index" : ["name,_id", "_id,members", "name,hidden"],
"search_index" : ["rules"],
"member_index" : ["_gid,candidate,luts","_gid,luts","_gid,expiring"],
"invite_ttl" : 30,
"candidate_ttl" : 3600,
"invitee_exlusive" : true,
"invitee_is_member" : true,
"max_member" : 4
},
"lobby" : {
"max_member" : 3,
"invitee_exlusive" : true,
"invitee_is_member" : true,
"transient" : true,
"invite_ttl" : 30
}
},
"ws_sync_pipeline" : "redis://192.168.8.94:6379/3",
"services" : {
"kingdom" : {
"개발중" : {
"url" :"http://localhost/warehouse/dev",
"development" : true
},
"개인서버" : {
"url" : "http://localhost/warehouse/private",
"development" : false
}
}
}
}

1067
core/apiimpl.go Normal file

File diff suppressed because it is too large Load Diff

49
core/group.go Normal file
View File

@ -0,0 +1,49 @@
package core
import (
"net/url"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type groupConfig struct {
UniqueIndex []string `json:"unique_index"`
SearchIndex []string `json:"search_index"`
MemberIndex []string `json:"member_index"`
TextSearchFields []string `json:"text_search_field"`
InviteExpire int32 `json:"invite_ttl"` // 그룹이 개인에게 보낸 초대장 만료 기한
CandidateExpire int32 `json:"candidate_ttl"` // 개인이 그룹에게 보낸 신청서 만료 기한
InviteeExlusive bool `json:"invitee_exlusive"`
InviteeIsMember bool `json:"invitee_is_member"`
MaxMember int `json:"max_member"`
Transient bool `json:"transient"`
Name string
}
type group interface {
Create(form url.Values, doc bson.M) (primitive.ObjectID, error)
Candidate(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error
Join(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID, doc bson.M) (newTicketID primitive.ObjectID, err error)
FindTicketID(groupID primitive.ObjectID, memberID primitive.ObjectID) primitive.ObjectID
Invite(groupID primitive.ObjectID, memberID primitive.ObjectID, inviterDoc bson.M, inviteeDoc bson.M) (string, error)
UpdateGroupMember(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID, doc bson.M) error
CancelInvitation(groupID primitive.ObjectID, ticketID primitive.ObjectID) error
AcceptInvitation(groupID primitive.ObjectID, mid primitive.ObjectID, ticketID primitive.ObjectID, member bson.M) (primitive.ObjectID, error)
DenyInvitation(groupID primitive.ObjectID, mid primitive.ObjectID, ticketID primitive.ObjectID) error
QueryInvitations(memberID primitive.ObjectID, after primitive.Timestamp) ([]bson.M, error)
Exist(groupID primitive.ObjectID, filter bson.M) (bool, error)
FindAll(filter bson.M, projection string, after primitive.Timestamp) ([]bson.M, error)
FindOne(groupID primitive.ObjectID, projection string) (bson.M, error)
QueryMembers(groupID primitive.ObjectID, requesterID primitive.ObjectID, projection string, after primitive.Timestamp) (map[string]bson.M, error)
QueryMember(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID, projection string) (bson.M, error)
Leave(groupID primitive.ObjectID, memberID primitive.ObjectID, ticketID primitive.ObjectID) error
DropPausedMember(groupID primitive.ObjectID, memberID primitive.ObjectID) error
PauseMember(groupID primitive.ObjectID, memberID primitive.ObjectID, conn *wshandler.Richconn) error
UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error
Dismiss(groupID primitive.ObjectID) error
UpdateGroupDocument(groupID primitive.ObjectID, body []byte) error
}

1221
core/group_memory.go Normal file

File diff suppressed because it is too large Load Diff

1050
core/group_mongo.go Normal file

File diff suppressed because it is too large Load Diff

73
core/richconn.go Normal file
View File

@ -0,0 +1,73 @@
package core
import (
"context"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"github.com/go-redis/redis/v8"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type richConnOuter struct {
wsh *wshandler.WebsocketHandler
rc *wshandler.Richconn
}
func (sub richConnOuter) JoinTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, hint string) error {
sub.wsh.JoinTag(region, tag, tid, sub.rc, hint)
wsh := sub.wsh
sub.rc.RegistOnCloseFunc(tag.Hex(), func() {
wsh.LeaveTag(region, tag, tid)
})
return nil
}
func (sub richConnOuter) LeaveTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, hint string) error {
sub.SetStateInTag(region, tag, tid, "", hint)
return sub.wsh.LeaveTag(region, tag, tid)
}
func (sub richConnOuter) SetStateInTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, state string, hint string) error {
return sub.wsh.SetStateInTag(region, tag, tid, state, hint)
}
func (sub richConnOuter) TurnGroupOnline(key string, gid primitive.ObjectID, score float64) error {
gidhex := gid.Hex()
_, err := sub.wsh.RedisSync.ZAdd(context.Background(), key, &redis.Z{Score: score, Member: gidhex}).Result()
if err != nil {
logger.Error("TurnGroupOnline failed. redis.ZAdd return err :", err)
return err
}
sub.rc.RegistOnCloseFunc(key, func() {
sub.wsh.RedisSync.ZRem(context.Background(), key, gidhex)
})
return nil
}
func (sub richConnOuter) TurnGroupOffline(key string, gid primitive.ObjectID) error {
f := sub.rc.UnregistOnCloseFunc(key)
if f != nil {
f()
} else {
sub.wsh.RedisSync.ZRem(context.Background(), key, gid.Hex())
}
return nil
}
func (sub richConnOuter) SendMessage(doc []byte) error {
return sub.rc.WriteBytes(doc)
}
func (sub richConnOuter) SendMessageToTag(region string, tag primitive.ObjectID, msg []byte) error {
sub.wsh.BroadcastRaw(region, tag, msg)
return nil
}
func (sub richConnOuter) CloseOnPurpose() error {
return sub.rc.Close()
}

195
core/rpc/connrpc.go Normal file
View File

@ -0,0 +1,195 @@
package rpc
import (
"bytes"
"encoding/gob"
"fmt"
"reflect"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var Everybody = primitive.ObjectID([12]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
func init() {
gob.Register(bson.M{})
gob.Register(primitive.ObjectID{})
gob.Register(primitive.Timestamp{})
}
type RpcCaller struct {
publish func(bt []byte) error
}
func NewRpcCaller(f func(bt []byte) error) RpcCaller {
return RpcCaller{
publish: f,
}
}
type rpcCallContext struct {
alias primitive.ObjectID
publish func(bt []byte) error
}
func (c *RpcCaller) One(alias primitive.ObjectID) rpcCallContext {
return rpcCallContext{
alias: alias,
publish: c.publish,
}
}
func (c *RpcCaller) Everybody() rpcCallContext {
return rpcCallContext{
alias: Everybody,
publish: c.publish,
}
}
func IsCallerCalleeMethodMatch[Callee any]() error {
var caller rpcCallContext
var callee Callee
callerType := reflect.TypeOf(caller)
calleeType := reflect.TypeOf(callee)
for i := 0; i < callerType.NumMethod(); i++ {
callerMethod := callerType.Method(i)
calleeMethod, ok := calleeType.MethodByName(callerMethod.Name)
if !ok {
return fmt.Errorf("method '%s' of '%s' is missing", callerMethod.Name, calleeType.Name())
}
if calleeMethod.Func.Type().NumIn() != callerMethod.Func.Type().NumIn() {
return fmt.Errorf("method '%s' argument num is not match", callerMethod.Name)
}
if calleeMethod.Func.Type().NumOut() != callerMethod.Func.Type().NumOut() {
return fmt.Errorf("method '%s' out num is not match", callerMethod.Name)
}
for i := 1; i < calleeMethod.Func.Type().NumIn(); i++ {
if calleeMethod.Func.Type().In(i) != callerMethod.Func.Type().In(i) {
return fmt.Errorf("method '%s' argument is not match. %s-%s", callerMethod.Name, calleeMethod.Func.Type().In(i).Name(), callerMethod.Func.Type().In(i).Name())
}
}
}
return nil
}
type fnsig struct {
FunctionName string `bson:"fn"`
Args []any `bson:"args"`
}
func Encode[T any](prefix T, fn string, args ...any) ([]byte, error) {
m := append([]any{
prefix,
fn,
}, args...)
buff := new(bytes.Buffer)
encoder := gob.NewEncoder(buff)
err := encoder.Encode(m)
if err != nil {
logger.Error("rpcCallContext.send err :", err)
return nil, err
}
return buff.Bytes(), nil
}
func Decode[T any](src []byte) (*T, string, []any, error) {
var m []any
decoder := gob.NewDecoder(bytes.NewReader(src))
if err := decoder.Decode(&m); err != nil {
logger.Error("RpcCallee.Call err :", err)
return nil, "", nil, err
}
prfix := m[0].(T)
fn := m[1].(string)
return &prfix, fn, m[2:], nil
}
func decode(src []byte) (string, []any, error) {
var sig fnsig
decoder := gob.NewDecoder(bytes.NewReader(src))
if err := decoder.Decode(&sig); err != nil {
logger.Error("RpcCallee.Call err :", err)
return "", nil, err
}
return sig.FunctionName, sig.Args, nil
}
func (c *rpcCallContext) send(fn string, args ...any) error {
bt, err := Encode(c.alias, fn, args...)
if err != nil {
return err
}
return c.publish(bt)
}
type RpcCallee[T any] struct {
methods map[string]reflect.Method
create func(*wshandler.Richconn) *T
}
func NewRpcCallee[T any](createReceiverFunc func(*wshandler.Richconn) *T) RpcCallee[T] {
out := RpcCallee[T]{
methods: make(map[string]reflect.Method),
create: createReceiverFunc,
}
var tmp *T
tp := reflect.TypeOf(tmp)
for i := 0; i < tp.NumMethod(); i++ {
method := tp.Method(i)
out.methods[method.Name] = method
}
return out
}
func (r RpcCallee[T]) Call(rc *wshandler.Richconn, src []byte) error {
defer func() {
s := recover()
if s != nil {
logger.Error(s)
}
}()
fn, params, err := decode(src)
if err != nil {
logger.Error("RpcCallee.Call err :", err)
return err
}
method, ok := r.methods[fn]
if !ok {
err := fmt.Errorf("method '%s' is missing", fn)
logger.Error("RpcCallee.Call err :", err)
return err
}
receiver := r.create(rc)
args := []reflect.Value{
reflect.ValueOf(receiver),
}
for _, arg := range params {
args = append(args, reflect.ValueOf(arg))
}
rets := method.Func.Call(args)
if len(rets) > 0 && rets[len(rets)-1].Interface() != nil {
return rets[len(rets)-1].Interface().(error)
}
return nil
}

33
core/rpc/proxy.go Normal file
View File

@ -0,0 +1,33 @@
package rpc
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
func (c rpcCallContext) JoinTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, hint string) error {
return c.send("JoinTag", region, tag, tid, hint)
}
func (c rpcCallContext) LeaveTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, hint string) error {
return c.send("LeaveTag", region, tag, tid, hint)
}
func (c rpcCallContext) TurnGroupOnline(key string, gid primitive.ObjectID, score float64) error {
return c.send("TurnGroupOnline", key, gid, score)
}
func (c rpcCallContext) TurnGroupOffline(key string, gid primitive.ObjectID) error {
return c.send("TurnGroupOffline", key, gid)
}
func (c rpcCallContext) SetStateInTag(region string, tag primitive.ObjectID, tid primitive.ObjectID, state string, hint string) error {
return c.send("SetStateInTag", region, tag, tid, state, hint)
}
func (c rpcCallContext) SendMessage(doc []byte) error {
return c.send("SendMessage", doc)
}
func (c rpcCallContext) SendMessageToTag(region string, gid primitive.ObjectID, msg []byte) error {
return c.send("SendMessageToTag", region, gid, msg)
}
func (c rpcCallContext) CloseOnPurpose() error {
return c.send("CloseOnPurpose")
}

317
core/tavern.go Normal file
View File

@ -0,0 +1,317 @@
package core
import (
"context"
"errors"
"flag"
"io"
"net"
"net/http"
"reflect"
"strings"
common "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"repositories.action2quare.com/ayo/tavern/core/rpc"
"github.com/go-redis/redis/v8"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const (
defaultMaxMemory = 32 << 10 // 32 KB
)
func writeBsonArr(w io.Writer, src []bson.M) error {
return writeBsonDoc(w, bson.M{
"r": src,
})
}
func onlineGroupQueryKey(prefix string) string {
return prefix + "_olg"
}
func writeBsonDoc[T any](w io.Writer, src T) error {
rw, err := bsonrw.NewBSONValueWriter(w)
if err != nil {
return err
}
enc, err := bson.NewEncoder(rw)
if err != nil {
return err
}
return enc.Encode(src)
}
func readBsonDoc(r io.Reader, src any) error {
body, err := io.ReadAll(r)
if err != nil {
return err
}
if len(body) == 0 {
return nil
}
decoder, err := bson.NewDecoder(bsonrw.NewBSONDocumentReader(body))
if err != nil {
return err
}
err = decoder.Decode(src)
if err != nil {
return err
}
return nil
}
type rpcCallDomain[T any] struct {
rpcCallChanName string
caller rpc.RpcCaller
callee rpc.RpcCallee[T]
methods map[string]reflect.Method
}
func createRpcCallDomain[CalleeType any](syncConn *redis.Client, creator func(*wshandler.Richconn) *CalleeType) rpcCallDomain[CalleeType] {
var tmp *CalleeType
methods := make(map[string]reflect.Method)
tp := reflect.TypeOf(tmp)
for i := 0; i < tp.NumMethod(); i++ {
method := tp.Method(i)
methods[method.Name] = method
}
rpcChanName := "conn_rpc_channel_" + tp.Name()
publishFunc := func(bt []byte) error {
_, err := syncConn.Publish(context.Background(), rpcChanName, bt).Result()
return err
}
return rpcCallDomain[CalleeType]{
rpcCallChanName: rpcChanName,
caller: rpc.NewRpcCaller(publishFunc),
callee: rpc.NewRpcCallee(creator),
methods: methods,
}
}
type TavernConfig struct {
common.RegionStorageConfig `json:",inline"`
GroupTypes map[string]*groupConfig `json:"tavern_group_types"`
MaingateApiToken string `json:"maingate_api_token"`
macAddr string
}
var config TavernConfig
type Tavern struct {
subTaverns []*subTavern
wsh *wshandler.WebsocketHandler
}
type subTavern struct {
mongoClient common.MongoClient
wsh *wshandler.WebsocketHandler
region string
groups map[string]group
methods map[string]reflect.Method
wshRpc rpcCallDomain[richConnOuter]
}
func getMacAddr() (string, error) {
ifas, err := net.Interfaces()
if err != nil {
return "", err
}
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
a = strings.ReplaceAll(a, ":", "")
return a, nil
}
}
return "", errors.New("no net interface")
}
// New :
func New(context context.Context, wsh *wshandler.WebsocketHandler, inconfig *TavernConfig) (*Tavern, error) {
if !flag.Parsed() {
flag.Parse()
}
if inconfig == nil {
var loaded TavernConfig
if err := common.LoadConfig(&loaded); err != nil {
return nil, err
}
inconfig = &loaded
}
config = *inconfig
macaddr, err := getMacAddr()
if err != nil {
return nil, err
}
config.macAddr = macaddr
tv := Tavern{
wsh: wsh,
}
if err = tv.prepare(context); err != nil {
logger.Println("tavern prepare() failed :", err)
return nil, err
}
return &tv, nil
}
func (tv *Tavern) Destructor() {
tv.wsh.Destructor()
for _, st := range tv.subTaverns {
st.mongoClient.Close()
}
}
type groupPipelineDocument struct {
OperationType string `bson:"operationType"`
FullDocument map[string]any `bson:"fullDocument"`
DocumentKey struct {
Id primitive.ObjectID `bson:"_id"`
} `bson:"documentKey"`
UpdateDescription struct {
UpdatedFields bson.M `bson:"updatedFields"`
RemovedFileds bson.A `bson:"removedFields"`
TruncatedArrays bson.A `bson:"truncatedArrays"`
} `bson:"updateDescription"`
}
func (tv *Tavern) prepare(ctx context.Context) error {
for region, url := range config.RegionStorage {
var dbconn common.MongoClient
var err error
var groupinstance group
if err := rpc.IsCallerCalleeMethodMatch[richConnOuter](); err != nil {
return err
}
var tmp *subTavern
methods := make(map[string]reflect.Method)
tp := reflect.TypeOf(tmp)
for i := 0; i < tp.NumMethod(); i++ {
method := tp.Method(i)
methods[method.Name] = method
}
sub := &subTavern{
wsh: tv.wsh,
mongoClient: dbconn,
region: region,
methods: methods,
}
sub.wshRpc = createRpcCallDomain(tv.wsh.RedisSync, func(rc *wshandler.Richconn) *richConnOuter {
return &richConnOuter{wsh: sub.wsh, rc: rc}
})
groups := make(map[string]group)
for typename, cfg := range config.GroupTypes {
cfg.Name = typename
if cfg.Transient {
groupinstance, err = cfg.prepareInMemory(ctx, region, typename, tv.wsh)
} else {
if !dbconn.Connected() {
dbconn, err = common.NewMongoClient(ctx, url.Mongo, region)
if err != nil {
return err
}
}
groupinstance, err = cfg.preparePersistent(ctx, region, dbconn, tv.wsh)
}
if err != nil {
return err
}
groups[typename] = groupinstance
}
sub.groups = groups
tv.subTaverns = append(tv.subTaverns, sub)
}
return nil
}
func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
// request는 항상 서비스 서버를 거쳐서 들어온다. [client] <--tls--> [service server] <--http--> tavern
// 클라이언트는 tavern으로부터 메시지를 수신할 뿐, 송신하지 못한다.
// 단, 요청은 https 서비스 서버를 통해 들어오고 클라이언트는 ws으로 수신만 한다는 원칙이 유지되어야 한다.(채팅 메시지는 예외?)
for _, sub := range tv.subTaverns {
var pattern string
if sub.region == "default" {
pattern = common.MakeHttpHandlerPattern(prefix, "api")
} else {
pattern = common.MakeHttpHandlerPattern(prefix, sub.region, "api")
}
serveMux.HandleFunc(pattern, sub.api)
deliveryChan := tv.wsh.DeliveryChannel(sub.region)
go sub.deliveryMessageHandler(deliveryChan)
}
return nil
}
func (sub *subTavern) api(w http.ResponseWriter, r *http.Request) {
defer func() {
s := recover()
if s != nil {
logger.Error(s)
}
io.Copy(io.Discard, r.Body)
r.Body.Close()
}()
// 서버에서 오는 요청만 처리
apitoken := r.Header.Get("MG-X-API-TOKEN")
if apitoken != config.MaingateApiToken {
// 서버가 보내는 쿼리만 허용
logger.Println("MG-X-API-TOKEN is missing")
w.WriteHeader(http.StatusBadRequest)
return
}
operation := r.URL.Query().Get("operation")
if len(operation) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
method, ok := sub.methods[operation]
if !ok {
// 없는 operation
logger.Println("fail to call api. operation is not valid :", operation)
w.WriteHeader(http.StatusBadRequest)
return
}
if r.PostForm == nil {
r.ParseMultipartForm(defaultMaxMemory)
}
args := []reflect.Value{
reflect.ValueOf(sub),
reflect.ValueOf(w),
reflect.ValueOf(r),
}
method.Func.Call(args)
}

27
go.mod Normal file
View File

@ -0,0 +1,27 @@
module repositories.action2quare.com/ayo/tavern
go 1.19
require (
github.com/go-redis/redis/v8 v8.11.5
github.com/gorilla/websocket v1.5.0
go.mongodb.org/mongo-driver v1.11.6
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524065958-abddf26379d1
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.9.0 // indirect
)

78
go.sum Normal file
View File

@ -0,0 +1,78 @@
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
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/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/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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
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/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/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
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/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o=
go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
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.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524065958-abddf26379d1 h1:0OInnPqpzQhRf1zwVTbWqWByWs8MTYbZc8c95099bSM=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230524065958-abddf26379d1/go.mod h1:5RmALPCFGFmqXa+AAPLsQaSlBVBafwX1H2CnIhsCM50=

46
main.go Normal file
View File

@ -0,0 +1,46 @@
// warroom project main.go
package main
import (
"context"
"flag"
"net/http"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"repositories.action2quare.com/ayo/tavern/core"
common "repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/logger"
)
func main() {
if !flag.Parsed() {
flag.Parse()
}
ctx, cancel := context.WithCancel(context.Background())
var config core.TavernConfig
if err := common.LoadConfig(&config); err != nil {
panic(err)
}
authcache, err := common.NewAuthCollectionGlobal(ctx, config.MaingateApiToken)
if err != nil {
panic(err)
}
wsh := wshandler.NewWebsocketHandler(authcache)
if tv, err := core.New(ctx, wsh, &config); err != nil {
panic(err)
} else {
serveMux := http.NewServeMux()
wsh.RegisterHandlers(ctx, serveMux, *common.PrefixPtr)
tv.RegisterHandlers(ctx, serveMux, *common.PrefixPtr)
server := common.NewHTTPServer(serveMux)
logger.Println("tavern is started")
server.Start()
cancel()
tv.Destructor()
wsh.Destructor()
}
}