package main import ( "errors" "fmt" "strconv" "github.com/go-ldap/ldap/v3" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" ) const ( // --- Default Quota Values --- QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB QUOTA_WEBSITE_OBJECTS = 10000 // 10k objects QUOTA_WEBSITE_COUNT = 5 // 5 buckets // --- Per-user overridable fields --- FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted" FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count" ) type UserQuota struct { WebsiteCount int64 WebsiteSizeDefault int64 WebsiteSizeBursted int64 WebsiteObjects int64 } func NewUserQuota() *UserQuota { return &UserQuota { WebsiteCount: QUOTA_WEBSITE_COUNT, WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT, WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED, WebsiteObjects: QUOTA_WEBSITE_OBJECTS, } } var ( ErrQuotaEmpty = fmt.Errorf("No quota is defined for this entry") ErrQuotaInvalid = fmt.Errorf("The defined quota can't be parsed") ) func entryToQuota(entry *ldap.Entry, field string) (int64, error) { f := entry.GetAttributeValue(field) if f == "" { return -1, ErrQuotaEmpty } q, err := strconv.ParseInt(f, 10, 64) if err != nil { return -1, errors.Join(ErrQuotaInvalid, err) } return q, nil } func NewUserQuotaFromEntry(entry *ldap.Entry) *UserQuota { quotas := NewUserQuota() if q, err := entryToQuota(entry, FIELD_QUOTA_WEBSITE_COUNT); err == nil { quotas.WebsiteCount = q } if q, err := entryToQuota(entry, FIELD_QUOTA_WEBSITE_SIZE_BURSTED); err == nil { quotas.WebsiteSizeBursted = q } return quotas } func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas { qr := garage.NewUpdateBucketRequestQuotas() qr.SetMaxSize(q.WebsiteSizeDefault) qr.SetMaxObjects(q.WebsiteSizeBursted) return qr } func (q *UserQuota) WebsiteSizeAdjust(sz int64) int64 { if sz < q.WebsiteSizeDefault { return q.WebsiteSizeDefault } else if sz > q.WebsiteSizeBursted { return q.WebsiteSizeBursted } else { return sz } } func (q *UserQuota) WebsiteObjectAdjust(objs int64) int64 { if objs > q.WebsiteObjects || objs <= 0 { return q.WebsiteObjects } else { return objs } } func (q *UserQuota) WebsiteSizeBurstedPretty() string { return prettyValue(q.WebsiteSizeBursted) } // --- A quota stat we can use type QuotaStat struct { Current int64 `json:"current"` Max int64 `json:"max"` Ratio float64 `json:"ratio"` Burstable bool `json:"burstable"` } func NewQuotaStat(current, max int64, burstable bool) QuotaStat { return QuotaStat { Current: current, Max: max, Ratio: float64(current) / float64(max), Burstable: burstable, } } func (q *QuotaStat) IsFull() bool { return q.Current >= q.Max } func (q *QuotaStat) Percent() int64 { return int64(q.Ratio * 100) } func (q *QuotaStat) PrettyCurrent() string { return prettyValue(q.Current) } func (q *QuotaStat) PrettyMax() string { return prettyValue(q.Max) } func prettyValue(v int64) string { if v < 1024 { return fmt.Sprintf("%d octets", v) } v = v / 1024 if v < 1024 { return fmt.Sprintf("%d kio", v) } v = v / 1024 if v < 1024 { return fmt.Sprintf("%d Mio", v) } v = v / 1024 if v < 1024 { return fmt.Sprintf("%d Gio", v) } v = v / 1024 return fmt.Sprintf("%d Tio", v) }