aboutsummaryrefslogtreecommitdiff
path: root/website.go
diff options
context:
space:
mode:
authorQuentin <quentin@dufour.io>2023-09-26 06:44:36 +0000
committerQuentin <quentin@dufour.io>2023-09-26 06:44:36 +0000
commit49d8e81fbea0d4703a33e87a807927169a8060ac (patch)
treed0b655454d5e13ed2238060fee27fc0d951d64c8 /website.go
parent1e75c21b65021da0c3c5a8be9be12114a2327464 (diff)
parent706ff58a6f6608719feda15075d50f978df39c5b (diff)
downloadguichet-49d8e81fbea0d4703a33e87a807927169a8060ac.tar.gz
guichet-49d8e81fbea0d4703a33e87a807927169a8060ac.zip
Merge pull request 'An API for Guichet' (#23) from api into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/guichet/pulls/23
Diffstat (limited to 'website.go')
-rw-r--r--website.go236
1 files changed, 236 insertions, 0 deletions
diff --git a/website.go b/website.go
new file mode 100644
index 0000000..ba432c5
--- /dev/null
+++ b/website.go
@@ -0,0 +1,236 @@
+package main
+
+import (
+ "fmt"
+ garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
+ "sort"
+ "strings"
+)
+
+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.)")
+ ErrBucketDeleteNotEmpty = fmt.Errorf("You must remove all the files before deleting a bucket")
+ ErrBucketDeleteUnfinishedUpload = fmt.Errorf("You must remove all the unfinished multipart uploads before deleting a bucket")
+)
+
+type WebsiteId struct {
+ Pretty string `json:"name"`
+ Internal string `json:"-"`
+ Alt []string `json:"alt_name"`
+ Expanded bool `json:"expanded"`
+ Url string `json:"domain"`
+}
+
+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
+}
+
+type WebsiteDescribe struct {
+ AccessKeyId string `json:"access_key_id"`
+ SecretAccessKey string `json:"secret_access_key"`
+ AllowedWebsites *QuotaStat `json:"quota_website_count"`
+ BurstBucketQuotaSize string `json:"burst_bucket_quota_size"`
+ Websites []*WebsiteId `json:"vhosts"`
+}
+
+func (w *WebsiteController) Describe() (*WebsiteDescribe, error) {
+ s3key, err := w.User.S3KeyInfo()
+ if err != nil {
+ return nil, err
+ }
+
+ r := make([]*WebsiteId, 0, len(w.PrettyList))
+ for _, k := range w.PrettyList {
+ r = append(r, w.WebsiteIdx[k])
+ }
+ return &WebsiteDescribe{
+ *s3key.AccessKeyId,
+ *s3key.SecretAccessKey,
+ &w.WebsiteCount,
+ w.User.Quota.WebsiteSizeBurstedPretty(),
+ r}, nil
+}
+
+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(pretty string, patch *WebsitePatch) (*WebsiteView, error) {
+ website, ok := w.WebsiteIdx[pretty]
+ if !ok {
+ return nil, ErrWebsiteNotFound
+ }
+
+ binfo, err := grgGetBucket(website.Internal)
+ if err != nil {
+ return nil, ErrFetchBucketInfo
+ }
+
+ // Patch the max size
+ urQuota := garage.NewUpdateBucketRequestQuotas()
+ urQuota.SetMaxSize(w.User.Quota.WebsiteSizeAdjust(binfo.Quotas.GetMaxSize()))
+ urQuota.SetMaxObjects(w.User.Quota.WebsiteObjectAdjust(binfo.Quotas.GetMaxObjects()))
+ if patch.Size != nil {
+ urQuota.SetMaxSize(w.User.Quota.WebsiteSizeAdjust(*patch.Size))
+ }
+
+ // Build the update
+ ur := garage.NewUpdateBucketRequest()
+ ur.SetQuotas(*urQuota)
+
+ // Call garage
+ binfo, err = grgUpdateBucket(website.Internal, ur)
+ if err != nil {
+ return nil, ErrCantConfigureBucket
+ }
+
+ return NewWebsiteView(binfo), 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
+}
+
+func (w *WebsiteController) Delete(pretty string) error {
+ if pretty == "" {
+ return ErrEmptyBucketName
+ }
+
+ website, ok := w.WebsiteIdx[pretty]
+ if !ok {
+ return ErrWebsiteNotFound
+ }
+
+ binfo, err := grgGetBucket(website.Internal)
+ if err != nil {
+ return ErrFetchBucketInfo
+ }
+
+ if *binfo.Objects > int64(0) {
+ return ErrBucketDeleteNotEmpty
+ }
+
+ if *binfo.UnfinishedUploads > int32(0) {
+ return ErrBucketDeleteUnfinishedUpload
+ }
+
+ err = grgDeleteBucket(website.Internal)
+ return err
+}
+
+type WebsiteView struct {
+ Name *WebsiteId `json:"identified_as"`
+ Size QuotaStat `json:"quota_size"`
+ Files QuotaStat `json:"quota_files"`
+}
+
+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 `json:"quota_size"`
+}