diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go index ec94923..c8c0f13 100644 --- a/server/cmd/server/main.go +++ b/server/cmd/server/main.go @@ -38,6 +38,7 @@ import ( "github.com/wangjia/pangolin/server/internal/provision" "github.com/wangjia/pangolin/server/internal/provision/providers" "github.com/wangjia/pangolin/server/internal/redisutil" + "github.com/wangjia/pangolin/server/internal/reward" "github.com/wangjia/pangolin/server/internal/scheduler" "github.com/wangjia/pangolin/server/internal/scheduler/probe" "github.com/wangjia/pangolin/server/internal/sessions" @@ -279,6 +280,7 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv // ── Auth ────────────────────────────────────────────────────────────────── var authHandler *auth.Handler + var authSvc *auth.Service if tm != nil { var mailer auth.Mailer if smtpHost := os.Getenv("SMTP_HOST"); smtpHost != "" { @@ -294,7 +296,7 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv } rl := auth.NewRateLimiter(rdb, nil) authStore := auth.NewSQLStore(sqlDB) - authSvc := auth.NewService(authStore, rdb, rl, tm, mailer, auth.ServiceConfig{}, nil) + authSvc = auth.NewService(authStore, rdb, rl, tm, mailer, auth.ServiceConfig{}, nil) authSvc.SetDeviceRegistrar(authDeviceRegistrar{svc: devicesSvc}) authSvc.SetSessionStore(sessionStore) authHandler = auth.NewHandler(authSvc) @@ -319,6 +321,31 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv webhookHandler := codes.NewWebhookHandler(codesStore, rdb, os.Getenv("WEBHOOK_SECRET"), 5*time.Minute, 15*time.Minute) + // ── Reward(邀请奖励 + 奖励任务)────────────────────────────────────────── + regCap := 10 + if v := os.Getenv("INVITE_REG_MONTHLY_CAP"); v != "" { + if n, err := strconv.Atoi(v); err == nil && n > 0 { + regCap = n + } + } + rewardStore := reward.NewStore(sqlDB) + rewardSvc := reward.NewService(sqlDB, rewardStore, codesSvc, + reward.Config{RegDays: 3, PaidDays: 7, TgDays: 3, RegMonthlyCap: regCap}, nil) + rewardSvc.SetRedis(rdb) + tgBotToken := os.Getenv("TG_REWARD_BOT_TOKEN") + tgEnabled := tgBotToken != "" + if tgEnabled { + rewardSvc.SetTelegram( + getenvDefault("TG_REWARD_BOT_USER", "pangolin_reward_bot"), + getenvDefault("TG_REWARD_CHANNEL", "@pangolin_app"), + tgBotToken, os.Getenv("TG_WEBHOOK_SECRET")) + } + rewardHandler := reward.NewHandler(rewardSvc, rewardStore, tgEnabled, + getenvDefault("TG_REWARD_CHANNEL", "@pangolin_app")) + if authSvc != nil { + authSvc.SetReferralHook(rewardSvc) + } + // ── Pay(pay v2 统一支付网关;PAY_BASE_URL 未配则整组不挂载)────────────── var payHandler *pay.Handler var payWebhook *pay.WebhookHandler @@ -333,6 +360,7 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv payHandler = pay.NewHandler(payClient, payStore, sqlDB) payWebhook = pay.NewWebhookHandler(payStore, codesSvc, sqlDB, rdb, paySystem, paySecret, 5*time.Minute, 15*time.Minute) + payWebhook.SetRewarder(rewardSvc) } else { log.Printf("PAY_BASE_URL 未配置 — /v1/pay 支付端点不挂载") } @@ -421,6 +449,7 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv if payWebhook != nil { v1.Post("/webhook/pay", payWebhook.ServeHTTP) } + v1.Post("/tg/webhook", rewardHandler.TelegramWebhook) // Protected: all routes that require a valid Bearer JWT. if tm != nil { @@ -458,6 +487,8 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv protected.Post("/pay/orders/{orderNo}/retry", payHandler.Retry) protected.Post("/pay/orders/{orderNo}/cancel", payHandler.Cancel) } + protected.Get("/invite", rewardHandler.GetInvite) + protected.Get("/tasks/telegram/start", rewardHandler.TelegramStart) if nodeAPI != nil { protected.Get("/nodes", nodeAPI.ListNodes)