aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--garage.go45
-rw-r--r--quotas.go71
-rw-r--r--templates/garage_website_inspect.html34
-rw-r--r--templates/garage_website_new.html13
-rw-r--r--website.go111
5 files changed, 210 insertions, 64 deletions
diff --git a/garage.go b/garage.go
index 8c8633f..d3d4d38 100644
--- a/garage.go
+++ b/garage.go
@@ -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)
diff --git a/quotas.go b/quotas.go
index 3eec9b8..9a2e426 100644
--- a/quotas.go
+++ b/quotas.go
@@ -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">
diff --git a/website.go b/website.go
index c06ccbc..6e86b19 100644
--- a/website.go
+++ b/website.go
@@ -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
}