同步历史版本代码

This commit is contained in:
2026-03-03 09:32:22 -08:00
parent 7d46b31866
commit 4d8516b2e1
140 changed files with 8399 additions and 489 deletions
@@ -0,0 +1,17 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
func GetErrorLogMessageDetailHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
id := c.Query("id")
l := log.NewGetErrorLogMessageDetailLogic(c.Request.Context(), svcCtx)
resp, err := l.GetErrorLogMessageDetail(id)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,19 @@
package log
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/log"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func GetErrorLogMessageListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetErrorLogMessageListRequest
_ = c.ShouldBind(&req)
l := log.NewGetErrorLogMessageListLogic(c.Request.Context(), svcCtx)
resp, err := l.GetErrorLogMessageList(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,31 @@
package auth
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/auth"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func EmailLoginHandler(svcCtx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
var req types.EmailLoginRequest
if err := c.ShouldBind(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
req.IP = c.ClientIP()
req.UserAgent = c.Request.UserAgent()
if err := svcCtx.Validate(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
l := auth.NewEmailLoginLogic(c.Request.Context(), svcCtx)
resp, err := l.EmailLogin(&req)
result.HttpResult(c, resp, err)
}
}
+24
View File
@@ -0,0 +1,24 @@
package common
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func SubmitContactHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.ContactRequest
_ = c.ShouldBindJSON(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := common.NewContactLogic(c.Request.Context(), svcCtx)
err := l.SubmitContact(&req)
result.HttpResult(c, nil, err)
}
}
@@ -0,0 +1,32 @@
package common
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
)
// GetDownloadLinkHandler 获取下载链接
func GetDownloadLinkHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetDownloadLinkRequest
if err := c.ShouldBind(&req); err != nil {
result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "parse params failed: %v", err))
return
}
validate := validator.New()
if err := validate.Struct(&req); err != nil {
result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "validate params failed: %v", err))
return
}
l := common.NewGetDownloadLinkLogic(c.Request.Context(), svcCtx)
resp, err := l.GetDownloadLink(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,24 @@
package common
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/common"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func ReportLogMessageHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.ReportLogMessageRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := common.NewReportLogMessageLogic(c.Request.Context(), svcCtx)
resp, err := l.ReportLogMessage(&req, c)
result.HttpResult(c, resp, err)
}
}
+5
View File
@@ -14,4 +14,9 @@ func RegisterNotifyHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
group.Any("/:platform/:token", notify.PaymentNotifyHandler(serverCtx))
}
iap := router.Group("/v1/iap")
{
iap.POST("/notifications", notify.AppleIAPNotifyHandler(serverCtx))
}
}
@@ -0,0 +1,23 @@
package notify
import (
"encoding/json"
"io"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/notify"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
func AppleIAPNotifyHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
raw, _ := io.ReadAll(c.Request.Body)
var body map[string]interface{}
_ = json.Unmarshal(raw, &body)
sp, _ := body["signedPayload"].(string)
l := notify.NewAppleIAPNotifyLogic(c.Request.Context(), svcCtx)
err := l.Handle(sp)
result.HttpResult(c, map[string]bool{"success": err == nil}, err)
}
}
@@ -0,0 +1,23 @@
package apple
import (
"github.com/gin-gonic/gin"
appleLogic "github.com/perfect-panel/server/internal/logic/public/iap/apple"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func AttachAppleTransactionByIdHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.AttachAppleTransactionByIdRequest
_ = c.ShouldBind(&req)
if err := svcCtx.Validate(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
l := appleLogic.NewAttachTransactionByIdLogic(c.Request.Context(), svcCtx)
resp, err := l.AttachById(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,23 @@
package apple
import (
"github.com/gin-gonic/gin"
appleLogic "github.com/perfect-panel/server/internal/logic/public/iap/apple"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func AttachAppleTransactionHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.AttachAppleTransactionRequest
_ = c.ShouldBind(&req)
if err := svcCtx.Validate(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
l := appleLogic.NewAttachTransactionLogic(c.Request.Context(), svcCtx)
resp, err := l.Attach(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,16 @@
package apple
import (
"github.com/gin-gonic/gin"
appleLogic "github.com/perfect-panel/server/internal/logic/public/iap/apple"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
func GetAppleStatusHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
l := appleLogic.NewGetStatusLogic(c.Request.Context(), svcCtx)
resp, err := l.GetStatus()
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,23 @@
package apple
import (
"github.com/gin-gonic/gin"
appleLogic "github.com/perfect-panel/server/internal/logic/public/iap/apple"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func RestoreAppleTransactionsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.RestoreAppleTransactionsRequest
_ = c.ShouldBind(&req)
if err := svcCtx.Validate(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
l := appleLogic.NewRestoreLogic(c.Request.Context(), svcCtx)
err := l.Restore(&req)
result.HttpResult(c, map[string]bool{"success": err == nil}, err)
}
}
@@ -0,0 +1,26 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Bind Email With Verification
func BindEmailWithVerificationHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.BindEmailWithVerificationRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := user.NewBindEmailWithVerificationLogic(c.Request.Context(), svcCtx)
resp, err := l.BindEmailWithVerification(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,25 @@
package user
import (
"github.com/gin-gonic/gin"
logic "github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func BindInviteCodeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.BindInviteCodeRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := logic.NewBindInviteCodeLogic(c.Request.Context(), svcCtx)
err := l.BindInviteCode(&req)
result.HttpResult(c, nil, err)
}
}
@@ -0,0 +1,94 @@
package user
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/config"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/result"
)
// DeleteAccountHandler 注销账号处理器
// 根据当前token删除所有关联设备,然后根据各自设备ID重新新建账号
// 新增:需携带邮箱验证码,验证通过后执行注销
type deleteAccountReq struct {
Email string `json:"email" binding:"required,email"` // 用户邮箱
Code string `json:"code" binding:"required"` // 邮箱验证码
}
func DeleteAccountHandler(serverCtx *svc.ServiceContext) gin.HandlerFunc {
return func(c *gin.Context) {
var req deleteAccountReq
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
return
}
// 统一处理邮箱格式:转小写并去空格,与发送验证码逻辑保持一致
req.Email = strings.ToLower(strings.TrimSpace(req.Email))
// 校验邮箱验证码
if err := verifyEmailCode(c.Request.Context(), serverCtx, req.Email, req.Code); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
l := user.NewDeleteAccountLogic(c.Request.Context(), serverCtx)
resp, err := l.DeleteAccountAll()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
result.HttpResult(c, resp, err)
}
}
// CacheKeyPayload 验证码缓存结构
type CacheKeyPayload struct {
Code string `json:"code"`
LastAt int64 `json:"lastAt"`
}
// verifyEmailCode 校验邮箱验证码
// 支持 DeleteAccount 和 Security 两种场景的验证码
func verifyEmailCode(ctx context.Context, serverCtx *svc.ServiceContext, email string, code string) error {
// 尝试多种场景的验证码
scenes := []string{constant.DeleteAccount.String(), constant.Security.String()}
var verified bool
var cacheKeyUsed string
var payload CacheKeyPayload
for _, scene := range scenes {
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email)
value, err := serverCtx.Redis.Get(ctx, cacheKey).Result()
if err != nil || value == "" {
continue
}
if err := json.Unmarshal([]byte(value), &payload); err != nil {
continue
}
// 检查验证码是否匹配且未过期
if payload.Code == code && time.Now().Unix()-payload.LastAt <= serverCtx.Config.VerifyCode.ExpireTime {
verified = true
cacheKeyUsed = cacheKey
break
}
}
if !verified {
return fmt.Errorf("verification code error or expired")
}
// 验证成功后删除缓存
serverCtx.Redis.Del(ctx, cacheKeyUsed)
return nil
}
@@ -0,0 +1,26 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get agent downloads data
func GetAgentDownloadsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetAgentDownloadsRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := user.NewGetAgentDownloadsLogic(c.Request.Context(), svcCtx)
resp, err := l.GetAgentDownloads(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,26 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get agent realtime data
func GetAgentRealtimeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetAgentRealtimeRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := user.NewGetAgentRealtimeLogic(c.Request.Context(), svcCtx)
resp, err := l.GetAgentRealtime(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,30 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get invite sales data
func GetInviteSalesHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetInviteSalesRequest
if err := c.ShouldBind(&req); err != nil {
result.ParamErrorResult(c, err)
return
}
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := user.NewGetInviteSalesLogic(c.Request.Context(), svcCtx)
resp, err := l.GetInviteSales(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,24 @@
package user
import (
"github.com/gin-gonic/gin"
userLogic "github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
func GetSubscribeStatusHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetSubscribeStatusRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := userLogic.NewGetSubscribeStatusLogic(c.Request.Context(), svcCtx)
resp, err := l.GetSubscribeStatus(&req)
result.HttpResult(c, resp, err)
}
}
@@ -0,0 +1,26 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get user invite statistics
func GetUserInviteStatsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.GetUserInviteStatsRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := user.NewGetUserInviteStatsLogic(c.Request.Context(), svcCtx)
resp, err := l.GetUserInviteStats(&req)
result.HttpResult(c, resp, err)
}
}
@@ -1,17 +1,25 @@
package user
import (
"context"
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/result"
)
// Query User Subscribe
func QueryUserSubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
// 1. Get param from URL Query (?includeExpired=all)
value := c.Query("includeExpired")
l := user.NewQueryUserSubscribeLogic(c.Request.Context(), svcCtx)
// 2. Inject param into Request Context
ctx := context.WithValue(c.Request.Context(), constant.CtxKeyIncludeExpired, value)
l := user.NewQueryUserSubscribeLogic(ctx, svcCtx)
resp, err := l.QueryUserSubscribe()
result.HttpResult(c, resp, err)
}
+52
View File
@@ -28,6 +28,7 @@ import (
common "github.com/perfect-panel/server/internal/handler/common"
publicAnnouncement "github.com/perfect-panel/server/internal/handler/public/announcement"
publicDocument "github.com/perfect-panel/server/internal/handler/public/document"
publicIapApple "github.com/perfect-panel/server/internal/handler/public/iap/apple"
publicOrder "github.com/perfect-panel/server/internal/handler/public/order"
publicPayment "github.com/perfect-panel/server/internal/handler/public/payment"
publicPortal "github.com/perfect-panel/server/internal/handler/public/portal"
@@ -236,6 +237,12 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Filter traffic log details
adminLogGroupRouter.GET("/traffic/details", adminLog.FilterTrafficLogDetailsHandler(serverCtx))
// Error message log list
adminLogGroupRouter.GET("/message/error/list", adminLog.GetErrorLogMessageListHandler(serverCtx))
// Error message log detail
adminLogGroupRouter.GET("/message/error/detail", adminLog.GetErrorLogMessageDetailHandler(serverCtx))
}
adminMarketingGroupRouter := router.Group("/v1/admin/marketing")
@@ -631,6 +638,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// User login
authGroupRouter.POST("/login", auth.UserLoginHandler(serverCtx))
// Email login
authGroupRouter.POST("/login/email", auth.EmailLoginHandler(serverCtx))
// Device Login
authGroupRouter.POST("/login/device", auth.DeviceLoginHandler(serverCtx))
@@ -676,6 +686,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Get Client
commonGroupRouter.GET("/client", common.GetClientHandler(serverCtx))
// Get Download Link
commonGroupRouter.GET("/client/download", common.GetDownloadLinkHandler(serverCtx))
// Heartbeat
commonGroupRouter.GET("/heartbeat", common.HeartbeatHandler(serverCtx))
@@ -696,6 +709,12 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Get Tos Content
commonGroupRouter.GET("/site/tos", common.GetTosHandler(serverCtx))
// Submit contact info
commonGroupRouter.POST("/contact", common.SubmitContactHandler(serverCtx))
// Report client error log
commonGroupRouter.POST("/log/message/report", common.ReportLogMessageHandler(serverCtx))
}
publicAnnouncementGroupRouter := router.Group("/v1/public/announcement")
@@ -785,6 +804,15 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
publicRedemptionGroupRouter.POST("/", publicRedemption.RedeemCodeHandler(serverCtx))
}
iapAppleGroupRouter := router.Group("/v1/public/iap/apple")
iapAppleGroupRouter.Use(middleware.AuthMiddleware(serverCtx), middleware.DeviceMiddleware(serverCtx))
{
iapAppleGroupRouter.GET("/status", publicIapApple.GetAppleStatusHandler(serverCtx))
iapAppleGroupRouter.POST("/transactions/attach", publicIapApple.AttachAppleTransactionHandler(serverCtx))
iapAppleGroupRouter.POST("/transactions/attach_by_id", publicIapApple.AttachAppleTransactionByIdHandler(serverCtx))
iapAppleGroupRouter.POST("/restore", publicIapApple.RestoreAppleTransactionsHandler(serverCtx))
}
publicSubscribeGroupRouter := router.Group("/v1/public/subscribe")
publicSubscribeGroupRouter.Use(middleware.AuthMiddleware(serverCtx), middleware.DeviceMiddleware(serverCtx))
@@ -909,6 +937,30 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Query Withdrawal Log
publicUserGroupRouter.GET("/withdrawal_log", publicUser.QueryWithdrawalLogHandler(serverCtx))
// Bind Email With Verification
publicUserGroupRouter.POST("/bind_email_with_verification", publicUser.BindEmailWithVerificationHandler(serverCtx))
// Bind Invite Code
publicUserGroupRouter.POST("/bind_invite_code", publicUser.BindInviteCodeHandler(serverCtx))
// Get Subscribe Status
publicUserGroupRouter.POST("/subscribe_status", publicUser.GetSubscribeStatusHandler(serverCtx))
// Delete Account
publicUserGroupRouter.POST("/delete_account", publicUser.DeleteAccountHandler(serverCtx))
// Get agent realtime data
publicUserGroupRouter.GET("/agent/realtime", publicUser.GetAgentRealtimeHandler(serverCtx))
// Get agent downloads data
publicUserGroupRouter.GET("/agent/downloads", publicUser.GetAgentDownloadsHandler(serverCtx))
// Get user invite statistics
publicUserGroupRouter.GET("/invite/stats", publicUser.GetUserInviteStatsHandler(serverCtx))
// Get invite sales data
publicUserGroupRouter.GET("/invite/sales", publicUser.GetInviteSalesHandler(serverCtx))
}
publicUserWsGroupRouter := router.Group("/v1/public/user")