feat: enhance limits
This commit is contained in:
@@ -157,6 +157,12 @@ func (s *AdminService) SetUserBanned(ctx context.Context, userID string, banned
|
||||
return s.repo.SetUserBanned(ctx, userID, banned)
|
||||
}
|
||||
|
||||
// SetUserLimits writes per-user save-limit overrides (a nil field resets that
|
||||
// limit to the plan default). found=false when the id is unknown.
|
||||
func (s *AdminService) SetUserLimits(ctx context.Context, userID string, bookmark, publicCollection, privateCollection *int) (bool, error) {
|
||||
return s.repo.SetUserLimits(ctx, userID, bookmark, publicCollection, privateCollection)
|
||||
}
|
||||
|
||||
// feedbackReplyEmailTpl renders the reply email. Reply text is auto-escaped by
|
||||
// html/template, so admin-entered text can't inject markup.
|
||||
var feedbackReplyEmailTpl = template.Must(template.New("feedbackReply").Parse(`<div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#2a2118;max-width:560px;margin:0 auto;font-size:17px;line-height:1.7">
|
||||
|
||||
+23
-47
@@ -3,19 +3,16 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/prabhupadasays/spsays-bff/internal/model"
|
||||
)
|
||||
|
||||
// Plan caps for the social features. Updates here propagate everywhere via
|
||||
// EnforceBookmarkLimit / EnforceCollectionLimit.
|
||||
// Plan identifiers. The numeric caps live in model.ResolveLimits (so the self
|
||||
// user payload and these enforcers agree), and per-user admin overrides take
|
||||
// precedence over the plan default.
|
||||
const (
|
||||
PlanFree = "free"
|
||||
PlanPremium = "premium"
|
||||
|
||||
freeBookmarkLimit = 1
|
||||
premiumBookmarkLimit = 5
|
||||
|
||||
freeCollectionPerVisibilityLimit = 5 // 5 public AND 5 private separately
|
||||
// Premium has no collection cap.
|
||||
)
|
||||
|
||||
// ErrPlanLimit signals that a free-plan limit blocks the requested action.
|
||||
@@ -38,50 +35,29 @@ func (e *PlanLimitError) Error() string {
|
||||
func (e *PlanLimitError) Is(target error) bool { return target == ErrPlanLimit }
|
||||
|
||||
// EnforceBookmarkLimit returns nil if the user is allowed to create one more
|
||||
// bookmark, or a PlanLimitError otherwise.
|
||||
func EnforceBookmarkLimit(plan string, currentCount int) error {
|
||||
limit := bookmarkLimitFor(plan)
|
||||
if currentCount >= limit {
|
||||
return &PlanLimitError{Resource: "bookmarks", Limit: limit, Plan: plan}
|
||||
// bookmark, or a PlanLimitError otherwise. The cap is the user's effective
|
||||
// bookmark limit (plan default, or an admin override). A negative cap means
|
||||
// unlimited.
|
||||
func EnforceBookmarkLimit(u *model.User, currentCount int) error {
|
||||
limit := u.EffectiveLimits().Bookmarks
|
||||
if limit >= 0 && currentCount >= limit {
|
||||
return &PlanLimitError{Resource: "bookmarks", Limit: limit, Plan: u.Plan}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnforceCollectionLimit returns nil if the user is allowed to create one
|
||||
// more collection of the given visibility.
|
||||
//
|
||||
// `isPublic=true` → checks against public-collection cap
|
||||
// `isPublic=false` → checks against private-collection cap
|
||||
// Premium users have no cap → always nil.
|
||||
func EnforceCollectionLimit(plan string, isPublic bool, currentCount int) error {
|
||||
if plan == PlanPremium {
|
||||
return nil
|
||||
// EnforceCollectionLimit returns nil if the user is allowed to create one more
|
||||
// collection of the given visibility. The cap is the user's effective
|
||||
// public/private collection limit (plan default, or an admin override); a
|
||||
// negative cap means unlimited.
|
||||
func EnforceCollectionLimit(u *model.User, isPublic bool, currentCount int) error {
|
||||
l := u.EffectiveLimits()
|
||||
limit, resource := l.PrivateCollections, "private collections"
|
||||
if isPublic {
|
||||
limit, resource = l.PublicCollections, "public collections"
|
||||
}
|
||||
limit := freeCollectionPerVisibilityLimit
|
||||
if currentCount >= limit {
|
||||
resource := "private collections"
|
||||
if isPublic {
|
||||
resource = "public collections"
|
||||
}
|
||||
return &PlanLimitError{Resource: resource, Limit: limit, Plan: plan}
|
||||
if limit >= 0 && currentCount >= limit {
|
||||
return &PlanLimitError{Resource: resource, Limit: limit, Plan: u.Plan}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bookmarkLimitFor(plan string) int {
|
||||
if plan == PlanPremium {
|
||||
return premiumBookmarkLimit
|
||||
}
|
||||
return freeBookmarkLimit
|
||||
}
|
||||
|
||||
// BookmarkLimitFor returns the cap (for showing "X / Y used" in the UI).
|
||||
func BookmarkLimitFor(plan string) int { return bookmarkLimitFor(plan) }
|
||||
|
||||
// CollectionLimitFor returns the per-visibility cap. -1 means unlimited.
|
||||
func CollectionLimitFor(plan string) int {
|
||||
if plan == PlanPremium {
|
||||
return -1
|
||||
}
|
||||
return freeCollectionPerVisibilityLimit
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ func (s *BookmarkService) Create(ctx context.Context, userID, sourceURL, sourceT
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := EnforceBookmarkLimit(u.Plan, current); err != nil {
|
||||
if err := EnforceBookmarkLimit(u, current); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -602,7 +602,7 @@ func (s *CollectionService) Create(ctx context.Context, userID, title string, de
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := EnforceCollectionLimit(u.Plan, isPublic, current); err != nil {
|
||||
if err := EnforceCollectionLimit(u, isPublic, current); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.collections.Create(ctx, userID, title, description, coverURL, isPublic)
|
||||
@@ -628,7 +628,7 @@ func (s *CollectionService) Update(ctx context.Context, id, userID string, title
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := EnforceCollectionLimit(u.Plan, *isPublic, current); err != nil {
|
||||
if err := EnforceCollectionLimit(u, *isPublic, current); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user