fix: audit fixes for websearch, notifications, and channel pricing
P0: fix wildcard matching test assertion (config order, not longest prefix) P0: add TotalRecharged to auth cache snapshot (v5) for percentage threshold P1: move pricing rules into per-platform sections in ChannelsView P1: populate account name cache when editing existing channel rules P1: sanitize email subject headers to prevent SMTP injection P1: make Redis INCR+EXPIRE idempotent for rate limiting P1: deep copy FeaturesConfig in Channel.Clone() P2: clean up stale email="" placeholder comments P2: replace log.Printf with slog in email_service.go
This commit is contained in:
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
@@ -10,10 +11,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
verifyCodeKeyPrefix = "verify_code:"
|
||||
notifyVerifyKeyPrefix = "notify_verify:"
|
||||
passwordResetKeyPrefix = "password_reset:"
|
||||
passwordResetSentAtKeyPrefix = "password_reset_sent:"
|
||||
verifyCodeKeyPrefix = "verify_code:"
|
||||
notifyVerifyKeyPrefix = "notify_verify:"
|
||||
passwordResetKeyPrefix = "password_reset:"
|
||||
passwordResetSentAtKeyPrefix = "password_reset_sent:"
|
||||
notifyCodeUserRateKeyPrefix = "notify_code_user_rate:"
|
||||
)
|
||||
|
||||
// verifyCodeKey generates the Redis key for email verification code.
|
||||
@@ -141,3 +143,31 @@ func (c *emailCache) DeleteNotifyVerifyCode(ctx context.Context, email string) e
|
||||
key := notifyVerifyKey(email)
|
||||
return c.rdb.Del(ctx, key).Err()
|
||||
}
|
||||
|
||||
// User-level rate limiting for notify email verification codes
|
||||
|
||||
func notifyCodeUserRateKey(userID int64) string {
|
||||
return notifyCodeUserRateKeyPrefix + fmt.Sprintf("%d", userID)
|
||||
}
|
||||
|
||||
func (c *emailCache) IncrNotifyCodeUserRate(ctx context.Context, userID int64, window time.Duration) (int64, error) {
|
||||
key := notifyCodeUserRateKey(userID)
|
||||
count, err := c.rdb.Incr(ctx, key).Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Always set TTL (idempotent) to avoid orphan keys if process crashes between INCR and EXPIRE.
|
||||
if err := c.rdb.Expire(ctx, key, window).Err(); err != nil {
|
||||
return count, fmt.Errorf("expire notify code rate key: %w", err)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (c *emailCache) GetNotifyCodeUserRate(ctx context.Context, userID int64) (int64, error) {
|
||||
key := notifyCodeUserRateKey(userID)
|
||||
count, err := c.rdb.Get(ctx, key).Int64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user