Files
tavern/core/tavern.go
2023-11-24 16:30:47 +09:00

197 lines
5.3 KiB
Go

package core
import (
"context"
"errors"
"io"
"net"
"net/http"
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/gorilla/websocket"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/flagx"
"repositories.action2quare.com/ayo/gocommon/logger"
"repositories.action2quare.com/ayo/gocommon/metric"
"repositories.action2quare.com/ayo/gocommon/session"
"repositories.action2quare.com/ayo/gocommon/wshandler"
"go.mongodb.org/mongo-driver/bson"
)
var devflag = flagx.Bool("dev", false, "")
type TavernConfig struct {
session.SessionConfig `json:",inline"`
Group map[string]configDocument `json:"tavern_group_types"`
MaingateApiToken string `json:"maingate_api_token"`
RedisURL string `json:"tavern_redis_url"`
macAddr string
}
var config TavernConfig
type Tavern struct {
wsh *wshandler.WebsocketHandler
mongoClient gocommon.MongoClient
redison *gocommon.RedisonHandler
httpApiBorker gocommon.HttpApiBroker
}
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) (*Tavern, error) {
if err := gocommon.LoadConfig(&config); err != nil {
return nil, logger.ErrorWithCallStack(err)
}
macaddr, err := getMacAddr()
if err != nil {
return nil, logger.ErrorWithCallStack(err)
}
config.macAddr = macaddr
tv := &Tavern{
wsh: wsh,
}
if err = tv.prepare(context); err != nil {
logger.Println("tavern prepare() failed :", err)
return nil, logger.ErrorWithCallStack(err)
}
return tv, nil
}
func (tv *Tavern) Cleanup() {
metric.ConcurrentUser.Set(0)
tv.mongoClient.Close()
}
func (tv *Tavern) prepare(ctx context.Context) error {
redisClient, err := gocommon.NewRedisClient(config.RedisURL)
if err != nil {
return logger.ErrorWithCallStack(err)
}
tv.redison = gocommon.NewRedisonHandler(redisClient.Context(), redisClient)
tv.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(tv, "tv"))
if cfg, ok := config.Group["chat"]; ok {
chat := new(groupChat)
if err := chat.Initialize(tv, cfg); err != nil {
return logger.ErrorWithCallStack(err)
}
tv.httpApiBorker.AddHandler(gocommon.MakeHttpApiHandler(chat, "chat"))
tv.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(chat, "chat"))
}
if cfg, ok := config.Group["party"]; ok {
party := new(groupParty)
if err := party.Initialize(tv, cfg); err != nil {
return logger.ErrorWithCallStack(err)
}
tv.httpApiBorker.AddHandler(gocommon.MakeHttpApiHandler(party, "party"))
tv.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(party, "party"))
}
instant := new(groupInstant)
if err := instant.Initialize(tv); err != nil {
return logger.ErrorWithCallStack(err)
}
tv.httpApiBorker.AddHandler(gocommon.MakeHttpApiHandler(instant, "instant"))
tv.wsh.AddHandler(wshandler.MakeWebsocketApiHandler(instant, "instant"))
return nil
}
func (tv *Tavern) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
// tv.wsh.RegisterReceiver(tv)
pattern := gocommon.MakeHttpHandlerPattern(prefix, "api")
serveMux.HandleFunc(pattern, tv.api)
return nil
}
func (tv *Tavern) EnterChannel(ctx wshandler.ApiCallContext) {
tv.wsh.EnterRoom(ctx.Arguments[0].(string), ctx.CallBy.Accid)
}
func (tv *Tavern) LeaveChannel(ctx wshandler.ApiCallContext) {
tv.wsh.LeaveRoom(ctx.Arguments[0].(string), ctx.CallBy.Accid)
}
func (tv *Tavern) ClientConnected(conn *websocket.Conn, callby *wshandler.Sender) {
metric.ConcurrentUser.Add(1)
tv.redison.Del(tv.redison.Context(), callby.Accid.Hex())
_, err := tv.redison.JSONSet(callby.Accid.Hex(), "$", bson.M{"_ts": time.Now().UTC().Unix()})
if err != nil {
logger.Println("OnClientMessageReceived HSet error :", err)
}
}
func (tv *Tavern) ClientDisconnected(msg string, callby *wshandler.Sender) {
metric.ConcurrentUser.Add(-1)
tv.redison.Del(tv.redison.Context(), callby.Accid.Hex()).Result()
}
func (tv *Tavern) OnRoomCreated(name string) {
cnt, err := tv.redison.IncrBy(tv.redison.Context(), "_ref_"+name, 1).Result()
if err != nil && !errors.Is(err, redis.Nil) {
logger.Println("OnRoomCreated JSONSet failed :", err)
return
}
if cnt == 1 {
tv.redison.JSONSet(name, "$", map[string]any{}, gocommon.RedisonSetOptionNX)
}
}
func (tv *Tavern) OnRoomDestroyed(name string) {
cnt, err := tv.redison.IncrBy(tv.redison.Context(), "_ref_"+name, -1).Result()
if err != nil {
logger.Println("OnRoomDestroyed JSONNumIncrBy failed :", err)
} else if cnt == 0 {
tv.redison.Del(tv.redison.Context(), "_ref_"+name)
tv.redison.JSONDel(name, "$")
}
}
func (tv *Tavern) 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
}
tv.httpApiBorker.Call(w, r)
}