Use adapter pattern for handlers
This commit is contained in:
parent
5466c615aa
commit
27bc86490e
6 changed files with 181 additions and 164 deletions
15
adapters.go
Normal file
15
adapters.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Adapter is an HTTP middleware
|
||||||
|
type Adapter func(http.Handler) http.Handler
|
||||||
|
|
||||||
|
// Adapt applies adapters to an HTTP handler function
|
||||||
|
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
|
||||||
|
for i := len(adapters) - 1; i >= 0; i-- {
|
||||||
|
h = adapters[i](h)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
188
api.go
188
api.go
|
@ -19,49 +19,17 @@ type CopyObjectInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBucketHandler creates a new bucket
|
// CreateBucketHandler creates a new bucket
|
||||||
func (s *Server) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) CreateBucketHandler() http.Handler {
|
||||||
var bucket minio.BucketInfo
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var bucket minio.BucketInfo
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&bucket)
|
err := json.NewDecoder(r.Body).Decode(&bucket)
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.s3.MakeBucket(bucket.Name, "")
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(bucket)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateObjectHandler allows to upload a new object
|
|
||||||
func (s *Server) CreateObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
|
|
||||||
if r.Header.Get("Content-Type") == "application/json" {
|
|
||||||
var copy CopyObjectInfo
|
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(©)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var copyConds = minio.NewCopyConditions()
|
err = s.s3.MakeBucket(bucket.Name, "")
|
||||||
objectSource := fmt.Sprintf("/%s/%s", copy.SourceBucketName, copy.SourceObjectName)
|
|
||||||
fmt.Println(copy)
|
|
||||||
fmt.Println(objectSource)
|
|
||||||
err = s.s3.CopyObject(copy.BucketName, copy.ObjectName, objectSource, copyConds)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -69,77 +37,119 @@ func (s *Server) CreateObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
err = json.NewEncoder(w).Encode(copy)
|
|
||||||
|
err = json.NewEncoder(w).Encode(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := r.ParseMultipartForm(32 << 20)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
file, handler, err := r.FormFile("file")
|
// CreateObjectHandler allows to upload a new object
|
||||||
if err != nil {
|
func (s *Server) CreateObjectHandler() http.Handler {
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
|
if r.Header.Get("Content-Type") == "application/json" {
|
||||||
|
var copy CopyObjectInfo
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(©)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyConds = minio.NewCopyConditions()
|
||||||
|
objectSource := fmt.Sprintf("/%s/%s", copy.SourceBucketName, copy.SourceObjectName)
|
||||||
|
fmt.Println(copy)
|
||||||
|
fmt.Println(objectSource)
|
||||||
|
err = s.s3.CopyObject(copy.BucketName, copy.ObjectName, objectSource, copyConds)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
err = json.NewEncoder(w).Encode(copy)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := r.ParseMultipartForm(32 << 20)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, handler, err := r.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = s.s3.PutObject(vars["bucketName"], handler.Filename, file, "application/octet-stream")
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
})
|
||||||
|
|
||||||
_, err = s.s3.PutObject(vars["bucketName"], handler.Filename, file, "application/octet-stream")
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBucketHandler deletes a bucket
|
// DeleteBucketHandler deletes a bucket
|
||||||
func (s *Server) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) DeleteBucketHandler() http.Handler {
|
||||||
vars := mux.Vars(r)
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
err := s.s3.RemoveBucket(vars["bucketName"])
|
err := s.s3.RemoveBucket(vars["bucketName"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObjectHandler deletes an object
|
// DeleteObjectHandler deletes an object
|
||||||
func (s *Server) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) DeleteObjectHandler() http.Handler {
|
||||||
vars := mux.Vars(r)
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
err := s.s3.RemoveObject(vars["bucketName"], vars["objectName"])
|
err := s.s3.RemoveObject(vars["bucketName"], vars["objectName"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectHandler downloads an object to the client
|
// GetObjectHandler downloads an object to the client
|
||||||
func (s *Server) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) GetObjectHandler() http.Handler {
|
||||||
vars := mux.Vars(r)
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
objectName := vars["objectName"]
|
vars := mux.Vars(r)
|
||||||
|
objectName := vars["objectName"]
|
||||||
|
|
||||||
object, err := s.s3.GetObject(vars["bucketName"], objectName)
|
object, err := s.s3.GetObject(vars["bucketName"], objectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", objectName))
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", objectName))
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
|
||||||
_, err = io.Copy(w, object)
|
_, err = io.Copy(w, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
10
logger.go
10
logger.go
|
@ -7,9 +7,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger logs HTTP requests
|
// Logger logs HTTP requests
|
||||||
func Logger() Middleware {
|
func Logger() Adapter {
|
||||||
return func(next http.HandlerFunc) http.HandlerFunc {
|
return func(next http.Handler) http.Handler {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -21,7 +21,7 @@ func Logger() Middleware {
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
next(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
main.go
16
main.go
|
@ -29,15 +29,15 @@ func main() {
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/").
|
Path("/").
|
||||||
HandlerFunc(Chain(IndexHandler, Logger()))
|
Handler(Adapt(IndexHandler(), Logger()))
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/buckets").
|
Path("/buckets").
|
||||||
HandlerFunc(Chain(s.BucketsPageHandler, Logger()))
|
Handler(Adapt(s.BucketsPageHandler(), Logger()))
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/buckets/{bucketName}").
|
Path("/buckets/{bucketName}").
|
||||||
HandlerFunc(Chain(s.BucketPageHandler, Logger()))
|
Handler(Adapt(s.BucketPageHandler(), Logger()))
|
||||||
|
|
||||||
api := router.PathPrefix("/api").Subrouter()
|
api := router.PathPrefix("/api").Subrouter()
|
||||||
|
|
||||||
|
@ -45,23 +45,23 @@ func main() {
|
||||||
buckets.
|
buckets.
|
||||||
Methods("POST").
|
Methods("POST").
|
||||||
Path("").
|
Path("").
|
||||||
HandlerFunc(Chain(s.CreateBucketHandler, Logger()))
|
Handler(Adapt(s.CreateBucketHandler(), Logger()))
|
||||||
buckets.
|
buckets.
|
||||||
Methods("DELETE").
|
Methods("DELETE").
|
||||||
Path("/{bucketName}").
|
Path("/{bucketName}").
|
||||||
HandlerFunc(Chain(s.DeleteBucketHandler, Logger()))
|
Handler(Adapt(s.DeleteBucketHandler(), Logger()))
|
||||||
buckets.
|
buckets.
|
||||||
Methods("POST").
|
Methods("POST").
|
||||||
Path("/{bucketName}/objects").
|
Path("/{bucketName}/objects").
|
||||||
HandlerFunc(Chain(s.CreateObjectHandler, Logger()))
|
Handler(Adapt(s.CreateObjectHandler(), Logger()))
|
||||||
buckets.
|
buckets.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/{bucketName}/objects/{objectName}").
|
Path("/{bucketName}/objects/{objectName}").
|
||||||
HandlerFunc(Chain(s.GetObjectHandler, Logger()))
|
Handler(Adapt(s.GetObjectHandler(), Logger()))
|
||||||
buckets.
|
buckets.
|
||||||
Methods("DELETE").
|
Methods("DELETE").
|
||||||
Path("/{bucketName}/objects/{objectName}").
|
Path("/{bucketName}/objects/{objectName}").
|
||||||
HandlerFunc(Chain(s.DeleteObjectHandler, Logger()))
|
Handler(Adapt(s.DeleteObjectHandler(), Logger()))
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe(":"+port, router))
|
log.Fatal(http.ListenAndServe(":"+port, router))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// Middleware is an HTTP middleware
|
|
||||||
type Middleware func(http.HandlerFunc) http.HandlerFunc
|
|
||||||
|
|
||||||
// Chain applies middleware to an HTTP handler function
|
|
||||||
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
|
|
||||||
for i := len(middlewares) - 1; i >= 0; i-- {
|
|
||||||
f = middlewares[i](f)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
102
pages.go
102
pages.go
|
@ -22,70 +22,76 @@ type ObjectWithIcon struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BucketPageHandler shows the details page of a bucket
|
// BucketPageHandler shows the details page of a bucket
|
||||||
func (s *Server) BucketPageHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) BucketPageHandler() http.Handler {
|
||||||
bucketName := mux.Vars(r)["bucketName"]
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var objects []ObjectWithIcon
|
bucketName := mux.Vars(r)["bucketName"]
|
||||||
|
var objects []ObjectWithIcon
|
||||||
|
|
||||||
lp := path.Join("templates", "layout.html")
|
lp := path.Join("templates", "layout.html")
|
||||||
bp := path.Join("templates", "bucket.html")
|
bp := path.Join("templates", "bucket.html")
|
||||||
|
|
||||||
t, err := template.ParseFiles(lp, bp)
|
t, err := template.ParseFiles(lp, bp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
doneCh := make(chan struct{})
|
|
||||||
|
|
||||||
objectCh := s.s3.ListObjectsV2(bucketName, "", false, doneCh)
|
|
||||||
for object := range objectCh {
|
|
||||||
if object.Err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
objectWithIcon := ObjectWithIcon{object, icon(object.Key)}
|
|
||||||
objects = append(objects, objectWithIcon)
|
|
||||||
}
|
|
||||||
|
|
||||||
bucketPage := BucketPage{
|
doneCh := make(chan struct{})
|
||||||
BucketName: bucketName,
|
|
||||||
Objects: objects,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, "layout", bucketPage)
|
objectCh := s.s3.ListObjectsV2(bucketName, "", false, doneCh)
|
||||||
if err != nil {
|
for object := range objectCh {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
if object.Err != nil {
|
||||||
return
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
objectWithIcon := ObjectWithIcon{object, icon(object.Key)}
|
||||||
|
objects = append(objects, objectWithIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketPage := BucketPage{
|
||||||
|
BucketName: bucketName,
|
||||||
|
Objects: objects,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.ExecuteTemplate(w, "layout", bucketPage)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// BucketsPageHandler shows all buckets
|
// BucketsPageHandler shows all buckets
|
||||||
func (s *Server) BucketsPageHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) BucketsPageHandler() http.Handler {
|
||||||
lp := path.Join("templates", "layout.html")
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ip := path.Join("templates", "index.html")
|
lp := path.Join("templates", "layout.html")
|
||||||
|
ip := path.Join("templates", "index.html")
|
||||||
|
|
||||||
t, err := template.ParseFiles(lp, ip)
|
t, err := template.ParseFiles(lp, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buckets, err := s.s3.ListBuckets()
|
buckets, err := s.s3.ListBuckets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, "layout", buckets)
|
err = t.ExecuteTemplate(w, "layout", buckets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexHandler forwards to "/buckets"
|
// IndexHandler forwards to "/buckets"
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
func IndexHandler() http.Handler {
|
||||||
http.Redirect(w, r, "/buckets", http.StatusPermanentRedirect)
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Redirect(w, r, "/buckets", http.StatusPermanentRedirect)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// icon returns an icon for a file type
|
// icon returns an icon for a file type
|
||||||
|
|
Loading…
Reference in a new issue