package main import ( "fmt" "sort" "strings" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" ) var ( ErrWebsiteNotFound = fmt.Errorf("Website not found") ErrFetchBucketInfo = fmt.Errorf("Failed to fetch bucket information") ErrWebsiteQuotaReached = fmt.Errorf("Can't create additional websites, quota reached") ErrEmptyBucketName = fmt.Errorf("You can't create a website with an empty name") ErrCantCreateBucket = fmt.Errorf("Can't create this bucket. Maybe another bucket already exists with this name or you have an invalid character") ErrCantAllowKey = fmt.Errorf("Can't allow given key on the target bucket") ErrCantConfigureBucket = fmt.Errorf("Unable to configure the bucket (activating website, adding quotas, etc.)") ) type QuotaStat struct { Current int64 Max int64 Ratio float64 Burstable bool } 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) } type WebsiteId struct { Pretty string Internal string Alt []string Expanded bool Url string } func NewWebsiteId(id string, aliases []string) *WebsiteId { pretty := id var alt []string if len(aliases) > 0 { pretty = aliases[0] alt = aliases[1:] } expanded := strings.Contains(pretty, ".") url := pretty if !expanded { url = fmt.Sprintf("%s.web.deuxfleurs.fr", pretty) } return &WebsiteId { pretty, id, alt, expanded, url } } func NewWebsiteIdFromBucketInfo(binfo *garage.BucketInfo) *WebsiteId { return NewWebsiteId(*binfo.Id, binfo.GlobalAliases) } type WebsiteController struct { User *LoggedUser WebsiteIdx map[string]*WebsiteId PrettyList []string WebsiteCount QuotaStat } func NewWebsiteController(user *LoggedUser) (*WebsiteController, error) { idx := map[string]*WebsiteId{} var wlist []string keyInfo, err := user.S3KeyInfo() if err != nil { return nil, err } for _, bckt := range(keyInfo.Buckets) { if len(bckt.GlobalAliases) > 0 { wid := NewWebsiteId(*bckt.Id, bckt.GlobalAliases) idx[wid.Pretty] = wid wlist = append(wlist, wid.Pretty) } } sort.Strings(wlist) maxW := user.Quota.WebsiteCount quota := NewQuotaStat(int64(len(wlist)), maxW, true) return &WebsiteController { user, idx, wlist, quota }, nil } func (w *WebsiteController) List() []*WebsiteId { r := make([]*WebsiteId, 0, len(w.PrettyList)) for _, k := range w.PrettyList { r = append(r, w.WebsiteIdx[k]) } return r } func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) { website, ok := w.WebsiteIdx[pretty] if !ok { return nil, ErrWebsiteNotFound } binfo, err := grgGetBucket(website.Internal) if err != nil { return nil, ErrFetchBucketInfo } return NewWebsiteView(binfo), nil } func (w *WebsiteController) Patch(patch *WebsitePatch) (*WebsiteView, error) { return nil, nil } func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) { if pretty == "" { return nil, ErrEmptyBucketName } if w.WebsiteCount.IsFull() { return nil, ErrWebsiteQuotaReached } binfo, err := grgCreateBucket(pretty) if err != nil { return nil, ErrCantCreateBucket } s3key, err := w.User.S3KeyInfo() if err != nil { return nil, err } binfo, err = grgAllowKeyOnBucket(*binfo.Id, *s3key.AccessKeyId) if err != nil { return nil, ErrCantAllowKey } qr := w.User.Quota.DefaultWebsiteQuota() wr := allowWebsiteDefault() ur := garage.NewUpdateBucketRequest() ur.SetWebsiteAccess(*wr) ur.SetQuotas(*qr) binfo, err = grgUpdateBucket(*binfo.Id, ur) if err != nil { return nil, ErrCantConfigureBucket } return NewWebsiteView(binfo), nil } type WebsiteView struct { Name *WebsiteId Size QuotaStat Files QuotaStat } func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView { q := binfo.GetQuotas() wid := NewWebsiteIdFromBucketInfo(binfo) size := NewQuotaStat(*binfo.Bytes, (&q).GetMaxSize(), true) objects := NewQuotaStat(*binfo.Objects, (&q).GetMaxObjects(), false) return &WebsiteView { wid, size, objects } } type WebsitePatch struct { size int64 }