312 lines
12 KiB
Go
312 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"flag"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"hyapp-admin-server/internal/cache"
|
|
"hyapp-admin-server/internal/config"
|
|
"hyapp-admin-server/internal/integration/activityclient"
|
|
"hyapp-admin-server/internal/integration/gameclient"
|
|
"hyapp-admin-server/internal/integration/roomclient"
|
|
"hyapp-admin-server/internal/integration/userclient"
|
|
"hyapp-admin-server/internal/integration/walletclient"
|
|
jobrunner "hyapp-admin-server/internal/job"
|
|
"hyapp-admin-server/internal/migration"
|
|
achievementconfigmodule "hyapp-admin-server/internal/modules/achievementconfig"
|
|
adminusermodule "hyapp-admin-server/internal/modules/adminuser"
|
|
appconfigmodule "hyapp-admin-server/internal/modules/appconfig"
|
|
appregistrymodule "hyapp-admin-server/internal/modules/appregistry"
|
|
appusermodule "hyapp-admin-server/internal/modules/appuser"
|
|
auditmodule "hyapp-admin-server/internal/modules/audit"
|
|
authmodule "hyapp-admin-server/internal/modules/auth"
|
|
coinledgermodule "hyapp-admin-server/internal/modules/coinledger"
|
|
countryregionmodule "hyapp-admin-server/internal/modules/countryregion"
|
|
dailytaskmodule "hyapp-admin-server/internal/modules/dailytask"
|
|
dashboardmodule "hyapp-admin-server/internal/modules/dashboard"
|
|
firstrechargerewardmodule "hyapp-admin-server/internal/modules/firstrechargereward"
|
|
gamemanagementmodule "hyapp-admin-server/internal/modules/gamemanagement"
|
|
giftdiamondmodule "hyapp-admin-server/internal/modules/giftdiamond"
|
|
healthmodule "hyapp-admin-server/internal/modules/health"
|
|
hostagencypolicymodule "hyapp-admin-server/internal/modules/hostagencypolicy"
|
|
hostorgmodule "hyapp-admin-server/internal/modules/hostorg"
|
|
hostsalarysettlementmodule "hyapp-admin-server/internal/modules/hostsalarysettlement"
|
|
jobmodule "hyapp-admin-server/internal/modules/job"
|
|
levelconfigmodule "hyapp-admin-server/internal/modules/levelconfig"
|
|
luckygiftmodule "hyapp-admin-server/internal/modules/luckygift"
|
|
menumodule "hyapp-admin-server/internal/modules/menu"
|
|
paymentmodule "hyapp-admin-server/internal/modules/payment"
|
|
rbacmodule "hyapp-admin-server/internal/modules/rbac"
|
|
redpacketmodule "hyapp-admin-server/internal/modules/redpacket"
|
|
regionblockmodule "hyapp-admin-server/internal/modules/regionblock"
|
|
registrationrewardmodule "hyapp-admin-server/internal/modules/registrationreward"
|
|
reportmodule "hyapp-admin-server/internal/modules/report"
|
|
resourcemodule "hyapp-admin-server/internal/modules/resource"
|
|
roomadminmodule "hyapp-admin-server/internal/modules/roomadmin"
|
|
roomrocketmodule "hyapp-admin-server/internal/modules/roomrocket"
|
|
searchmodule "hyapp-admin-server/internal/modules/search"
|
|
sevendaycheckinmodule "hyapp-admin-server/internal/modules/sevendaycheckin"
|
|
teamsalarypolicymodule "hyapp-admin-server/internal/modules/teamsalarypolicy"
|
|
teamsalarysettlementmodule "hyapp-admin-server/internal/modules/teamsalarysettlement"
|
|
uploadmodule "hyapp-admin-server/internal/modules/upload"
|
|
userleaderboardmodule "hyapp-admin-server/internal/modules/userleaderboard"
|
|
vipconfigmodule "hyapp-admin-server/internal/modules/vipconfig"
|
|
"hyapp-admin-server/internal/platform/logging"
|
|
"hyapp-admin-server/internal/platform/tencentcos"
|
|
"hyapp-admin-server/internal/repository"
|
|
"hyapp-admin-server/internal/router"
|
|
"hyapp-admin-server/internal/service"
|
|
|
|
mysqlDriver "github.com/go-sql-driver/mysql"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func main() {
|
|
configPath := flag.String("config", "configs/config.yaml", "path to admin-server config")
|
|
runBootstrap := flag.Bool("bootstrap", false, "run configured bootstrap seed before starting")
|
|
flag.Parse()
|
|
|
|
cfg, err := config.Load(*configPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := logging.Init(cfg.ServiceName, cfg.Environment, cfg.NodeID, cfg.Log); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if err := ensureDatabase(cfg.MySQLDSN); err != nil {
|
|
fatalRuntime("ensure_mysql_database_failed", err)
|
|
}
|
|
|
|
dbLogger := logger.New(log.New(logging.StdWriter(), "", 0), logger.Config{
|
|
LogLevel: logger.Warn,
|
|
IgnoreRecordNotFoundError: true,
|
|
})
|
|
db, err := gorm.Open(mysql.Open(cfg.MySQLDSN), &gorm.Config{
|
|
Logger: dbLogger,
|
|
NowFunc: func() time.Time {
|
|
return time.Now().UTC()
|
|
},
|
|
})
|
|
if err != nil {
|
|
fatalRuntime("connect_mysql_failed", err)
|
|
}
|
|
sqlDB, err := db.DB()
|
|
if err != nil {
|
|
fatalRuntime("mysql_db_handle_failed", err)
|
|
}
|
|
if cfg.Migrations.Enabled {
|
|
if err := migration.ApplyWithOptions(context.Background(), sqlDB, cfg.Migrations.Dir, migration.Options{
|
|
AllowChecksumRepair: cfg.Migrations.AllowChecksumRepair,
|
|
}); err != nil {
|
|
fatalRuntime("apply_migrations_failed", err)
|
|
}
|
|
}
|
|
redisClient, err := cache.NewRedis(context.Background(), cfg.Redis)
|
|
if err != nil {
|
|
fatalRuntime("connect_redis_failed", err)
|
|
}
|
|
if redisClient != nil {
|
|
defer redisClient.Close()
|
|
}
|
|
|
|
userDB, err := sql.Open("mysql", cfg.UserMySQLDSN)
|
|
if err != nil {
|
|
fatalRuntime("connect_user_mysql_failed", err)
|
|
}
|
|
if err := userDB.PingContext(context.Background()); err != nil {
|
|
fatalRuntime("ping_user_mysql_failed", err)
|
|
}
|
|
defer userDB.Close()
|
|
|
|
walletDB, err := sql.Open("mysql", cfg.WalletMySQLDSN)
|
|
if err != nil {
|
|
fatalRuntime("connect_wallet_mysql_failed", err)
|
|
}
|
|
if err := walletDB.PingContext(context.Background()); err != nil {
|
|
fatalRuntime("ping_wallet_mysql_failed", err)
|
|
}
|
|
defer walletDB.Close()
|
|
|
|
store := repository.New(db)
|
|
if cfg.MySQLAutoMigrate {
|
|
if err := store.AutoMigrate(); err != nil {
|
|
fatalRuntime("auto_migrate_failed", err)
|
|
}
|
|
}
|
|
if *runBootstrap || cfg.Bootstrap.Enabled {
|
|
if err := store.Seed(cfg); err != nil {
|
|
fatalRuntime("seed_database_failed", err)
|
|
}
|
|
}
|
|
|
|
userConn, err := grpc.Dial(cfg.UserService.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
fatalRuntime("connect_user_service_failed", err)
|
|
}
|
|
defer userConn.Close()
|
|
|
|
walletConn, err := grpc.Dial(cfg.WalletService.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
fatalRuntime("connect_wallet_service_failed", err)
|
|
}
|
|
defer walletConn.Close()
|
|
|
|
roomConn, err := grpc.Dial(cfg.RoomService.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
fatalRuntime("connect_room_service_failed", err)
|
|
}
|
|
defer roomConn.Close()
|
|
roomClient := roomclient.NewGRPC(roomConn)
|
|
|
|
activityConn, err := grpc.Dial(cfg.ActivityService.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
fatalRuntime("connect_activity_service_failed", err)
|
|
}
|
|
defer activityConn.Close()
|
|
|
|
gameConn, err := grpc.Dial(cfg.GameService.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
fatalRuntime("connect_game_service_failed", err)
|
|
}
|
|
defer gameConn.Close()
|
|
|
|
auth := service.NewAuthService(cfg.JWTSecret, cfg.AccessTokenTTL)
|
|
var runner *jobrunner.Runner
|
|
var jobStatus healthmodule.JobStatusProvider
|
|
if cfg.Jobs.Enabled {
|
|
runner = jobrunner.NewRunner(store, redisClient, cfg)
|
|
runner.Start()
|
|
defer runner.Close()
|
|
jobStatus = runner
|
|
}
|
|
|
|
auditHandler := auditmodule.New(store)
|
|
teamSalarySettlementHandler := teamsalarysettlementmodule.New(sqlDB, walletDB, userDB, auditHandler)
|
|
if cfg.Jobs.Enabled {
|
|
teamSalaryRunner := teamsalarysettlementmodule.NewAutoRunner(teamSalarySettlementHandler.Service(), "lalu", cfg.NodeID)
|
|
teamSalaryRunner.Start()
|
|
defer teamSalaryRunner.Close()
|
|
}
|
|
var objectUploader uploadmodule.ObjectUploader
|
|
if cfg.TencentCOS.Enabled {
|
|
uploader, err := tencentcos.NewUploader(tencentcos.Config{
|
|
SecretID: cfg.TencentCOS.SecretID,
|
|
SecretKey: cfg.TencentCOS.SecretKey,
|
|
Bucket: cfg.TencentCOS.BucketName,
|
|
Region: cfg.TencentCOS.Region,
|
|
AccessURL: cfg.TencentCOS.AccessURL,
|
|
}, nil)
|
|
if err != nil {
|
|
fatalRuntime("create_tencent_cos_uploader_failed", err)
|
|
}
|
|
objectUploader = uploader
|
|
}
|
|
handlers := router.Handlers{
|
|
Audit: auditHandler,
|
|
Auth: authmodule.New(store, auth, auditHandler, cfg),
|
|
AdminUser: adminusermodule.New(store, cfg, auditHandler),
|
|
AchievementConfig: achievementconfigmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
|
AppConfig: appconfigmodule.New(store, auditHandler),
|
|
AppRegistry: appregistrymodule.New(userDB),
|
|
AppUser: appusermodule.New(userclient.NewGRPC(userConn), activityclient.NewGRPC(activityConn), sqlDB, userDB, walletDB, cfg, auditHandler),
|
|
CoinLedger: coinledgermodule.New(userDB, walletDB, sqlDB, walletclient.NewGRPC(walletConn), auditHandler),
|
|
CountryRegion: countryregionmodule.New(userclient.NewGRPC(userConn), userDB, cfg, auditHandler),
|
|
DailyTask: dailytaskmodule.New(activityclient.NewGRPC(activityConn), auditHandler),
|
|
Dashboard: dashboardmodule.New(store, cfg),
|
|
FirstRechargeReward: firstrechargerewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
|
Game: gamemanagementmodule.New(gameclient.NewGRPC(gameConn), auditHandler),
|
|
GiftDiamond: giftdiamondmodule.New(walletDB, auditHandler),
|
|
Health: healthmodule.New(store, redisClient, jobStatus),
|
|
HostAgencyPolicy: hostagencypolicymodule.New(store, walletDB, auditHandler),
|
|
HostOrg: hostorgmodule.New(userclient.NewGRPC(userConn), walletclient.NewGRPC(walletConn), userDB, walletDB, sqlDB, auditHandler),
|
|
HostSalarySettlement: hostsalarysettlementmodule.New(walletDB, userDB),
|
|
Job: jobmodule.New(store, cfg, auditHandler),
|
|
LevelConfig: levelconfigmodule.New(activityclient.NewGRPC(activityConn), walletclient.NewGRPC(walletConn), auditHandler),
|
|
LuckyGift: luckygiftmodule.New(activityclient.NewGRPC(activityConn), cfg.ActivityService.RequestTimeout, auditHandler),
|
|
Menu: menumodule.New(store, auditHandler),
|
|
Payment: paymentmodule.New(walletclient.NewGRPC(walletConn), userDB, auditHandler),
|
|
RBAC: rbacmodule.New(store, auditHandler),
|
|
RedPacket: redpacketmodule.New(walletclient.NewGRPC(walletConn), auditHandler),
|
|
Report: reportmodule.New(userDB, roomClient),
|
|
RegistrationReward: registrationrewardmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
|
RegionBlock: regionblockmodule.New(userDB, auditHandler),
|
|
Resource: resourcemodule.New(walletclient.NewGRPC(walletConn), store, userDB, auditHandler),
|
|
RoomAdmin: roomadminmodule.New(userDB, roomClient, auditHandler),
|
|
RoomRocket: roomrocketmodule.New(roomClient, auditHandler),
|
|
Search: searchmodule.New(store),
|
|
SevenDayCheckIn: sevendaycheckinmodule.New(activityclient.NewGRPC(activityConn), userDB, auditHandler),
|
|
TeamSalaryPolicy: teamsalarypolicymodule.New(store, auditHandler),
|
|
TeamSalarySettlement: teamSalarySettlementHandler,
|
|
Upload: uploadmodule.New(objectUploader, cfg.TencentCOS.ObjectPrefix, auditHandler),
|
|
UserLeaderboard: userleaderboardmodule.New(walletDB, userDB, roomClient),
|
|
VIPConfig: vipconfigmodule.New(walletclient.NewGRPC(walletConn), auditHandler),
|
|
}
|
|
engine := router.New(cfg, auth, handlers)
|
|
|
|
server := &http.Server{
|
|
Addr: cfg.HTTPAddr,
|
|
Handler: engine,
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
}
|
|
|
|
go func() {
|
|
slog.Info("service_listening", "addr", cfg.HTTPAddr)
|
|
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
fatalRuntime("listen_failed", err)
|
|
}
|
|
}()
|
|
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
<-quit
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
if err := server.Shutdown(ctx); err != nil {
|
|
slog.Error("shutdown_failed", "error", err.Error())
|
|
}
|
|
}
|
|
|
|
func fatalRuntime(msg string, err error) {
|
|
slog.Error(msg, "error", err.Error())
|
|
os.Exit(1)
|
|
}
|
|
|
|
func ensureDatabase(dsn string) error {
|
|
parsed, err := mysqlDriver.ParseDSN(dsn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if parsed.DBName == "" {
|
|
return nil
|
|
}
|
|
|
|
dbName := parsed.DBName
|
|
parsed.DBName = ""
|
|
|
|
db, err := sql.Open("mysql", parsed.FormatDSN())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
escaped := strings.ReplaceAll(dbName, "`", "``")
|
|
_, err = db.Exec("CREATE DATABASE IF NOT EXISTS `" + escaped + "` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
|
|
return err
|
|
}
|