diff options
-rw-r--r-- | garage.go | 45 | ||||
-rw-r--r-- | quotas.go | 71 | ||||
-rw-r--r-- | templates/garage_website_inspect.html | 34 | ||||
-rw-r--r-- | templates/garage_website_new.html | 13 | ||||
-rw-r--r-- | website.go | 111 |
5 files changed, 210 insertions, 64 deletions
@@ -160,6 +160,16 @@ func grgGetBucket(bid string) (*garage.BucketInfo, error) { } +func grgDeleteBucket(bid string) error { + client, ctx := gadmin() + + _, err := client.BucketApi.DeleteBucket(ctx, bid).Execute() + if err != nil { + log.Println(err) + } + return err +} + // --- Start page rendering functions func handleWebsiteConfigure(w http.ResponseWriter, r *http.Request) { @@ -193,8 +203,8 @@ func handleWebsiteList(w http.ResponseWriter, r *http.Request) { } type WebsiteNewTpl struct { - ctrl *WebsiteController - err error + Ctrl *WebsiteController + Err error } func handleWebsiteNew(w http.ResponseWriter, r *http.Request) { @@ -209,10 +219,7 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) { return } - tpl := &WebsiteNewTpl{ - ctrl: ctrl, - err: nil, - } + tpl := &WebsiteNewTpl{ctrl, nil} tWebsiteNew := getTemplate("garage_website_new.html") if r.Method == "POST" { @@ -225,23 +232,27 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) { view, err := ctrl.Create(bucket) if err != nil { - tpl.err = err + tpl.Err = err tWebsiteNew.Execute(w, tpl) + return } http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound) return } - tWebsiteNew.Execute(w, nil) + tWebsiteNew.Execute(w, tpl) } type WebsiteInspectTpl struct { Ctrl *WebsiteController View *WebsiteView + Err error } func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) { + var processErr error + user := RequireUserHtml(w, r) if user == nil { return @@ -254,6 +265,22 @@ func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) { } bucketName := mux.Vars(r)["bucket"] + + if r.Method == "POST" { + r.ParseForm() + action := strings.Join(r.Form["action"],"") + switch action { + case "increase_quota": + _, processErr = ctrl.Patch(bucketName, &WebsitePatch { size: &user.Quota.WebsiteSizeBursted }) + case "delete_bucket": + processErr = ctrl.Delete(bucketName) + http.Redirect(w, r, "/website", http.StatusFound) + return + default: + processErr = fmt.Errorf("Unknown action") + } + + } view, err := ctrl.Inspect(bucketName) if err != nil { @@ -261,7 +288,7 @@ func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) { return } - tpl := &WebsiteInspectTpl{ ctrl, view } + tpl := &WebsiteInspectTpl{ ctrl, view, processErr } tWebsiteInspect := getTemplate("garage_website_inspect.html") tWebsiteInspect.Execute(w, &tpl) @@ -77,3 +77,74 @@ func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas { return qr } + +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) +} + +// --- A quota stat we can use +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) +} diff --git a/templates/garage_website_inspect.html b/templates/garage_website_inspect.html index d5f48c2..c062ab9 100644 --- a/templates/garage_website_inspect.html +++ b/templates/garage_website_inspect.html @@ -2,15 +2,27 @@ {{define "body"}} <div class="d-flex"> - <a class="ml-4 btn btn-primary" href="/website/new">Nouveau site web</a> <!--<h4>Inspecter les sites webs</h4>--> <a class="ml-auto btn btn-link" href="/website/configure">Mes identifiants</a> <a class="ml-4 btn btn-info" href="/">Menu principal</a> </div> -<div class="row mt-3" > - <div class="col-md-3"> - <div class="list-group"> +<div class="row"> + {{ if .Err }} + <div class="col-md-12 mt-3"> + <div class="alert alert-danger">{{ .Err.Error }}</div> + </div> + {{ end }} + + <div class="col-md-3 mt-3"> + <a class="btn btn-primary btn-block" href="/website/new"> + <svg id="i-plus" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="18" height="18" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"> + <path d="M16 2 L16 30 M2 16 L30 16" /> + </svg> + <span class="ml-1">Nouveau site web</span> + </a> + + <div class="list-group mt-3"> {{ $view := .View }} {{ range $wid := .Ctrl.List }} {{ if eq $wid.Internal $view.Name.Internal }} @@ -24,6 +36,11 @@ {{ end }} {{ end }} </div> + + <p class="text-center mt-2"> + {{ .Ctrl.WebsiteCount.Current }} sites créés sur {{ .Ctrl.WebsiteCount.Max }}<br/> + Jusqu'à {{ .Ctrl.User.Quota.WebsiteSizeBurstedPretty }} par site web + </p> </div> <div class="col-md-9"> <h2>{{ .View.Name.Url }}</h2> @@ -42,6 +59,15 @@ {{ end }} </p> + <h5 class="mt-3">Actions</h5> + <form action="" method="post"> + <div class="btn-group" role="group" aria-label="Actions sur le site web"> + <button class="btn btn-secondary" name="action" value="increase_quota">Augmenter le quota</button> + <a class="btn btn-secondary disabled">Changer le nom de domaine</a> + <button class="btn btn-danger" name="action" value="delete_bucket">Supprimer</button> + </div> + </form> + {{ if .View.Name.Expanded }} <h5 class="mt-5">Vous ne savez pas comment configurer votre nom de domaine ?</h5> diff --git a/templates/garage_website_new.html b/templates/garage_website_new.html index f1cd847..7ee4936 100644 --- a/templates/garage_website_new.html +++ b/templates/garage_website_new.html @@ -3,8 +3,16 @@ {{define "body"}} <div class="d-flex"> <h4>Créer un site web</h4> - <a class="ml-auto btn btn-link" href="/garage/key">Mes identifiants</a> - <a class="ml-4 btn btn-info" href="/garage/website">Mes sites webs</a> + <a class="ml-auto btn btn-link" href="/website/configure">Mes identifiants</a> + <a class="ml-4 btn btn-info" href="/website">Mes sites webs</a> +</div> + +<div class="row mt-3"> + <div class="col-md-12"> + {{if .Err}} + <div class="alert alert-danger">{{ .Err.Error }}</div> + {{end}} + </div> </div> <ul class="nav nav-tabs" id="proto" role="tablist"> @@ -17,6 +25,7 @@ </ul> <div class="tab-content" id="protocols"> + <div class="tab-pane fade show active" id="dnsint" role="tabpanel" aria-labelledby="dnsint-tab"> <form method="POST" class="mt-4"> <div class="form-row"> @@ -15,55 +15,11 @@ var ( 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 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 @@ -146,8 +102,36 @@ func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) { return NewWebsiteView(binfo), nil } -func (w *WebsiteController) Patch(patch *WebsitePatch) (*WebsiteView, error) { - return nil, 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) { @@ -190,6 +174,35 @@ func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) { 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 @@ -207,5 +220,5 @@ func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView { } type WebsitePatch struct { - size int64 + size *int64 } |