patch and delete with quota are now implemented
This commit is contained in:
parent
bc368943a4
commit
0828737573
5 changed files with 210 additions and 64 deletions
45
garage.go
45
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
|
// --- Start page rendering functions
|
||||||
|
|
||||||
func handleWebsiteConfigure(w http.ResponseWriter, r *http.Request) {
|
func handleWebsiteConfigure(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -193,8 +203,8 @@ func handleWebsiteList(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteNewTpl struct {
|
type WebsiteNewTpl struct {
|
||||||
ctrl *WebsiteController
|
Ctrl *WebsiteController
|
||||||
err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
|
func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -209,10 +219,7 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tpl := &WebsiteNewTpl{
|
tpl := &WebsiteNewTpl{ctrl, nil}
|
||||||
ctrl: ctrl,
|
|
||||||
err: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
tWebsiteNew := getTemplate("garage_website_new.html")
|
tWebsiteNew := getTemplate("garage_website_new.html")
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
|
@ -225,23 +232,27 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
view, err := ctrl.Create(bucket)
|
view, err := ctrl.Create(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tpl.err = err
|
tpl.Err = err
|
||||||
tWebsiteNew.Execute(w, tpl)
|
tWebsiteNew.Execute(w, tpl)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
|
http.Redirect(w, r, "/website/inspect/"+view.Name.Pretty, http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tWebsiteNew.Execute(w, nil)
|
tWebsiteNew.Execute(w, tpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsiteInspectTpl struct {
|
type WebsiteInspectTpl struct {
|
||||||
Ctrl *WebsiteController
|
Ctrl *WebsiteController
|
||||||
View *WebsiteView
|
View *WebsiteView
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
|
func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var processErr error
|
||||||
|
|
||||||
user := RequireUserHtml(w, r)
|
user := RequireUserHtml(w, r)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return
|
return
|
||||||
|
@ -255,13 +266,29 @@ func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
bucketName := mux.Vars(r)["bucket"]
|
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)
|
view, err := ctrl.Inspect(bucketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tpl := &WebsiteInspectTpl{ ctrl, view }
|
tpl := &WebsiteInspectTpl{ ctrl, view, processErr }
|
||||||
|
|
||||||
tWebsiteInspect := getTemplate("garage_website_inspect.html")
|
tWebsiteInspect := getTemplate("garage_website_inspect.html")
|
||||||
tWebsiteInspect.Execute(w, &tpl)
|
tWebsiteInspect.Execute(w, &tpl)
|
||||||
|
|
71
quotas.go
71
quotas.go
|
@ -77,3 +77,74 @@ func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas {
|
||||||
|
|
||||||
return qr
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,15 +2,27 @@
|
||||||
|
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a class="ml-4 btn btn-primary" href="/website/new">Nouveau site web</a>
|
|
||||||
<!--<h4>Inspecter les sites webs</h4>-->
|
<!--<h4>Inspecter les sites webs</h4>-->
|
||||||
<a class="ml-auto btn btn-link" href="/website/configure">Mes identifiants</a>
|
<a class="ml-auto btn btn-link" href="/website/configure">Mes identifiants</a>
|
||||||
<a class="ml-4 btn btn-info" href="/">Menu principal</a>
|
<a class="ml-4 btn btn-info" href="/">Menu principal</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-3" >
|
<div class="row">
|
||||||
<div class="col-md-3">
|
{{ if .Err }}
|
||||||
<div class="list-group">
|
<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 }}
|
{{ $view := .View }}
|
||||||
{{ range $wid := .Ctrl.List }}
|
{{ range $wid := .Ctrl.List }}
|
||||||
{{ if eq $wid.Internal $view.Name.Internal }}
|
{{ if eq $wid.Internal $view.Name.Internal }}
|
||||||
|
@ -24,6 +36,11 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</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>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<h2>{{ .View.Name.Url }}</h2>
|
<h2>{{ .View.Name.Url }}</h2>
|
||||||
|
@ -42,6 +59,15 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</p>
|
</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 }}
|
{{ if .View.Name.Expanded }}
|
||||||
<h5 class="mt-5">Vous ne savez pas comment configurer votre nom de domaine ?</h5>
|
<h5 class="mt-5">Vous ne savez pas comment configurer votre nom de domaine ?</h5>
|
||||||
|
|
|
@ -3,8 +3,16 @@
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<h4>Créer un site web</h4>
|
<h4>Créer un site web</h4>
|
||||||
<a class="ml-auto btn btn-link" href="/garage/key">Mes identifiants</a>
|
<a class="ml-auto btn btn-link" href="/website/configure">Mes identifiants</a>
|
||||||
<a class="ml-4 btn btn-info" href="/garage/website">Mes sites webs</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>
|
</div>
|
||||||
|
|
||||||
<ul class="nav nav-tabs" id="proto" role="tablist">
|
<ul class="nav nav-tabs" id="proto" role="tablist">
|
||||||
|
@ -17,6 +25,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content" id="protocols">
|
<div class="tab-content" id="protocols">
|
||||||
|
|
||||||
<div class="tab-pane fade show active" id="dnsint" role="tabpanel" aria-labelledby="dnsint-tab">
|
<div class="tab-pane fade show active" id="dnsint" role="tabpanel" aria-labelledby="dnsint-tab">
|
||||||
<form method="POST" class="mt-4">
|
<form method="POST" class="mt-4">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
|
|
111
website.go
111
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")
|
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")
|
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.)")
|
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 {
|
type WebsiteId struct {
|
||||||
Pretty string
|
Pretty string
|
||||||
|
@ -146,8 +102,36 @@ func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) {
|
||||||
return NewWebsiteView(binfo), nil
|
return NewWebsiteView(binfo), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebsiteController) Patch(patch *WebsitePatch) (*WebsiteView, error) {
|
func (w *WebsiteController) Patch(pretty string, patch *WebsitePatch) (*WebsiteView, error) {
|
||||||
return nil, nil
|
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) {
|
func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) {
|
||||||
|
@ -190,6 +174,35 @@ func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) {
|
||||||
return NewWebsiteView(binfo), nil
|
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 {
|
type WebsiteView struct {
|
||||||
Name *WebsiteId
|
Name *WebsiteId
|
||||||
|
@ -207,5 +220,5 @@ func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebsitePatch struct {
|
type WebsitePatch struct {
|
||||||
size int64
|
size *int64
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue