64 lines
2.2 KiB
Go
64 lines
2.2 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/prabhupadasays/spsays-bff/internal/model"
|
|
)
|
|
|
|
// 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"
|
|
)
|
|
|
|
// ErrPlanLimit signals that a free-plan limit blocks the requested action.
|
|
// Handlers surface this as HTTP 402 (Payment Required) so clients can show
|
|
// an upsell. The message tells the user what cap was hit.
|
|
var ErrPlanLimit = errors.New("plan limit reached")
|
|
|
|
// PlanLimitError carries detail about which limit was hit so the frontend
|
|
// can render a helpful message instead of a generic "upgrade to premium".
|
|
type PlanLimitError struct {
|
|
Resource string // e.g. "bookmarks", "private collections"
|
|
Limit int
|
|
Plan string
|
|
}
|
|
|
|
func (e *PlanLimitError) Error() string {
|
|
return fmt.Sprintf("%s limit reached (%d on %s plan)", e.Resource, e.Limit, e.Plan)
|
|
}
|
|
|
|
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. 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. 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"
|
|
}
|
|
if limit >= 0 && currentCount >= limit {
|
|
return &PlanLimitError{Resource: resource, Limit: limit, Plan: u.Plan}
|
|
}
|
|
return nil
|
|
}
|