Compare commits

...

3 Commits

Author SHA1 Message Date
98d8525fa9 refactor: 简化 trial 赠送配置,删除多余的白名单启用开关
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m42s
**简化方案**:
- 删除 `EnableTrialEmailWhitelist` 配置字段(多余)
- 保留 `TrialEmailDomainWhitelist`(逗号分隔的白名单域名)
- 赠送规则:白名单**非空** 且 **邮箱在列表** 才赠送

**新的赠送逻辑**:
```yaml
TrialEmailDomainWhitelist: "qq.com,163.com,gmail.com"
```
- 为空 →  不赠送(关闭)
- 非空 →  赠送给白名单域名的邮箱

**更新的地方**:
1. internal/config/config.go - 删除 EnableTrialEmailWhitelist 字段
2. userRegisterLogic.go - 简化赠送逻辑
3. emailLoginLogic.go - 简化赠送逻辑
4. bindEmailWithVerificationLogic.go - 简化赠送逻辑
5. oAuthLoginGetTokenLogic.go - 简化赠送逻辑

**配置升级说明**:
旧配置:
```yaml
EnableTrial: true
EnableTrialEmailWhitelist: true
TrialEmailDomainWhitelist: "qq.com,163.com"
```

新配置(只保留两项):
```yaml
EnableTrial: true
TrialEmailDomainWhitelist: "qq.com,163.com"
```

关闭赠送:
```yaml
EnableTrial: true
TrialEmailDomainWhitelist: ""
```

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-03 07:20:10 -07:00
19777df2ed fix: bindEmailWithVerificationLogic 邮箱绑定后赠送逻辑错误
**问题**: 邮箱绑定后应该赠送 trial 的逻辑判断错误,导致以下场景无法赠送:
- EnableTrial=true, EnableTrialEmailWhitelist=false → 应该赠送但未赠送

**根本原因**: 第215行条件判断使用 OR 逻辑,要求白名单必须启用才处理
```go
if !rc.EnableTrial || !rc.EnableTrialEmailWhitelist {
    return  //  错误:关闭白名单时也返回,无法赠送
}
```

**修复**: 改为正确的逻辑
```go
if !rc.EnableTrial {
    return  // 关闭赠送时不处理
}
if rc.EnableTrialEmailWhitelist && !IsEmailDomainWhitelisted(...) {
    return  // 白名单启用但域名不匹配时不赠送
}
// 否则赠送
```

**影响场景**:
- 设备登录 → 绑定邮箱 → 应该赠送 trial
  - 当 EnableTrialEmailWhitelist=false 时,应该赠送(修复前未赠送)
  - 当 EnableTrialEmailWhitelist=true 且域名在白名单 → 赠送(修复前未赠送)

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-03 07:17:27 -07:00
d586bbeabb fix: OAuth registration missing email domain whitelist check for trial gifting
**Problem**: OAuth registration path (Google, Apple, Telegram) was missing the
email domain whitelist validation, causing trial subscriptions to be granted to
all users regardless of the whitelist configuration.

**Root Cause**: The previous commit (3417da2a) that implemented trial domain
whitelist only updated device/phone/email direct registration paths, but
missed the OAuth registration path in oAuthLoginGetTokenLogic.go.

**Solution**:
- Added email domain whitelist check to OAuth register() method
- Added isEmailDomainWhitelisted() helper function matching the pattern
  used in other auth logic files
- Only activate trial if EnableTrial=true AND
  (whitelist disabled OR email domain matches whitelist)
- Added email logging to trial subscription activation log

Affected flows:
- OAuth Google login with new user
- OAuth Apple login with new user
- OAuth Telegram login with new user

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-03 06:44:33 -07:00
5 changed files with 31 additions and 7 deletions

View File

