This commit is contained in:
parent
98d8525fa9
commit
e818ac8764
@ -84,6 +84,7 @@ type SubscribeConfig struct {
|
|||||||
type RegisterConfig struct {
|
type RegisterConfig struct {
|
||||||
StopRegister bool `yaml:"StopRegister" default:"false"`
|
StopRegister bool `yaml:"StopRegister" default:"false"`
|
||||||
EnableTrial bool `yaml:"EnableTrial" default:"false"`
|
EnableTrial bool `yaml:"EnableTrial" default:"false"`
|
||||||
|
EnableTrialEmailWhitelist bool `yaml:"EnableTrialEmailWhitelist" default:"true"`
|
||||||
TrialSubscribe int64 `yaml:"TrialSubscribe" default:"0"`
|
TrialSubscribe int64 `yaml:"TrialSubscribe" default:"0"`
|
||||||
TrialTime int64 `yaml:"TrialTime" default:"0"`
|
TrialTime int64 `yaml:"TrialTime" default:"0"`
|
||||||
TrialTimeUnit string `yaml:"TrialTimeUnit" default:""`
|
TrialTimeUnit string `yaml:"TrialTimeUnit" default:""`
|
||||||
|
|||||||
@ -126,7 +126,7 @@ func (l *EmailLoginLogic) EmailLogin(req *types.EmailLoginRequest) (resp *types.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rc := l.svcCtx.Config.Register
|
rc := l.svcCtx.Config.Register
|
||||||
if rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist) {
|
if ShouldGrantTrialForEmail(rc, req.Email) {
|
||||||
if err = l.activeTrial(userInfo.Id); err != nil {
|
if err = l.activeTrial(userInfo.Id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/config"
|
"github.com/perfect-panel/server/internal/config"
|
||||||
|
authlogic "github.com/perfect-panel/server/internal/logic/auth"
|
||||||
"github.com/perfect-panel/server/internal/model/auth"
|
"github.com/perfect-panel/server/internal/model/auth"
|
||||||
"github.com/perfect-panel/server/internal/model/log"
|
"github.com/perfect-panel/server/internal/model/log"
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
@ -395,8 +396,7 @@ func (l *OAuthLoginGetTokenLogic) register(email, avatar, method, openid, reques
|
|||||||
}
|
}
|
||||||
|
|
||||||
rc := l.svcCtx.Config.Register
|
rc := l.svcCtx.Config.Register
|
||||||
// Only activate trial if email domain is in whitelist (whitelist cannot be empty)
|
shouldActivateTrial := email != "" && authlogic.ShouldGrantTrialForEmail(rc, email)
|
||||||
shouldActivateTrial := rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && (email != "" && l.isEmailDomainWhitelisted(email, rc.TrialEmailDomainWhitelist))
|
|
||||||
|
|
||||||
if shouldActivateTrial {
|
if shouldActivateTrial {
|
||||||
l.Debugw("activating trial subscription",
|
l.Debugw("activating trial subscription",
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
// IsEmailDomainWhitelisted checks if the email's domain is in the comma-separated whitelist.
|
// IsEmailDomainWhitelisted checks if the email's domain is in the comma-separated whitelist.
|
||||||
// Returns false if the email format is invalid.
|
// Returns false if the email format is invalid.
|
||||||
@ -20,3 +24,16 @@ func IsEmailDomainWhitelisted(email, whitelistCSV string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ShouldGrantTrialForEmail(register config.RegisterConfig, email string) bool {
|
||||||
|
if !register.EnableTrial {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !register.EnableTrialEmailWhitelist {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if register.TrialEmailDomainWhitelist == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return IsEmailDomainWhitelisted(email, register.TrialEmailDomainWhitelist)
|
||||||
|
}
|
||||||
|
|||||||
@ -148,7 +148,7 @@ func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *
|
|||||||
|
|
||||||
// Activate trial subscription after transaction success (moved outside transaction to reduce lock time)
|
// Activate trial subscription after transaction success (moved outside transaction to reduce lock time)
|
||||||
rc := l.svcCtx.Config.Register
|
rc := l.svcCtx.Config.Register
|
||||||
if rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist) {
|
if ShouldGrantTrialForEmail(rc, req.Email) {
|
||||||
trialSubscribe, err = l.activeTrial(userInfo.Id)
|
trialSubscribe, err = l.activeTrial(userInfo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorw("Failed to activate trial subscription", logger.Field("error", err.Error()))
|
l.Errorw("Failed to activate trial subscription", logger.Field("error", err.Error()))
|
||||||
|
|||||||
@ -8,14 +8,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/config"
|
"github.com/perfect-panel/server/internal/config"
|
||||||
"github.com/perfect-panel/server/internal/logic/auth"
|
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
"github.com/perfect-panel/server/internal/types"
|
"github.com/perfect-panel/server/internal/types"
|
||||||
"github.com/perfect-panel/server/pkg/constant"
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
"github.com/perfect-panel/server/pkg/jwt"
|
"github.com/perfect-panel/server/pkg/jwt"
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
"github.com/perfect-panel/server/pkg/tool"
|
|
||||||
"github.com/perfect-panel/server/pkg/uuidx"
|
"github.com/perfect-panel/server/pkg/uuidx"
|
||||||
"github.com/perfect-panel/server/pkg/xerr"
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -129,8 +127,7 @@ func (l *BindEmailWithVerificationLogic) BindEmailWithVerification(req *types.Bi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Grant trial subscription if email domain is whitelisted
|
tryGrantTrialOnEmailBind(l.ctx, l.svcCtx, l.Logger, emailUser.Id, req.Email)
|
||||||
l.tryGrantTrialOnEmailBind(emailUser.Id, req.Email)
|
|
||||||
return &types.BindEmailWithVerificationResponse{
|
return &types.BindEmailWithVerificationResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
Message: "email user created and joined family",
|
Message: "email user created and joined family",
|
||||||
@ -158,8 +155,7 @@ func (l *BindEmailWithVerificationLogic) BindEmailWithVerification(req *types.Bi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grant trial subscription if email domain is whitelisted
|
tryGrantTrialOnEmailBind(l.ctx, l.svcCtx, l.Logger, existingMethod.UserId, req.Email)
|
||||||
l.tryGrantTrialOnEmailBind(existingMethod.UserId, req.Email)
|
|
||||||
|
|
||||||
return &types.BindEmailWithVerificationResponse{
|
return &types.BindEmailWithVerificationResponse{
|
||||||
Success: true,
|
Success: true,
|
||||||
@ -207,74 +203,3 @@ func (l *BindEmailWithVerificationLogic) refreshBindSessionToken(userId int64) (
|
|||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryGrantTrialOnEmailBind grants trial subscription to the email user (family owner)
|
|
||||||
// if email domain is in the configured whitelist (or if whitelist is empty, no trial is granted).
|
|
||||||
func (l *BindEmailWithVerificationLogic) tryGrantTrialOnEmailBind(ownerUserId int64, email string) {
|
|
||||||
rc := l.svcCtx.Config.Register
|
|
||||||
if !rc.EnableTrial || rc.TrialEmailDomainWhitelist == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !auth.IsEmailDomainWhitelisted(email, rc.TrialEmailDomainWhitelist) {
|
|
||||||
l.Infow("email domain not in trial whitelist, skip",
|
|
||||||
logger.Field("email", email),
|
|
||||||
logger.Field("owner_user_id", ownerUserId),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Anti-duplicate: check if owner already has trial subscription
|
|
||||||
var count int64
|
|
||||||
if err := l.svcCtx.DB.WithContext(l.ctx).
|
|
||||||
Model(&user.Subscribe{}).
|
|
||||||
Where("user_id = ? AND subscribe_id = ?", ownerUserId, rc.TrialSubscribe).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
l.Errorw("failed to check existing trial", logger.Field("error", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
l.Infow("trial already granted, skip",
|
|
||||||
logger.Field("owner_user_id", ownerUserId),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, rc.TrialSubscribe)
|
|
||||||
if err != nil {
|
|
||||||
l.Errorw("failed to find trial subscribe template", logger.Field("error", err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userSub := &user.Subscribe{
|
|
||||||
UserId: ownerUserId,
|
|
||||||
OrderId: 0,
|
|
||||||
SubscribeId: sub.Id,
|
|
||||||
StartTime: time.Now(),
|
|
||||||
ExpireTime: tool.AddTime(rc.TrialTimeUnit, rc.TrialTime, time.Now()),
|
|
||||||
Traffic: sub.Traffic,
|
|
||||||
Download: 0,
|
|
||||||
Upload: 0,
|
|
||||||
Token: uuidx.NewUUID().String(),
|
|
||||||
UUID: uuidx.NewUUID().String(),
|
|
||||||
Status: 1,
|
|
||||||
}
|
|
||||||
if err = l.svcCtx.UserModel.InsertSubscribe(l.ctx, userSub); err != nil {
|
|
||||||
l.Errorw("failed to insert trial subscribe",
|
|
||||||
logger.Field("error", err.Error()),
|
|
||||||
logger.Field("owner_user_id", ownerUserId),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertSubscribe auto-clears user subscribe cache via execSubscribeMutation.
|
|
||||||
// Clear server cache so nodes pick up the new subscription.
|
|
||||||
if err = l.svcCtx.NodeModel.ClearServerAllCache(l.ctx); err != nil {
|
|
||||||
l.Errorw("ClearServerAllCache error", logger.Field("error", err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Infow("trial granted on email bind",
|
|
||||||
logger.Field("owner_user_id", ownerUserId),
|
|
||||||
logger.Field("email", email),
|
|
||||||
logger.Field("subscribe_id", sub.Id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
80
internal/logic/public/user/emailTrialGrant.go
Normal file
80
internal/logic/public/user/emailTrialGrant.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/logic/auth"
|
||||||
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
|
"github.com/perfect-panel/server/pkg/uuidx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tryGrantTrialOnEmailBind(ctx context.Context, svcCtx *svc.ServiceContext, log logger.Logger, ownerUserId int64, email string) {
|
||||||
|
rc := svcCtx.Config.Register
|
||||||
|
if !auth.ShouldGrantTrialForEmail(rc, email) {
|
||||||
|
if rc.EnableTrial && rc.EnableTrialEmailWhitelist {
|
||||||
|
log.Infow("email domain not in trial whitelist, skip",
|
||||||
|
logger.Field("email", email),
|
||||||
|
logger.Field("owner_user_id", ownerUserId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := svcCtx.DB.WithContext(ctx).
|
||||||
|
Model(&user.Subscribe{}).
|
||||||
|
Where("user_id = ? AND subscribe_id = ?", ownerUserId, rc.TrialSubscribe).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
log.Errorw("failed to check existing trial", logger.Field("error", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
log.Infow("trial already granted, skip",
|
||||||
|
logger.Field("owner_user_id", ownerUserId),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, err := svcCtx.SubscribeModel.FindOne(ctx, rc.TrialSubscribe)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorw("failed to find trial subscribe template", logger.Field("error", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userSub := &user.Subscribe{
|
||||||
|
UserId: ownerUserId,
|
||||||
|
OrderId: 0,
|
||||||
|
SubscribeId: sub.Id,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
ExpireTime: tool.AddTime(rc.TrialTimeUnit, rc.TrialTime, time.Now()),
|
||||||
|
Traffic: sub.Traffic,
|
||||||
|
Download: 0,
|
||||||
|
Upload: 0,
|
||||||
|
Token: uuidx.NewUUID().String(),
|
||||||
|
UUID: uuidx.NewUUID().String(),
|
||||||
|
Status: 1,
|
||||||
|
}
|
||||||
|
if err = svcCtx.UserModel.InsertSubscribe(ctx, userSub); err != nil {
|
||||||
|
log.Errorw("failed to insert trial subscribe",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("owner_user_id", ownerUserId),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if svcCtx.NodeModel != nil {
|
||||||
|
if err = svcCtx.NodeModel.ClearServerAllCache(ctx); err != nil {
|
||||||
|
log.Errorw("ClearServerAllCache error", logger.Field("error", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infow("trial granted on email bind",
|
||||||
|
logger.Field("owner_user_id", ownerUserId),
|
||||||
|
logger.Field("email", email),
|
||||||
|
logger.Field("subscribe_id", sub.Id),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -77,5 +77,6 @@ func (l *VerifyEmailLogic) VerifyEmail(req *types.VerifyEmailRequest) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateUserAuthMethods error")
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateUserAuthMethods error")
|
||||||
}
|
}
|
||||||
|
tryGrantTrialOnEmailBind(l.ctx, l.svcCtx, l.Logger, method.UserId, req.Email)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user