package main import ( "errors" "fmt" "strconv" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" "github.com/go-ldap/ldap/v3" ) // Note: PIM = Personal Information Manager const ( // --- Default Quota Values Websites --- QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB QUOTA_WEBSITE_OBJECTS = 10_000 // 10k objects QUOTA_WEBSITE_COUNT = 5 // 5 buckets // --- Default Quota Values PIM --- QUOTA_PIM_SIZE_DEFAULT = 1024 * 1024 * 100 // 100MB QUOTA_PIM_SIZE_BURSTED = 1024 * 1024 * 500 // 500MB QUOTA_PIM_OBJECTS = 100_000 // 100k objects // --- Per-user overridable fields --- FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted" FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count" FIELD_QUOTA_PIM_SIZE_BURSTED = "quota_pim_size_bursted" ) type UserQuota struct { WebsiteCount int64 WebsiteSizeDefault int64 WebsiteSizeBursted int64 WebsiteObjects int64 PimSizeDefault int64 PimSizeBursted int64 PimObjects int64 } func NewUserQuota() *UserQuota { return &UserQuota{ WebsiteCount: QUOTA_WEBSITE_COUNT, WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT, WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED, WebsiteObjects: QUOTA_WEBSITE_OBJECTS, PimSizeDefault: QUOTA_PIM_SIZE_DEFAULT, PimSizeBursted: QUOTA_PIM_SIZE_BURSTED, PimObjects: QUOTA_PIM_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 } if q, err := entryToQuota(entry, FIELD_QUOTA_PIM_SIZE_BURSTED); err == nil { quotas.PimSizeBursted = 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) DefaultPimQuota() *garage.UpdateBucketRequestQuotas { qr := garage.NewUpdateBucketRequestQuotas() qr.SetMaxSize(q.PimSizeDefault) qr.SetMaxObjects(q.PimObjects) return qr } // Website getters/setters 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) } // PIM getters/setters func (q *UserQuota) PimSizeAdjust(sz int64) int64 { if sz < q.PimSizeDefault { return q.PimSizeDefault } else if sz > q.PimSizeBursted { return q.PimSizeBursted } else { return sz } } func (q *UserQuota) PimObjectAdjust(objs int64) int64 { if objs > q.PimObjects || objs <= 0 { return q.PimObjects } else { return objs } } func (q *UserQuota) PimSizeBurstedPretty() string { return prettyValue(q.PimSizeBursted) } // --- 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) }