@ -87,12 +87,11 @@ type RegisterConfig struct {
TrialSubscribe int64 `yaml:"TrialSubscribe" default:"0"`
TrialTime int64 `yaml:"TrialTime" default:"0"`
TrialTimeUnit string `yaml:"TrialTimeUnit" default:""`
TrialEmailDomainWhitelist string `yaml:"TrialEmailDomainWhitelist" default:""`
IpRegisterLimit int64 `yaml:"IpRegisterLimit" default:"0"`
IpRegisterLimitDuration int64 `yaml:"IpRegisterLimitDuration" default:"0"`
EnableIpRegisterLimit bool `yaml:"EnableIpRegisterLimit" default:"false"`
DeviceLimit int64 `yaml:"DeviceLimit" default:"2"`
EnableTrialEmailWhitelist bool `yaml:"EnableTrialEmailWhitelist" default:"false"`
TrialEmailDomainWhitelist string `yaml:"TrialEmailDomainWhitelist" default:""`
}
type EmailConfig struct {

View File

@ -126,7 +126,7 @@ func (l *EmailLoginLogic) EmailLogin(req *types.EmailLoginRequest) (resp *types.
return err
}
rc := l.svcCtx.Config.Register
if rc.EnableTrial && (!rc.EnableTrialEmailWhitelist || IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist)) {
if rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist) {
if err = l.activeTrial(userInfo.Id); err != nil {
return err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/perfect-panel/server/internal/config"
@ -393,10 +394,15 @@ func (l *OAuthLoginGetTokenLogic) register(email, avatar, method, openid, reques
}
}
if l.svcCtx.Config.Register.EnableTrial {
rc := l.svcCtx.Config.Register
// Only activate trial if email domain is in whitelist (whitelist cannot be empty)
shouldActivateTrial := rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && (email != "" && l.isEmailDomainWhitelisted(email, rc.TrialEmailDomainWhitelist))
if shouldActivateTrial {
l.Debugw("activating trial subscription",
logger.Field("request_id", requestID),
logger.Field("user_id", userInfo.Id),
logger.Field("email", email),
)
var trialErr error
trialSubscribe, trialErr = l.activeTrial(userInfo.Id, requestID)
@ -882,3 +888,22 @@ func (l *OAuthLoginGetTokenLogic) activeTrial(uid int64, requestID string) (*use
)
return userSub, nil
}
// isEmailDomainWhitelisted checks if the email's domain is in the comma-separated whitelist.
// Returns false if the email format is invalid.
func (l *OAuthLoginGetTokenLogic) isEmailDomainWhitelisted(email, whitelistCSV string) bool {
if whitelistCSV == "" {
return false
}
parts := strings.SplitN(email, "@", 2)
if len(parts) != 2 {
return false
}
domain := strings.ToLower(strings.TrimSpace(parts[1]))
for _, d := range strings.Split(whitelistCSV, ",") {
if strings.ToLower(strings.TrimSpace(d)) == domain {
return true
}
}
return false
}

View File

@ -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)
rc := l.svcCtx.Config.Register
if rc.EnableTrial && (!rc.EnableTrialEmailWhitelist || IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist)) {
if rc.EnableTrial && rc.TrialEmailDomainWhitelist != "" && IsEmailDomainWhitelisted(req.Email, rc.TrialEmailDomainWhitelist) {
trialSubscribe, err = l.activeTrial(userInfo.Id)
if err != nil {
l.Errorw("Failed to activate trial subscription", logger.Field("error", err.Error()))

View File

@ -209,10 +209,10 @@ func (l *BindEmailWithVerificationLogic) refreshBindSessionToken(userId int64) (
}
// tryGrantTrialOnEmailBind grants trial subscription to the email user (family owner)
// if EnableTrialEmailWhitelist is on and the email domain matches.
// 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.EnableTrialEmailWhitelist {
if !rc.EnableTrial || rc.TrialEmailDomainWhitelist == "" {
return
}
if !auth.IsEmailDomainWhitelisted(email, rc.TrialEmailDomainWhitelist) {