fix(adtracking): 修复生产网关反代路由、成交态统计口径与安全加固
This commit is contained in:
@@ -13,12 +13,13 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLinkNotFound = errors.New("推广链接不存在")
|
||||
ErrLinkDisabled = errors.New("该推广链接已被停用")
|
||||
ErrCodeAlreadyExists = errors.New("该自定义后缀已被占用,请更换")
|
||||
ErrInvalidCodeFormat = errors.New("后缀格式不正确,仅支持2-64位英文、数字、下划线和连字符")
|
||||
ErrReservedCode = errors.New("该后缀为系统保留字,不可使用")
|
||||
codePattern = regexp.MustCompile(`^[a-zA-Z0-9_-]{2,64}$`)
|
||||
ErrLinkNotFound = errors.New("推广链接不存在")
|
||||
ErrLinkDisabled = errors.New("该推广链接已被停用")
|
||||
ErrCodeAlreadyExists = errors.New("该自定义后缀已被占用,请更换")
|
||||
ErrInvalidCodeFormat = errors.New("后缀格式不正确,仅支持2-64位英文、数字、下划线和连字符")
|
||||
ErrReservedCode = errors.New("该后缀为系统保留字,不可使用")
|
||||
ErrDisallowedTargetDomain = errors.New("目标落地页仅允许为 hao858.com 及其子域名或站内路径")
|
||||
codePattern = regexp.MustCompile(`^[a-zA-Z0-9_-]{2,64}$`)
|
||||
)
|
||||
|
||||
var reservedCodes = map[string]bool{
|
||||
@@ -27,6 +28,28 @@ var reservedCodes = map[string]bool{
|
||||
"pay": true, "notice": true, "chat": true, "dashboard": true,
|
||||
}
|
||||
|
||||
func validateTargetURL(rawURL string) (string, error) {
|
||||
targetURL := strings.TrimSpace(rawURL)
|
||||
if targetURL == "" {
|
||||
return "https://hao858.com/", nil
|
||||
}
|
||||
if strings.HasPrefix(targetURL, "/") {
|
||||
return "https://hao858.com" + targetURL, nil
|
||||
}
|
||||
if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
|
||||
targetURL = "https://" + targetURL
|
||||
}
|
||||
u, err := url.Parse(targetURL)
|
||||
if err != nil {
|
||||
return "", errors.New("目标落地页地址不合法")
|
||||
}
|
||||
host := strings.ToLower(u.Hostname())
|
||||
if host != "hao858.com" && !strings.HasSuffix(host, ".hao858.com") && host != "localhost" && host != "127.0.0.1" {
|
||||
return "", ErrDisallowedTargetDomain
|
||||
}
|
||||
return targetURL, nil
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repo *Repository
|
||||
redis *redis.Client
|
||||
@@ -54,11 +77,9 @@ func (s *Service) CreateLink(ctx context.Context, adminID uint64, req CreateTrac
|
||||
return nil, ErrCodeAlreadyExists
|
||||
}
|
||||
|
||||
targetURL := strings.TrimSpace(req.TargetURL)
|
||||
if targetURL == "" {
|
||||
targetURL = "https://hao858.com/"
|
||||
} else if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
|
||||
targetURL = "https://" + targetURL
|
||||
targetURL, err := validateTargetURL(req.TargetURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelType := strings.TrimSpace(req.ChannelType)
|
||||
@@ -92,11 +113,9 @@ func (s *Service) UpdateLink(ctx context.Context, id uint64, req UpdateTrackingL
|
||||
return ErrLinkNotFound
|
||||
}
|
||||
|
||||
targetURL := strings.TrimSpace(req.TargetURL)
|
||||
if targetURL == "" {
|
||||
targetURL = "https://hao858.com/"
|
||||
} else if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
|
||||
targetURL = "https://" + targetURL
|
||||
targetURL, err := validateTargetURL(req.TargetURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channelType := strings.TrimSpace(req.ChannelType)
|
||||
@@ -151,9 +170,30 @@ func (s *Service) ResolveRedirect(ctx context.Context, code, ip, ua, referer, vi
|
||||
// 异步记录点击流水,防止写库阻塞跳转
|
||||
deviceType, os, browser := parseUserAgent(ua)
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// 保护主进程,防止异步协程 panic
|
||||
}
|
||||
}()
|
||||
|
||||
bgCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 防刷频控:同一个访客/IP在 5 秒内重复点击相同短链,不重复记录流水
|
||||
if s.redis != nil {
|
||||
dedupID := visitorID
|
||||
if dedupID == "" {
|
||||
dedupID = ip
|
||||
}
|
||||
if dedupID != "" {
|
||||
dedupKey := "adtrack:dedup:" + link.Code + ":" + dedupID
|
||||
ok, rErr := s.redis.SetNX(bgCtx, dedupKey, "1", 5*time.Second).Result()
|
||||
if rErr == nil && !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
click := &AdTrackingClick{
|
||||
LinkID: link.ID,
|
||||
Code: link.Code,
|
||||
|
||||
Reference in New Issue
Block a user