feat: enhance limits
This commit is contained in:
@@ -24,6 +24,13 @@ type AdminUser struct {
|
||||
// Providers lists linked OAuth providers (e.g. "google", "facebook").
|
||||
HasPassword bool `json:"has_password"`
|
||||
Providers []string `json:"providers"`
|
||||
|
||||
// Effective save limits in force for this user (plan default merged with any
|
||||
// admin override); -1 means unlimited. The admin UI pre-fills the "Edit
|
||||
// limits" modal with these.
|
||||
BookmarkLimit int `json:"bookmark_limit"`
|
||||
PublicCollectionLimit int `json:"public_collection_limit"`
|
||||
PrivateCollectionLimit int `json:"private_collection_limit"`
|
||||
}
|
||||
|
||||
// AdminPost is a row in the admin posts table.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package model
|
||||
|
||||
// Plan-default save limits (-1 = unlimited). Per-user override columns on the
|
||||
// users row take precedence when non-nil; admins set them from the panel.
|
||||
const (
|
||||
planPremium = "premium"
|
||||
|
||||
defaultFreeBookmarkLimit = 1
|
||||
defaultPremiumBookmarkLimit = 5
|
||||
defaultFreeCollectionLimit = 5 // per visibility; premium = unlimited
|
||||
)
|
||||
|
||||
// UserLimits are the effective per-resource caps in force for a user (-1 means
|
||||
// unlimited). Surfaced on the self user payload so the app can render
|
||||
// "X / Y used" and gate create actions without hardcoding the numbers.
|
||||
type UserLimits struct {
|
||||
Bookmarks int `json:"bookmarks"`
|
||||
PublicCollections int `json:"public_collections"`
|
||||
PrivateCollections int `json:"private_collections"`
|
||||
}
|
||||
|
||||
// ResolveLimits computes the effective caps from a plan plus optional per-user
|
||||
// overrides (nil = use the plan default). Shared by the self payload and the
|
||||
// admin user listing so both agree on what's in force.
|
||||
func ResolveLimits(plan string, bookmark, publicCollection, privateCollection *int) UserLimits {
|
||||
premium := plan == planPremium
|
||||
|
||||
bm := defaultFreeBookmarkLimit
|
||||
if premium {
|
||||
bm = defaultPremiumBookmarkLimit
|
||||
}
|
||||
pub, priv := defaultFreeCollectionLimit, defaultFreeCollectionLimit
|
||||
if premium {
|
||||
pub, priv = -1, -1
|
||||
}
|
||||
if bookmark != nil {
|
||||
bm = *bookmark
|
||||
}
|
||||
if publicCollection != nil {
|
||||
pub = *publicCollection
|
||||
}
|
||||
if privateCollection != nil {
|
||||
priv = *privateCollection
|
||||
}
|
||||
return UserLimits{Bookmarks: bm, PublicCollections: pub, PrivateCollections: priv}
|
||||
}
|
||||
|
||||
// EffectiveLimits resolves this user's caps (plan default + any overrides).
|
||||
func (u *User) EffectiveLimits() UserLimits {
|
||||
return ResolveLimits(u.Plan, u.BookmarkLimit, u.PublicCollectionLimit, u.PrivateCollectionLimit)
|
||||
}
|
||||
+10
-1
@@ -75,6 +75,13 @@ type User struct {
|
||||
// BannedAt suspends the account when set: RequireAuth rejects the session
|
||||
// and LogIn refuses to issue a new one. nil = in good standing.
|
||||
BannedAt *time.Time `json:"banned_at,omitempty"`
|
||||
|
||||
// Per-user save-limit overrides (admin-settable; nil = use the plan
|
||||
// default). Never serialized raw — surfaced only via the derived `limits`
|
||||
// object in MarshalJSON so the app can render/enforce the right caps.
|
||||
BookmarkLimit *int `json:"-"`
|
||||
PublicCollectionLimit *int `json:"-"`
|
||||
PrivateCollectionLimit *int `json:"-"`
|
||||
}
|
||||
|
||||
// MarshalJSON adds a derived `has_password` flag so the frontend can pick
|
||||
@@ -84,10 +91,12 @@ func (u User) MarshalJSON() ([]byte, error) {
|
||||
type alias User
|
||||
return json.Marshal(&struct {
|
||||
alias
|
||||
HasPassword bool `json:"has_password"`
|
||||
HasPassword bool `json:"has_password"`
|
||||
Limits UserLimits `json:"limits"`
|
||||
}{
|
||||
alias: alias(u),
|
||||
HasPassword: u.PasswordHash != nil,
|
||||
Limits: u.EffectiveLimits(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user