2023-09-25 17:07:07 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-09-26 06:40:41 +00:00
|
|
|
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
|
2024-06-24 08:22:17 +00:00
|
|
|
"log"
|
2023-09-25 17:07:07 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2023-09-26 06:40:41 +00:00
|
|
|
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")
|
2023-09-25 20:00:46 +00:00
|
|
|
ErrBucketDeleteUnfinishedUpload = fmt.Errorf("You must remove all the unfinished multipart uploads before deleting a bucket")
|
2023-10-17 16:33:11 +00:00
|
|
|
ErrCantChangeVhost = fmt.Errorf("Can't change the vhost to the desired value. Maybe it's already used by someone else or an internal error occured")
|
2024-06-24 07:17:09 +00:00
|
|
|
ErrCantRemoveOldVhost = fmt.Errorf("The new vhost is bound to the bucket but the old one can't be removed, it's an internal error")
|
|
|
|
ErrFetchDedicatedKey = fmt.Errorf("Bucket has no dedicated key while it's required, it's an internal error")
|
2024-06-24 08:22:17 +00:00
|
|
|
ErrDedicatedKeyInvariant = fmt.Errorf("A security invariant on the dedicated key has been violated, aborting.")
|
2023-09-25 17:07:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type WebsiteId struct {
|
2023-09-26 06:40:41 +00:00
|
|
|
Pretty string `json:"name"`
|
|
|
|
Internal string `json:"-"`
|
|
|
|
Alt []string `json:"alt_name"`
|
|
|
|
Expanded bool `json:"expanded"`
|
|
|
|
Url string `json:"domain"`
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
2023-09-26 06:40:41 +00:00
|
|
|
|
2023-09-25 17:07:07 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-09-26 06:40:41 +00:00
|
|
|
return &WebsiteId{pretty, id, alt, expanded, url}
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
func NewWebsiteIdFromBucketInfo(binfo *garage.BucketInfo) *WebsiteId {
|
|
|
|
return NewWebsiteId(*binfo.Id, binfo.GlobalAliases)
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
// -----
|
|
|
|
|
|
|
|
type WebsiteDescribe struct {
|
2024-06-24 10:07:28 +00:00
|
|
|
Username string `json:"username"`
|
2024-06-24 08:22:17 +00:00
|
|
|
AllowedWebsites *QuotaStat `json:"quota_website_count"`
|
|
|
|
BurstBucketQuotaSize string `json:"burst_bucket_quota_size"`
|
|
|
|
Websites []*WebsiteId `json:"vhosts"`
|
|
|
|
}
|
|
|
|
|
2023-09-25 17:07:07 +00:00
|
|
|
type WebsiteController struct {
|
2023-09-26 06:40:41 +00:00
|
|
|
User *LoggedUser
|
2024-06-24 08:22:17 +00:00
|
|
|
RootKey *garage.KeyInfo
|
2023-09-26 06:40:41 +00:00
|
|
|
WebsiteIdx map[string]*WebsiteId
|
|
|
|
PrettyList []string
|
2023-09-25 17:07:07 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-09-26 06:40:41 +00:00
|
|
|
for _, bckt := range keyInfo.Buckets {
|
2023-09-25 17:07:07 +00:00
|
|
|
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)
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
return &WebsiteController{user, keyInfo, idx, wlist, quota}, nil
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
func (w *WebsiteController) getDedicatedWebsiteKey(binfo *garage.BucketInfo) (*garage.KeyInfo, error) {
|
|
|
|
// Check bucket info is not null
|
|
|
|
if binfo == nil {
|
|
|
|
return nil, ErrFetchBucketInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the bucket is owned by the user's root key
|
|
|
|
usersRootKeyFound := false
|
|
|
|
for _, bucketKeyInfo := range binfo.Keys {
|
|
|
|
if *bucketKeyInfo.AccessKeyId == *w.RootKey.AccessKeyId && *bucketKeyInfo.Permissions.Owner {
|
|
|
|
usersRootKeyFound = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !usersRootKeyFound {
|
|
|
|
log.Printf("%s is not an owner of bucket %s. Invariant violated.\n", w.User.Username, *binfo.Id)
|
|
|
|
return nil, ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that username does not contain a ":" (should not be possible due to the invitation regex)
|
|
|
|
// We do this check as ":" is used as a separator
|
|
|
|
if strings.Contains(w.User.Username, ":") || w.User.Username == "" || *binfo.Id == "" {
|
|
|
|
log.Printf("Username (%s) or bucket identifier (%s) is invalid. Invariant violated.\n", w.User.Username, *binfo.Id)
|
|
|
|
return nil, ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the string template by concatening the username and the bucket identifier
|
|
|
|
dedicatedKeyName := fmt.Sprintf("%s:web:%s", w.User.Username, *binfo.Id)
|
|
|
|
|
|
|
|
// Try to fetch the dedicated key
|
|
|
|
keyInfo, err := grgSearchKey(dedicatedKeyName)
|
|
|
|
if err != nil {
|
|
|
|
// On error, try to create it.
|
|
|
|
// @FIXME we should try to create only on 404 Not Found errors
|
|
|
|
keyInfo, err = grgCreateKey(dedicatedKeyName)
|
|
|
|
if err != nil {
|
|
|
|
// On error again, abort
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
log.Printf("Created dedicated key %s\n", dedicatedKeyName)
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:43:11 +00:00
|
|
|
// Check that the key name is *exactly* the one we requested
|
|
|
|
if *keyInfo.Name != dedicatedKeyName {
|
|
|
|
log.Printf("Expected key: %s, got %s. Invariant violated.\n", dedicatedKeyName, *keyInfo.Name)
|
|
|
|
return nil, ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
// Check that the dedicated key does not contain any other bucket than this one
|
2024-06-24 08:43:11 +00:00
|
|
|
// and report if this bucket key is found with correct permissions
|
2024-06-24 08:22:17 +00:00
|
|
|
permissionsOk := false
|
|
|
|
for _, buck := range keyInfo.Buckets {
|
|
|
|
if *buck.Id != *binfo.Id {
|
|
|
|
log.Printf("Key %s is used on bucket %s while it should be exclusive to %s. Invariant violated.\n", dedicatedKeyName, *buck.Id, *binfo.Id)
|
|
|
|
return nil, ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
if *buck.Id == *binfo.Id && *buck.Permissions.Read && *buck.Permissions.Write {
|
|
|
|
permissionsOk = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow this bucket on the key if it's not already the case
|
|
|
|
// (will be executed when 1) key is first created and 2) as an healing mechanism)
|
|
|
|
if !permissionsOk {
|
|
|
|
binfo, err = grgAllowKeyOnBucket(*binfo.Id, *keyInfo.AccessKeyId, true, true, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
log.Printf("Key %s was not properly allowed on bucket %s, fixing permissions. Intended behavior.", dedicatedKeyName, *binfo.Id)
|
|
|
|
|
|
|
|
// Refresh the key to have an object with proper permissions
|
|
|
|
keyInfo, err = grgGetKey(*keyInfo.AccessKeyId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the key
|
|
|
|
return keyInfo, nil
|
2023-09-25 21:00:57 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 08:43:11 +00:00
|
|
|
func (w *WebsiteController) flushDedicatedWebsiteKey(binfo *garage.BucketInfo) error {
|
|
|
|
// Check bucket info is not null
|
|
|
|
if binfo == nil {
|
|
|
|
return ErrFetchBucketInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the bucket is owned by the user's root key
|
|
|
|
usersRootKeyFound := false
|
|
|
|
for _, bucketKeyInfo := range binfo.Keys {
|
|
|
|
if *bucketKeyInfo.AccessKeyId == *w.RootKey.AccessKeyId && *bucketKeyInfo.Permissions.Owner {
|
|
|
|
usersRootKeyFound = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !usersRootKeyFound {
|
|
|
|
log.Printf("%s is not an owner of bucket %s. Invariant violated.\n", w.User.Username, *binfo.Id)
|
|
|
|
return ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the string template by concatening the username and the bucket identifier
|
|
|
|
dedicatedKeyName := fmt.Sprintf("%s:web:%s", w.User.Username, *binfo.Id)
|
|
|
|
|
|
|
|
// Fetch the dedicated key
|
|
|
|
keyInfo, err := grgSearchKey(dedicatedKeyName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the key name is *exactly* the one we requested
|
|
|
|
if *keyInfo.Name != dedicatedKeyName {
|
|
|
|
log.Printf("Expected key: %s, got %s. Invariant violated.\n", dedicatedKeyName, *keyInfo.Name)
|
|
|
|
return ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the dedicated key contains no other bucket than this one
|
|
|
|
// (can also be empty, useful to heal a partially created key)
|
|
|
|
for _, buck := range keyInfo.Buckets {
|
|
|
|
if *buck.Id != *binfo.Id {
|
|
|
|
log.Printf("Key %s is used on bucket %s while it should be exclusive to %s. Invariant violated.\n", dedicatedKeyName, *buck.Id, *binfo.Id)
|
|
|
|
return ErrDedicatedKeyInvariant
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally delete this key
|
|
|
|
err = grgDelKey(*keyInfo.AccessKeyId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Printf("Deleted dedicated key %s", dedicatedKeyName)
|
|
|
|
return nil
|
|
|
|
}
|
2024-06-24 08:22:17 +00:00
|
|
|
|
2023-09-26 06:40:30 +00:00
|
|
|
func (w *WebsiteController) Describe() (*WebsiteDescribe, error) {
|
2023-09-25 17:07:07 +00:00
|
|
|
r := make([]*WebsiteId, 0, len(w.PrettyList))
|
|
|
|
for _, k := range w.PrettyList {
|
2023-09-26 06:40:41 +00:00
|
|
|
r = append(r, w.WebsiteIdx[k])
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
2024-06-24 07:17:09 +00:00
|
|
|
|
2023-09-26 06:40:41 +00:00
|
|
|
return &WebsiteDescribe{
|
2024-06-24 10:07:28 +00:00
|
|
|
w.User.Username,
|
2023-09-26 06:40:41 +00:00
|
|
|
&w.WebsiteCount,
|
2023-09-26 06:40:30 +00:00
|
|
|
w.User.Quota.WebsiteSizeBurstedPretty(),
|
2024-06-24 07:17:09 +00:00
|
|
|
r,
|
|
|
|
}, nil
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
dedicatedKey, err := w.getDedicatedWebsiteKey(binfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-06-24 07:17:09 +00:00
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
return NewWebsiteView(binfo, dedicatedKey)
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 20:00:46 +00:00
|
|
|
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()))
|
2023-09-25 21:00:57 +00:00
|
|
|
if patch.Size != nil {
|
|
|
|
urQuota.SetMaxSize(w.User.Quota.WebsiteSizeAdjust(*patch.Size))
|
2023-09-25 20:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build the update
|
|
|
|
ur := garage.NewUpdateBucketRequest()
|
|
|
|
ur.SetQuotas(*urQuota)
|
|
|
|
|
2023-10-17 16:33:11 +00:00
|
|
|
// Call garage "update bucket" function
|
2023-09-25 20:00:46 +00:00
|
|
|
binfo, err = grgUpdateBucket(website.Internal, ur)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrCantConfigureBucket
|
|
|
|
}
|
|
|
|
|
2023-10-17 16:33:11 +00:00
|
|
|
// Update the alias if the vhost field is set and different
|
|
|
|
if patch.Vhost != nil && *patch.Vhost != "" && *patch.Vhost != pretty {
|
|
|
|
binfo, err = grgAddGlobalAlias(website.Internal, *patch.Vhost)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrCantChangeVhost
|
|
|
|
}
|
|
|
|
binfo, err = grgDelGlobalAlias(website.Internal, pretty)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrCantRemoveOldVhost
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
if patch.RotateKey != nil && *patch.RotateKey {
|
2024-06-24 08:43:11 +00:00
|
|
|
err = w.flushDedicatedWebsiteKey(binfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-06-24 08:22:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dedicatedKey, err := w.getDedicatedWebsiteKey(binfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2024-06-24 07:17:09 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
return NewWebsiteView(binfo, dedicatedKey)
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) {
|
|
|
|
if pretty == "" {
|
|
|
|
return nil, ErrEmptyBucketName
|
|
|
|
}
|
|
|
|
|
|
|
|
if w.WebsiteCount.IsFull() {
|
|
|
|
return nil, ErrWebsiteQuotaReached
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Create bucket
|
2023-09-25 17:07:07 +00:00
|
|
|
binfo, err := grgCreateBucket(pretty)
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrCantCreateBucket
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Allow user's global key on bucket
|
2023-09-25 17:07:07 +00:00
|
|
|
s3key, err := w.User.S3KeyInfo()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
binfo, err = grgAllowKeyOnBucket(*binfo.Id, *s3key.AccessKeyId, true, true, true)
|
2023-09-25 17:07:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, ErrCantAllowKey
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Set quota
|
2023-09-25 17:07:07 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Create a dedicated key
|
2024-06-24 08:22:17 +00:00
|
|
|
dedicatedKey, err := w.getDedicatedWebsiteKey(binfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-06-24 07:17:09 +00:00
|
|
|
|
2024-06-24 08:22:17 +00:00
|
|
|
return NewWebsiteView(binfo, dedicatedKey)
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 20:00:46 +00:00
|
|
|
func (w *WebsiteController) Delete(pretty string) error {
|
|
|
|
if pretty == "" {
|
|
|
|
return ErrEmptyBucketName
|
|
|
|
}
|
|
|
|
|
|
|
|
website, ok := w.WebsiteIdx[pretty]
|
|
|
|
if !ok {
|
|
|
|
return ErrWebsiteNotFound
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Error checking
|
2023-09-25 20:00:46 +00:00
|
|
|
binfo, err := grgGetBucket(website.Internal)
|
|
|
|
if err != nil {
|
|
|
|
return ErrFetchBucketInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
if *binfo.Objects > int64(0) {
|
|
|
|
return ErrBucketDeleteNotEmpty
|
2023-09-26 06:40:41 +00:00
|
|
|
}
|
2023-09-25 20:00:46 +00:00
|
|
|
|
|
|
|
if *binfo.UnfinishedUploads > int32(0) {
|
|
|
|
return ErrBucketDeleteUnfinishedUpload
|
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
// Delete dedicated key
|
2024-06-24 08:43:11 +00:00
|
|
|
err = w.flushDedicatedWebsiteKey(binfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-06-24 07:17:09 +00:00
|
|
|
|
|
|
|
// Actually delete bucket
|
2023-09-25 20:00:46 +00:00
|
|
|
err = grgDeleteBucket(website.Internal)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-25 17:07:07 +00:00
|
|
|
type WebsiteView struct {
|
2024-06-24 06:44:22 +00:00
|
|
|
Name *WebsiteId `json:"vhost"`
|
|
|
|
AccessKeyId string `json:"access_key_id"`
|
|
|
|
SecretAccessKey string `json:"secret_access_key"`
|
|
|
|
Size QuotaStat `json:"quota_size"`
|
|
|
|
Files QuotaStat `json:"quota_files"`
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 07:17:09 +00:00
|
|
|
func NewWebsiteView(binfo *garage.BucketInfo, s3key *garage.KeyInfo) (*WebsiteView, error) {
|
|
|
|
if binfo == nil {
|
2024-06-24 08:22:17 +00:00
|
|
|
return nil, ErrFetchBucketInfo
|
2024-06-24 07:17:09 +00:00
|
|
|
}
|
|
|
|
if s3key == nil {
|
|
|
|
return nil, ErrFetchDedicatedKey
|
|
|
|
}
|
|
|
|
|
2023-09-25 17:07:07 +00:00
|
|
|
q := binfo.GetQuotas()
|
|
|
|
|
|
|
|
wid := NewWebsiteIdFromBucketInfo(binfo)
|
|
|
|
size := NewQuotaStat(*binfo.Bytes, (&q).GetMaxSize(), true)
|
|
|
|
objects := NewQuotaStat(*binfo.Objects, (&q).GetMaxObjects(), false)
|
2024-06-24 06:44:22 +00:00
|
|
|
return &WebsiteView{
|
2024-06-24 08:22:17 +00:00
|
|
|
wid,
|
2024-06-24 07:17:09 +00:00
|
|
|
*s3key.AccessKeyId,
|
|
|
|
*s3key.SecretAccessKey.Get(),
|
2024-06-24 08:22:17 +00:00
|
|
|
size,
|
2024-06-24 06:44:22 +00:00
|
|
|
objects,
|
2024-06-24 07:17:09 +00:00
|
|
|
}, nil
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type WebsitePatch struct {
|
2024-06-24 08:22:17 +00:00
|
|
|
Size *int64 `json:"quota_size"`
|
|
|
|
Vhost *string `json:"vhost"`
|
|
|
|
RotateKey *bool `json:"rotate_key"`
|
2023-09-25 17:07:07 +00:00
|
|
|
}
|