aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api.go212
-rw-r--r--garage.go8
-rw-r--r--main.go3
-rw-r--r--quotas.go8
-rw-r--r--templates/home.html4
-rw-r--r--website.go33
6 files changed, 108 insertions, 160 deletions
diff --git a/api.go b/api.go
index fd6df93..c804276 100644
--- a/api.go
+++ b/api.go
@@ -1,48 +1,29 @@
package main
import (
- //"context"
"errors"
"encoding/json"
- garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
+ "fmt"
"github.com/gorilla/mux"
- "log"
"net/http"
)
-type ApiQuotaView struct {
- files *uint64
- size *uint64
-}
-
-type ApiBucketView struct {
- global *bool
- max *ApiQuotaView
- used *ApiQuotaView
-}
-
-type BucketRequest struct {
- s3key *garage.KeyInfo
- bucketName string
- bucketId string
- global bool
- http *http.Request
-}
+func handleAPIWebsiteList(w http.ResponseWriter, r *http.Request) {
+ user := RequireUserApi(w, r)
-func handleAPIWebsite(w http.ResponseWriter, r *http.Request) {
-
- br, err := buildBucketRequest(w, r)
- if err != nil {
- return
+ if user == nil {
+ return
}
- if r.Method == http.MethodPatch {
- patchGarageBucket(w, br)
+ ctrl, err := NewWebsiteController(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if r.Method == http.MethodGet {
- getGarageBucket(w, br)
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(ctrl.Describe())
return
}
@@ -50,131 +31,92 @@ func handleAPIWebsite(w http.ResponseWriter, r *http.Request) {
return
}
-func buildBucketRequest(w http.ResponseWriter, r *http.Request) (*BucketRequest, error) {
+func handleAPIWebsiteInspect(w http.ResponseWriter, r *http.Request) {
user := RequireUserApi(w, r)
+
if user == nil {
- return nil, errors.New("Unable to fetch user")
+ return
}
- // FETCH BUCKET ID by iterating over buckets owned by this key
bucketName := mux.Vars(r)["bucket"]
- var bucketId *string
- var global *bool
-
- s3key, err := user.S3KeyInfo()
+ ctrl, err := NewWebsiteController(user)
if err != nil {
- return nil, err
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
}
-findBucketIdLoop:
- for _, bucket := range s3key.Buckets {
- for _, localAlias := range bucket.LocalAliases {
- if localAlias == bucketName {
- bucketId = bucket.Id
- *global = false
- break findBucketIdLoop
- }
- }
- for _, globalAlias := range bucket.GlobalAliases {
- if globalAlias == bucketName {
- bucketId = bucket.Id
- *global = true
- break findBucketIdLoop
- }
+ if r.Method == http.MethodGet {
+ view, err := ctrl.Inspect(bucketName)
+ if errors.Is(err, ErrWebsiteNotFound) {
+ http.Error(w, err.Error(), http.StatusNotFound)
+ return
+ } else if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
}
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(view)
+ return
}
- if bucketId == nil || global == nil {
- http.Error(w, "Bucket not found in this account", http.StatusNotFound)
- return nil, errors.New("Unable to fetch bucket ID")
- }
-
- return &BucketRequest{
- s3key: s3key,
- bucketName: bucketName,
- bucketId: *bucketId,
- global: *global,
- http: r,
- }, nil
-
-}
-
-func patchGarageBucket(w http.ResponseWriter, br *BucketRequest) {
- var err error
+ if r.Method == http.MethodPost {
+ view, err := ctrl.Create(bucketName)
+ if errors.Is(err, ErrEmptyBucketName) {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ } else if errors.Is(err, ErrWebsiteQuotaReached) {
+ http.Error(w, err.Error(), http.StatusForbidden)
+ return
+ } else if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
- // DECODE BODY
- var queuedChange ApiBucketView
- decoder := json.NewDecoder(br.http.Body)
- err = decoder.Decode(&queuedChange)
- if err != nil {
- log.Println(err)
- http.Error(w, "Unable to decode the body", http.StatusBadRequest)
+ w.WriteHeader(http.StatusCreated)
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(view)
return
}
- // SET THE GLOBAL FLAG
- if queuedChange.global != nil {
- if *queuedChange.global && !br.global {
- _, err = grgAddGlobalAlias(br.bucketId, br.bucketName)
- if err != nil {
- http.Error(w, "Unable to add the requested name as global alias for this bucket", http.StatusInternalServerError)
- return
- }
- _, err = grgDelLocalAlias(br.bucketId, *br.s3key.AccessKeyId, br.bucketName)
- if err != nil {
- http.Error(w, "Unable to remove the local alias for this bucket", http.StatusInternalServerError)
- return
- }
- } else if !*queuedChange.global && br.global {
- grgAddLocalAlias(br.bucketId, *br.s3key.AccessKeyId, br.bucketName)
- if err != nil {
- http.Error(w, "Unable to add the requested name as local alias for this bucket", http.StatusInternalServerError)
- return
- }
- grgDelGlobalAlias(br.bucketId, br.bucketName)
- if err != nil {
- http.Error(w, "Unable to remove the global alias for this bucket", http.StatusInternalServerError)
- return
- }
+ if r.Method == http.MethodPatch {
+ var patch WebsitePatch
+ err := json.NewDecoder(r.Body).Decode(&patch)
+ if err != nil {
+ http.Error(w, errors.Join(fmt.Errorf("Can't parse the request body as a website patch JSON"), err).Error(), http.StatusBadRequest)
+ return
}
- }
-
- // CHECK IF QUOTA MUST BE ADDED TO THIS BUCKET
-
- // VALIDATE IT
- // --- global ---
- // 1. can be true, false, or nil (use pointers)
- // 2. if nil do nothing
- // 3. if false, throw "not yet implemented" (501)
- // 4. if true, check that the bucket name does not exist yet in the global namespace, throw "forbidden" (403)
- // --- quota.size ---
- // 1. if no quota on the bucket + this field is none, set to 50MB
- // 2. if lower than 50MB, set to 50MB. If higher than 200MB, set to 200MB
- // --- quota.files ---
- // 1. if no quota on the bucket + this field is none, set to 10k
- // 2. if lower than 10k, set to 10k. If higher than 40k, set to 40k
- // READ BODY JSON
-
- // IF BODY.GLOBAL is not NONE
- // DO: Add an alias
- // IF BODY.QUOTA.SIZE is not NONE
- // DO: Change quota
-
- // IF BODY.QUOTA.FILE is not NONE
- // DO: Change quota
-
- getGarageBucket(w, br)
-}
+ view, err := ctrl.Patch(bucketName, &patch)
+ if errors.Is(err, ErrWebsiteNotFound) {
+ http.Error(w, err.Error(), http.StatusNotFound)
+ return
+ } else if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(view)
+ return
+ }
-func getGarageBucket(w http.ResponseWriter, br *BucketRequest) {
- // FETCH AN UPDATED BUCKET VIEW
- bucket, err := grgGetBucket(br.bucketId)
- if err != nil {
- http.Error(w, "Unable to fetch bucket details", http.StatusInternalServerError)
+ if r.Method == http.MethodDelete {
+ err := ctrl.Delete(bucketName)
+ if errors.Is(err, ErrEmptyBucketName) || errors.Is(err, ErrBucketDeleteNotEmpty) || errors.Is(err, ErrBucketDeleteUnfinishedUpload) {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ } else if errors.Is(err, ErrWebsiteNotFound) {
+ http.Error(w, err.Error(), http.StatusNotFound)
+ return
+ } else if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.WriteHeader(http.StatusNoContent)
return
+
}
- // BUILD A VIEW
- log.Println(bucket)
+ http.Error(w, "This method is not implemented for this endpoint", http.StatusNotImplemented)
+ return
}
diff --git a/garage.go b/garage.go
index d3d4d38..b3ca836 100644
--- a/garage.go
+++ b/garage.go
@@ -194,9 +194,9 @@ func handleWebsiteList(w http.ResponseWriter, r *http.Request) {
return
}
- list := ctrl.List()
- if len(list) > 0 {
- http.Redirect(w, r, "/website/inspect/"+list[0].Pretty, http.StatusFound)
+ desc := ctrl.Describe()
+ if len(desc.Websites) > 0 {
+ http.Redirect(w, r, "/website/inspect/"+desc.Websites[0].Pretty, http.StatusFound)
} else {
http.Redirect(w, r, "/website/new", http.StatusFound)
}
@@ -271,7 +271,7 @@ func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
action := strings.Join(r.Form["action"],"")
switch action {
case "increase_quota":
- _, processErr = ctrl.Patch(bucketName, &WebsitePatch { size: &user.Quota.WebsiteSizeBursted })
+ _, processErr = ctrl.Patch(bucketName, &WebsitePatch { Size: &user.Quota.WebsiteSizeBursted })
case "delete_bucket":
processErr = ctrl.Delete(bucketName)
http.Redirect(w, r, "/website", http.StatusFound)
diff --git a/main.go b/main.go
index 34a1630..9763f53 100644
--- a/main.go
+++ b/main.go
@@ -147,7 +147,8 @@ func server(args []string) {
r.HandleFunc("/login", handleLogin)
r.HandleFunc("/logout", handleLogout)
- r.HandleFunc("/api/unstable/website/{bucket}", handleAPIWebsite)
+ r.HandleFunc("/api/unstable/website", handleAPIWebsiteList)
+ r.HandleFunc("/api/unstable/website/{bucket}", handleAPIWebsiteInspect)
r.HandleFunc("/profile", handleProfile)
r.HandleFunc("/passwd", handlePasswd)
diff --git a/quotas.go b/quotas.go
index 9a2e426..e520f5c 100644
--- a/quotas.go
+++ b/quotas.go
@@ -102,10 +102,10 @@ func (q *UserQuota) WebsiteSizeBurstedPretty() string {
// --- A quota stat we can use
type QuotaStat struct {
- Current int64
- Max int64
- Ratio float64
- Burstable bool
+ Current int64 `json:"current"`
+ Max int64 `json:"max"`
+ Ratio float64 `json:"ratio"`
+ Burstable bool `json:"burstable"`
}
func NewQuotaStat(current, max int64, burstable bool) QuotaStat {
return QuotaStat {
diff --git a/templates/home.html b/templates/home.html
index 3475795..dd88d13 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -24,11 +24,11 @@
<div class="mt-3">
<div class="card">
<div class="card-header">
- Garage
+ Mon espace sur la toile
</div>
<div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="/website/configure">Mes identifiants</a>
- <a class="list-group-item list-group-item-action" href="/website">Mes sites webs</a>
+ <a class="list-group-item list-group-item-action" href="/website">Mes sites Web</a>
</div>
</div>
</div>
diff --git a/website.go b/website.go
index 6e86b19..7e89a41 100644
--- a/website.go
+++ b/website.go
@@ -22,11 +22,11 @@ var (
type WebsiteId struct {
- Pretty string
- Internal string
- Alt []string
- Expanded bool
- Url string
+ 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 {
@@ -50,7 +50,7 @@ func NewWebsiteIdFromBucketInfo(binfo *garage.BucketInfo) *WebsiteId {
}
type WebsiteController struct {
- User *LoggedUser
+ User *LoggedUser
WebsiteIdx map[string]*WebsiteId
PrettyList []string
WebsiteCount QuotaStat
@@ -80,12 +80,17 @@ func NewWebsiteController(user *LoggedUser) (*WebsiteController, error) {
return &WebsiteController { user, idx, wlist, quota }, nil
}
-func (w *WebsiteController) List() []*WebsiteId {
+type WebsiteDescribe struct {
+ AllowedWebsites *QuotaStat `json:"quota"`
+ Websites []*WebsiteId `json:"vhosts"`
+}
+
+func (w *WebsiteController) Describe() *WebsiteDescribe {
r := make([]*WebsiteId, 0, len(w.PrettyList))
for _, k := range w.PrettyList {
r = append(r, w.WebsiteIdx[k])
}
- return r
+ return &WebsiteDescribe { &w.WebsiteCount, r }
}
func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) {
@@ -117,8 +122,8 @@ func (w *WebsiteController) Patch(pretty string, patch *WebsitePatch) (*WebsiteV
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))
+ if patch.Size != nil {
+ urQuota.SetMaxSize(w.User.Quota.WebsiteSizeAdjust(*patch.Size))
}
// Build the update
@@ -205,9 +210,9 @@ func (w *WebsiteController) Delete(pretty string) error {
type WebsiteView struct {
- Name *WebsiteId
- Size QuotaStat
- Files QuotaStat
+ Name *WebsiteId `json:"identified_as"`
+ Size QuotaStat `json:"quota_size"`
+ Files QuotaStat `json:"quota_files"`
}
func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
@@ -220,5 +225,5 @@ func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
}
type WebsitePatch struct {
- size *int64
+ Size *int64 `json:"quota_size"`
}