feat: enhance limits

This commit is contained in:
Krispire Admin
2026-06-23 14:21:45 +05:30
parent eadef33eac
commit 96ebbfd719
12 changed files with 183 additions and 53 deletions
+7
View File
@@ -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.
+51
View File
@@ -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
View File
@@ -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(),
})
}