212 lines
4.6 KiB
Go
212 lines
4.6 KiB
Go
|
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
|
||
|
}
|