An API for Guichet #23

Merged
quentin merged 14 commits from api into main 2023-09-26 06:44:36 +00:00
8 changed files with 139 additions and 147 deletions
Showing only changes of commit 706ff58a6f - Show all commits

12
api.go
View file

@ -1,8 +1,8 @@
package main package main
import ( import (
"errors"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"net/http" "net/http"
@ -12,7 +12,7 @@ func handleAPIWebsiteList(w http.ResponseWriter, r *http.Request) {
user := RequireUserApi(w, r) user := RequireUserApi(w, r)
if user == nil { if user == nil {
return return
} }
ctrl, err := NewWebsiteController(user) ctrl, err := NewWebsiteController(user)
@ -40,7 +40,7 @@ func handleAPIWebsiteInspect(w http.ResponseWriter, r *http.Request) {
user := RequireUserApi(w, r) user := RequireUserApi(w, r)
if user == nil { if user == nil {
return return
} }
bucketName := mux.Vars(r)["bucket"] bucketName := mux.Vars(r)["bucket"]
@ -86,7 +86,7 @@ func handleAPIWebsiteInspect(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPatch { if r.Method == http.MethodPatch {
var patch WebsitePatch var patch WebsitePatch
err := json.NewDecoder(r.Body).Decode(&patch) err := json.NewDecoder(r.Body).Decode(&patch)
if err != nil { 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) http.Error(w, errors.Join(fmt.Errorf("Can't parse the request body as a website patch JSON"), err).Error(), http.StatusBadRequest)
return return
} }
@ -98,7 +98,7 @@ func handleAPIWebsiteInspect(w http.ResponseWriter, r *http.Request) {
} else if err != nil { } else if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(view) json.NewEncoder(w).Encode(view)
@ -119,7 +119,7 @@ func handleAPIWebsiteInspect(w http.ResponseWriter, r *http.Request) {
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} }
http.Error(w, "This method is not implemented for this endpoint", http.StatusNotImplemented) http.Error(w, "This method is not implemented for this endpoint", http.StatusNotImplemented)

20
cli.go
View file

@ -3,9 +3,9 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"os"
"syscall"
"golang.org/x/term" "golang.org/x/term"
"os"
"syscall"
) )
var fsCli = flag.NewFlagSet("cli", flag.ContinueOnError) var fsCli = flag.NewFlagSet("cli", flag.ContinueOnError)
@ -27,18 +27,18 @@ func cliMain(args []string) {
func cliPasswd() { func cliPasswd() {
fmt.Print("Password: ") fmt.Print("Password: ")
bytepw, err := term.ReadPassword(int(syscall.Stdin)) bytepw, err := term.ReadPassword(int(syscall.Stdin))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
pass := string(bytepw) pass := string(bytepw)
hash, err := SSHAEncode(pass) hash, err := SSHAEncode(pass)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
fmt.Println(hash) fmt.Println(hash)
} }

View file

@ -10,7 +10,6 @@ import (
"strings" "strings"
) )
func gadmin() (*garage.APIClient, context.Context) { func gadmin() (*garage.APIClient, context.Context) {
// Set Host and other parameters // Set Host and other parameters
configuration := garage.NewConfiguration() configuration := garage.NewConfiguration()
@ -47,8 +46,6 @@ func grgGetKey(accessKey string) (*garage.KeyInfo, error) {
return resp, nil return resp, nil
} }
func grgCreateBucket(bucket string) (*garage.BucketInfo, error) { func grgCreateBucket(bucket string) (*garage.BucketInfo, error) {
client, ctx := gadmin() client, ctx := gadmin()
@ -64,7 +61,7 @@ func grgCreateBucket(bucket string) (*garage.BucketInfo, error) {
return binfo, nil return binfo, nil
} }
func grgAllowKeyOnBucket(bid, gkey string) (*garage.BucketInfo, error) { func grgAllowKeyOnBucket(bid, gkey string) (*garage.BucketInfo, error) {
client, ctx := gadmin() client, ctx := gadmin()
// Allow user's key // Allow user's key
@ -163,10 +160,10 @@ func grgGetBucket(bid string) (*garage.BucketInfo, error) {
func grgDeleteBucket(bid string) error { func grgDeleteBucket(bid string) error {
client, ctx := gadmin() client, ctx := gadmin()
_, err := client.BucketApi.DeleteBucket(ctx, bid).Execute() _, err := client.BucketApi.DeleteBucket(ctx, bid).Execute()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
return err return err
} }
@ -189,9 +186,9 @@ func handleWebsiteList(w http.ResponseWriter, r *http.Request) {
} }
ctrl, err := NewWebsiteController(user) ctrl, err := NewWebsiteController(user)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if len(ctrl.PrettyList) > 0 { if len(ctrl.PrettyList) > 0 {
@ -203,7 +200,7 @@ 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) {
@ -213,9 +210,9 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
} }
ctrl, err := NewWebsiteController(user) ctrl, err := NewWebsiteController(user)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
tpl := &WebsiteNewTpl{ctrl, nil} tpl := &WebsiteNewTpl{ctrl, nil}
@ -245,8 +242,8 @@ func handleWebsiteNew(w http.ResponseWriter, r *http.Request) {
type WebsiteInspectTpl struct { type WebsiteInspectTpl struct {
Describe *WebsiteDescribe Describe *WebsiteDescribe
View *WebsiteView View *WebsiteView
Err error Err error
} }
func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) { func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
@ -258,42 +255,42 @@ func handleWebsiteInspect(w http.ResponseWriter, r *http.Request) {
} }
ctrl, err := NewWebsiteController(user) ctrl, err := NewWebsiteController(user)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
bucketName := mux.Vars(r)["bucket"] bucketName := mux.Vars(r)["bucket"]
if r.Method == "POST" { if r.Method == "POST" {
r.ParseForm() r.ParseForm()
action := strings.Join(r.Form["action"],"") action := strings.Join(r.Form["action"], "")
switch action { switch action {
case "increase_quota": case "increase_quota":
_, processErr = ctrl.Patch(bucketName, &WebsitePatch { Size: &user.Quota.WebsiteSizeBursted }) _, processErr = ctrl.Patch(bucketName, &WebsitePatch{Size: &user.Quota.WebsiteSizeBursted})
case "delete_bucket": case "delete_bucket":
processErr = ctrl.Delete(bucketName) processErr = ctrl.Delete(bucketName)
http.Redirect(w, r, "/website", http.StatusFound) http.Redirect(w, r, "/website", http.StatusFound)
return return
default: default:
processErr = fmt.Errorf("Unknown action") 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
} }
describe, err := ctrl.Describe() describe, err := ctrl.Describe()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
tpl := &WebsiteInspectTpl{ describe, view, processErr } tpl := &WebsiteInspectTpl{describe, view, processErr}
tWebsiteInspect := getTemplate("garage_website_inspect.html") tWebsiteInspect := getTemplate("garage_website_inspect.html")
tWebsiteInspect.Execute(w, &tpl) tWebsiteInspect.Execute(w, &tpl)

View file

@ -13,12 +13,12 @@ import (
var ( var (
ErrNotAuthenticatedSession = fmt.Errorf("User has no session") ErrNotAuthenticatedSession = fmt.Errorf("User has no session")
ErrNotAuthenticatedBasic = fmt.Errorf("User has not sent Authentication Basic information") ErrNotAuthenticatedBasic = fmt.Errorf("User has not sent Authentication Basic information")
ErrNotAuthenticated = fmt.Errorf("User is not authenticated") ErrNotAuthenticated = fmt.Errorf("User is not authenticated")
ErrWrongLDAPCredentials = fmt.Errorf("LDAP credentials are wrong") ErrWrongLDAPCredentials = fmt.Errorf("LDAP credentials are wrong")
ErrLDAPServerUnreachable = fmt.Errorf("Unable to open the LDAP server") ErrLDAPServerUnreachable = fmt.Errorf("Unable to open the LDAP server")
ErrLDAPSearchInternalError = fmt.Errorf("LDAP Search of this user failed with an internal error") ErrLDAPSearchInternalError = fmt.Errorf("LDAP Search of this user failed with an internal error")
ErrLDAPSearchNotFound = fmt.Errorf("User is authenticated but its associated data can not be found during search") ErrLDAPSearchNotFound = fmt.Errorf("User is authenticated but its associated data can not be found during search")
) )
// --- Login Info --- // --- Login Info ---
@ -48,10 +48,10 @@ func NewLoginInfoFromSession(r *http.Request) (*LoginInfo, error) {
func NewLoginInfoFromBasicAuth(r *http.Request) (*LoginInfo, error) { func NewLoginInfoFromBasicAuth(r *http.Request) (*LoginInfo, error) {
username, password, ok := r.BasicAuth() username, password, ok := r.BasicAuth()
if ok { if ok {
login_info := &LoginInfo{ login_info := &LoginInfo{
Username: username, Username: username,
Password: password, Password: password,
} }
return login_info, nil return login_info, nil
} }
@ -69,8 +69,8 @@ func (li *LoginInfo) DN() string {
// --- Login Status --- // --- Login Status ---
type LoginStatus struct { type LoginStatus struct {
Info *LoginInfo Info *LoginInfo
conn *ldap.Conn conn *ldap.Conn
} }
func NewLoginStatus(r *http.Request, login_info *LoginInfo) (*LoginStatus, error) { func NewLoginStatus(r *http.Request, login_info *LoginInfo) (*LoginStatus, error) {
@ -109,12 +109,13 @@ func NewLdapCon() (*ldap.Conn, error) {
// --- Capabilities --- // --- Capabilities ---
type Capabilities struct { type Capabilities struct {
CanAdmin bool CanAdmin bool
CanInvite bool CanInvite bool
} }
func NewCapabilities(login *LoginStatus, entry *ldap.Entry) *Capabilities { func NewCapabilities(login *LoginStatus, entry *ldap.Entry) *Capabilities {
// Initialize // Initialize
canAdmin := false canAdmin := false
canInvite := false canInvite := false
// Special case for the "admin" account that is de-facto admin // Special case for the "admin" account that is de-facto admin
@ -135,19 +136,20 @@ func NewCapabilities(login *LoginStatus, entry *ldap.Entry) *Capabilities {
} }
return &Capabilities{ return &Capabilities{
CanAdmin: canAdmin, CanAdmin: canAdmin,
CanInvite: canInvite, CanInvite: canInvite,
} }
} }
// --- Logged User --- // --- Logged User ---
type LoggedUser struct { type LoggedUser struct {
Login *LoginStatus Login *LoginStatus
Entry *ldap.Entry Entry *ldap.Entry
Capabilities *Capabilities Capabilities *Capabilities
Quota *UserQuota Quota *UserQuota
s3key *garage.KeyInfo s3key *garage.KeyInfo
} }
func NewLoggedUser(login *LoginStatus) (*LoggedUser, error) { func NewLoggedUser(login *LoginStatus) (*LoggedUser, error) {
requestKind := "(objectClass=organizationalPerson)" requestKind := "(objectClass=organizationalPerson)"
if strings.EqualFold(login.Info.DN(), config.AdminAccount) { if strings.EqualFold(login.Info.DN(), config.AdminAccount) {
@ -184,11 +186,11 @@ func NewLoggedUser(login *LoginStatus) (*LoggedUser, error) {
} }
entry := sr.Entries[0] entry := sr.Entries[0]
lu := &LoggedUser { lu := &LoggedUser{
Login: login, Login: login,
Entry: entry, Entry: entry,
Capabilities: NewCapabilities(login, entry), Capabilities: NewCapabilities(login, entry),
Quota: NewUserQuotaFromEntry(entry), Quota: NewUserQuotaFromEntry(entry),
} }
return lu, nil return lu, nil
} }
@ -251,7 +253,6 @@ func RequireUser(r *http.Request) (*LoggedUser, error) {
return nil, ErrNotAuthenticated return nil, ErrNotAuthenticated
} }
loginStatus, err := NewLoginStatus(r, login_info) loginStatus, err := NewLoginStatus(r, login_info)
if err != nil { if err != nil {
return nil, err return nil, err

23
main.go
View file

@ -119,13 +119,13 @@ func main() {
} }
switch os.Args[1] { switch os.Args[1] {
case "cli": case "cli":
cliMain(os.Args[2:]) cliMain(os.Args[2:])
case "server": case "server":
server(os.Args[2:]) server(os.Args[2:])
default: default:
log.Println("Usage: guichet [server|cli] --help") log.Println("Usage: guichet [server|cli] --help")
os.Exit(1) os.Exit(1)
} }
} }
@ -192,10 +192,9 @@ func logRequest(handler http.Handler) http.Handler {
// Page handlers ---- // Page handlers ----
// --- Home Controller // --- Home Controller
type HomePageData struct { type HomePageData struct {
User *LoggedUser User *LoggedUser
BaseDN string BaseDN string
} }
@ -208,7 +207,7 @@ func handleHome(w http.ResponseWriter, r *http.Request) {
} }
data := &HomePageData{ data := &HomePageData{
User: user, User: user,
BaseDN: config.BaseDN, BaseDN: config.BaseDN,
} }
@ -235,7 +234,7 @@ func handleLogout(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/login", http.StatusFound) http.Redirect(w, r, "/login", http.StatusFound)
} }
// --- Login Controller --- // --- Login Controller ---
type LoginFormData struct { type LoginFormData struct {
Username string Username string
WrongUser bool WrongUser bool
@ -254,7 +253,7 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
username := strings.Join(r.Form["username"], "") username := strings.Join(r.Form["username"], "")
password := strings.Join(r.Form["password"], "") password := strings.Join(r.Form["password"], "")
loginInfo := LoginInfo { username, password } loginInfo := LoginInfo{username, password}
l, err := NewLdapCon() l, err := NewLdapCon()
if err != nil { if err != nil {

View file

@ -8,7 +8,7 @@ import (
) )
type ProfileTplData struct { type ProfileTplData struct {
User *LoggedUser User *LoggedUser
ErrorMessage string ErrorMessage string
Success bool Success bool
Mail string Mail string
@ -29,7 +29,7 @@ func handleProfile(w http.ResponseWriter, r *http.Request) {
} }
data := &ProfileTplData{ data := &ProfileTplData{
User: user, User: user,
ErrorMessage: "", ErrorMessage: "",
Success: false, Success: false,
} }

View file

@ -5,40 +5,40 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/go-ldap/ldap/v3"
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
"github.com/go-ldap/ldap/v3"
) )
const ( const (
// --- Default Quota Values --- // --- Default Quota Values ---
QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB QUOTA_WEBSITE_SIZE_DEFAULT = 1024 * 1024 * 50 // 50MB
QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB QUOTA_WEBSITE_SIZE_BURSTED = 1024 * 1024 * 200 // 200MB
QUOTA_WEBSITE_OBJECTS = 10000 // 10k objects QUOTA_WEBSITE_OBJECTS = 10000 // 10k objects
QUOTA_WEBSITE_COUNT = 5 // 5 buckets QUOTA_WEBSITE_COUNT = 5 // 5 buckets
// --- Per-user overridable fields --- // --- Per-user overridable fields ---
FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted" FIELD_QUOTA_WEBSITE_SIZE_BURSTED = "quota_website_size_bursted"
FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count" FIELD_QUOTA_WEBSITE_COUNT = "quota_website_count"
) )
type UserQuota struct { type UserQuota struct {
WebsiteCount int64 WebsiteCount int64
WebsiteSizeDefault int64 WebsiteSizeDefault int64
WebsiteSizeBursted int64 WebsiteSizeBursted int64
WebsiteObjects int64 WebsiteObjects int64
} }
func NewUserQuota() *UserQuota { func NewUserQuota() *UserQuota {
return &UserQuota { return &UserQuota{
WebsiteCount: QUOTA_WEBSITE_COUNT, WebsiteCount: QUOTA_WEBSITE_COUNT,
WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT, WebsiteSizeDefault: QUOTA_WEBSITE_SIZE_DEFAULT,
WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED, WebsiteSizeBursted: QUOTA_WEBSITE_SIZE_BURSTED,
WebsiteObjects: QUOTA_WEBSITE_OBJECTS, WebsiteObjects: QUOTA_WEBSITE_OBJECTS,
} }
} }
var ( var (
ErrQuotaEmpty = fmt.Errorf("No quota is defined for this entry") ErrQuotaEmpty = fmt.Errorf("No quota is defined for this entry")
ErrQuotaInvalid = fmt.Errorf("The defined quota can't be parsed") ErrQuotaInvalid = fmt.Errorf("The defined quota can't be parsed")
) )
@ -72,7 +72,7 @@ func NewUserQuotaFromEntry(entry *ldap.Entry) *UserQuota {
func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas { func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas {
qr := garage.NewUpdateBucketRequestQuotas() qr := garage.NewUpdateBucketRequestQuotas()
qr.SetMaxSize(q.WebsiteSizeDefault) qr.SetMaxSize(q.WebsiteSizeDefault)
qr.SetMaxObjects(q.WebsiteSizeBursted) qr.SetMaxObjects(q.WebsiteSizeBursted)
return qr return qr
@ -80,7 +80,7 @@ func (q *UserQuota) DefaultWebsiteQuota() *garage.UpdateBucketRequestQuotas {
func (q *UserQuota) WebsiteSizeAdjust(sz int64) int64 { func (q *UserQuota) WebsiteSizeAdjust(sz int64) int64 {
if sz < q.WebsiteSizeDefault { if sz < q.WebsiteSizeDefault {
return q.WebsiteSizeDefault return q.WebsiteSizeDefault
} else if sz > q.WebsiteSizeBursted { } else if sz > q.WebsiteSizeBursted {
return q.WebsiteSizeBursted return q.WebsiteSizeBursted
} else { } else {
@ -102,16 +102,17 @@ func (q *UserQuota) WebsiteSizeBurstedPretty() string {
// --- A quota stat we can use // --- A quota stat we can use
type QuotaStat struct { type QuotaStat struct {
Current int64 `json:"current"` Current int64 `json:"current"`
Max int64 `json:"max"` Max int64 `json:"max"`
Ratio float64 `json:"ratio"` Ratio float64 `json:"ratio"`
Burstable bool `json:"burstable"` Burstable bool `json:"burstable"`
} }
func NewQuotaStat(current, max int64, burstable bool) QuotaStat { func NewQuotaStat(current, max int64, burstable bool) QuotaStat {
return QuotaStat { return QuotaStat{
Current: current, Current: current,
Max: max, Max: max,
Ratio: float64(current) / float64(max), Ratio: float64(current) / float64(max),
Burstable: burstable, Burstable: burstable,
} }
} }
@ -141,7 +142,7 @@ func prettyValue(v int64) string {
if v < 1024 { if v < 1024 {
return fmt.Sprintf("%d Mio", v) return fmt.Sprintf("%d Mio", v)
} }
v = v / 1024 v = v / 1024
if v < 1024 { if v < 1024 {
return fmt.Sprintf("%d Gio", v) return fmt.Sprintf("%d Gio", v)
} }

View file

@ -2,33 +2,31 @@ package main
import ( import (
"fmt" "fmt"
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
"sort" "sort"
"strings" "strings"
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
) )
var ( var (
ErrWebsiteNotFound = fmt.Errorf("Website not found") ErrWebsiteNotFound = fmt.Errorf("Website not found")
ErrFetchBucketInfo = fmt.Errorf("Failed to fetch bucket information") ErrFetchBucketInfo = fmt.Errorf("Failed to fetch bucket information")
ErrWebsiteQuotaReached = fmt.Errorf("Can't create additional websites, quota reached") ErrWebsiteQuotaReached = fmt.Errorf("Can't create additional websites, quota reached")
ErrEmptyBucketName = fmt.Errorf("You can't create a website with an empty name") 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") 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") 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") ErrBucketDeleteUnfinishedUpload = fmt.Errorf("You must remove all the unfinished multipart uploads before deleting a bucket")
) )
type WebsiteId struct { type WebsiteId struct {
Pretty string `json:"name"` Pretty string `json:"name"`
Internal string `json:"-"` Internal string `json:"-"`
Alt []string `json:"alt_name"` Alt []string `json:"alt_name"`
Expanded bool `json:"expanded"` Expanded bool `json:"expanded"`
Url string `json:"domain"` Url string `json:"domain"`
} }
func NewWebsiteId(id string, aliases []string) *WebsiteId { func NewWebsiteId(id string, aliases []string) *WebsiteId {
pretty := id pretty := id
var alt []string var alt []string
@ -43,16 +41,16 @@ func NewWebsiteId(id string, aliases []string) *WebsiteId {
url = fmt.Sprintf("%s.web.deuxfleurs.fr", pretty) url = fmt.Sprintf("%s.web.deuxfleurs.fr", pretty)
} }
return &WebsiteId { pretty, id, alt, expanded, url } return &WebsiteId{pretty, id, alt, expanded, url}
} }
func NewWebsiteIdFromBucketInfo(binfo *garage.BucketInfo) *WebsiteId { func NewWebsiteIdFromBucketInfo(binfo *garage.BucketInfo) *WebsiteId {
return NewWebsiteId(*binfo.Id, binfo.GlobalAliases) return NewWebsiteId(*binfo.Id, binfo.GlobalAliases)
} }
type WebsiteController struct { type WebsiteController struct {
User *LoggedUser User *LoggedUser
WebsiteIdx map[string]*WebsiteId WebsiteIdx map[string]*WebsiteId
PrettyList []string PrettyList []string
WebsiteCount QuotaStat WebsiteCount QuotaStat
} }
@ -65,7 +63,7 @@ func NewWebsiteController(user *LoggedUser) (*WebsiteController, error) {
return nil, err return nil, err
} }
for _, bckt := range(keyInfo.Buckets) { for _, bckt := range keyInfo.Buckets {
if len(bckt.GlobalAliases) > 0 { if len(bckt.GlobalAliases) > 0 {
wid := NewWebsiteId(*bckt.Id, bckt.GlobalAliases) wid := NewWebsiteId(*bckt.Id, bckt.GlobalAliases)
idx[wid.Pretty] = wid idx[wid.Pretty] = wid
@ -77,15 +75,15 @@ func NewWebsiteController(user *LoggedUser) (*WebsiteController, error) {
maxW := user.Quota.WebsiteCount maxW := user.Quota.WebsiteCount
quota := NewQuotaStat(int64(len(wlist)), maxW, true) quota := NewQuotaStat(int64(len(wlist)), maxW, true)
return &WebsiteController { user, idx, wlist, quota }, nil return &WebsiteController{user, idx, wlist, quota}, nil
} }
type WebsiteDescribe struct { type WebsiteDescribe struct {
AccessKeyId string `json:"access_key_id"` AccessKeyId string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"` SecretAccessKey string `json:"secret_access_key"`
AllowedWebsites *QuotaStat `json:"quota_website_count"` AllowedWebsites *QuotaStat `json:"quota_website_count"`
BurstBucketQuotaSize string `json:"burst_bucket_quota_size"` BurstBucketQuotaSize string `json:"burst_bucket_quota_size"`
Websites []*WebsiteId `json:"vhosts"` Websites []*WebsiteId `json:"vhosts"`
} }
func (w *WebsiteController) Describe() (*WebsiteDescribe, error) { func (w *WebsiteController) Describe() (*WebsiteDescribe, error) {
@ -96,14 +94,14 @@ func (w *WebsiteController) Describe() (*WebsiteDescribe, error) {
r := make([]*WebsiteId, 0, len(w.PrettyList)) r := make([]*WebsiteId, 0, len(w.PrettyList))
for _, k := range w.PrettyList { for _, k := range w.PrettyList {
r = append(r, w.WebsiteIdx[k]) r = append(r, w.WebsiteIdx[k])
} }
return &WebsiteDescribe { return &WebsiteDescribe{
*s3key.AccessKeyId, *s3key.AccessKeyId,
*s3key.SecretAccessKey, *s3key.SecretAccessKey,
&w.WebsiteCount, &w.WebsiteCount,
w.User.Quota.WebsiteSizeBurstedPretty(), w.User.Quota.WebsiteSizeBurstedPretty(),
r }, nil r}, nil
} }
func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) { func (w *WebsiteController) Inspect(pretty string) (*WebsiteView, error) {
@ -183,7 +181,6 @@ func (w *WebsiteController) Create(pretty string) (*WebsiteView, error) {
ur.SetWebsiteAccess(*wr) ur.SetWebsiteAccess(*wr)
ur.SetQuotas(*qr) ur.SetQuotas(*qr)
binfo, err = grgUpdateBucket(*binfo.Id, ur) binfo, err = grgUpdateBucket(*binfo.Id, ur)
if err != nil { if err != nil {
return nil, ErrCantConfigureBucket return nil, ErrCantConfigureBucket
@ -209,7 +206,7 @@ func (w *WebsiteController) Delete(pretty string) error {
if *binfo.Objects > int64(0) { if *binfo.Objects > int64(0) {
return ErrBucketDeleteNotEmpty return ErrBucketDeleteNotEmpty
} }
if *binfo.UnfinishedUploads > int32(0) { if *binfo.UnfinishedUploads > int32(0) {
return ErrBucketDeleteUnfinishedUpload return ErrBucketDeleteUnfinishedUpload
@ -219,13 +216,10 @@ func (w *WebsiteController) Delete(pretty string) error {
return err return err
} }
type WebsiteView struct { type WebsiteView struct {
Name *WebsiteId `json:"identified_as"` Name *WebsiteId `json:"identified_as"`
Size QuotaStat `json:"quota_size"` Size QuotaStat `json:"quota_size"`
Files QuotaStat `json:"quota_files"` Files QuotaStat `json:"quota_files"`
} }
func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView { func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
@ -234,7 +228,7 @@ func NewWebsiteView(binfo *garage.BucketInfo) *WebsiteView {
wid := NewWebsiteIdFromBucketInfo(binfo) wid := NewWebsiteIdFromBucketInfo(binfo)
size := NewQuotaStat(*binfo.Bytes, (&q).GetMaxSize(), true) size := NewQuotaStat(*binfo.Bytes, (&q).GetMaxSize(), true)
objects := NewQuotaStat(*binfo.Objects, (&q).GetMaxObjects(), false) objects := NewQuotaStat(*binfo.Objects, (&q).GetMaxObjects(), false)
return &WebsiteView { wid, size, objects } return &WebsiteView{wid, size, objects}
} }
type WebsitePatch struct { type WebsitePatch struct {