diff --git a/.cfignore b/.cfignore index 4b19c34..d3d449e 100644 --- a/.cfignore +++ b/.cfignore @@ -1,2 +1,3 @@ vendor +s3manager README.md diff --git a/.gitignore b/.gitignore index 79ccbc4..6af0b65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor +s3manager glide.lock diff --git a/api-handlers.go b/api-handlers.go new file mode 100644 index 0000000..adbe629 --- /dev/null +++ b/api-handlers.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + + "github.com/gorilla/mux" + "github.com/minio/minio-go" +) + +// createBucketHandler creates a new bucket +func createBucketHandler(w http.ResponseWriter, r *http.Request) { + var bucket minio.BucketInfo + body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) + if err != nil { + panic(err) + } + if err = r.Body.Close(); err != nil { + panic(err) + } + err = json.Unmarshal(body, &bucket) + if err != nil { + w.WriteHeader(http.StatusUnprocessableEntity) + return + } + + err = minioClient.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 { + panic(err) + } +} + +// getObjectHandler downloads an object to the client +func getObjectHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + objectName := vars["objectName"] + + object, err := minioClient.GetObject(vars["bucketName"], objectName) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", objectName)) + w.Header().Set("Content-Type", "application/octet-stream") + + _, err = io.Copy(w, object) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } +} + +// deleteObjectHandler deletes an object +func deleteObjectHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + err := minioClient.RemoveObject(vars["bucketName"], vars["objectName"]) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} diff --git a/handlers.go b/page-handlers.go similarity index 54% rename from handlers.go rename to page-handlers.go index eff4f84..07ce000 100644 --- a/handlers.go +++ b/page-handlers.go @@ -6,11 +6,22 @@ import ( "net/http" "path" - "strings" - + "github.com/gorilla/mux" minio "github.com/minio/minio-go" ) +// ObjectWithIcon is a minio object with an added icon +type ObjectWithIcon struct { + minio.ObjectInfo + Icon string +} + +// BucketPage defines the details page of a bucket +type BucketPage struct { + BucketName string + Objects []ObjectWithIcon +} + // indexPageHandler forwards to "/buckets" func indexPageHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/buckets", http.StatusPermanentRedirect) @@ -37,10 +48,10 @@ func bucketsPageHandler(w http.ResponseWriter, r *http.Request) { } } -// bucketHandler handles the main page +// bucketHandler shows the details page of a bucket func bucketPageHandler(w http.ResponseWriter, r *http.Request) { - bucket := strings.Split(r.URL.Path, "/")[2] - var objects []minio.ObjectInfo + bucketName := mux.Vars(r)["bucketName"] + var objects []ObjectWithIcon lp := path.Join("templates", "layout.html") bp := path.Join("templates", "bucket.html") @@ -50,20 +61,40 @@ func bucketPageHandler(w http.ResponseWriter, r *http.Request) { panic(err) } - // Create a done channel to control 'ListObjectsV2' go routine. doneCh := make(chan struct{}) - objectCh := minioClient.ListObjectsV2(bucket, "", false, doneCh) + objectCh := minioClient.ListObjectsV2(bucketName, "", false, doneCh) for object := range objectCh { if object.Err != nil { fmt.Println(object.Err) return } - objects = append(objects, object) + objectWithIcon := ObjectWithIcon{object, getIcon(object.Key)} + objects = append(objects, objectWithIcon) } - err = t.ExecuteTemplate(w, "layout", objects) + bucketPage := BucketPage{ + BucketName: bucketName, + Objects: objects, + } + + err = t.ExecuteTemplate(w, "layout", bucketPage) if err != nil { panic(err) } } + +func getIcon(fileName string) string { + e := path.Ext(fileName) + + switch e { + case ".tgz": + return "archive" + case ".png", ".jpg", ".gif", ".svg": + return "photo" + case ".mp3": + return "music_note" + } + + return "insert_drive_file" +} diff --git a/routes.go b/routes.go index 66f401a..5aaec9e 100644 --- a/routes.go +++ b/routes.go @@ -29,7 +29,25 @@ var routes = Routes{ Route{ "Load Bucket Page", "GET", - "/buckets/{bucketID}", + "/buckets/{bucketName}", bucketPageHandler, }, + Route{ + "Create Bucket", + "POST", + "/api/buckets", + createBucketHandler, + }, + Route{ + "Download Object", + "GET", + "/api/buckets/{bucketName}/objects/{objectName}", + getObjectHandler, + }, + Route{ + "Delete Object", + "DELETE", + "/api/buckets/{bucketName}/objects/{objectName}", + deleteObjectHandler, + }, } diff --git a/templates/bucket.html b/templates/bucket.html index 3d7a699..dd5526b 100644 --- a/templates/bucket.html +++ b/templates/bucket.html @@ -1,25 +1,94 @@ {{ define "content" }} -
Key | -Size | -Owner | -Last Modified | -||
---|---|---|---|---|---|
{{ $object.Key }} | -{{ $object.Size }} bytes | -{{ $object.Owner }} | -{{ $object.LastModified }} | -
+ | Key | +Size | +Owner | +Last Modified | +Actions | +
---|---|---|---|---|---|
{{ $object.Icon }} | +{{ $object.Key }} | +{{ $object.Size }} bytes | +{{ $object.Owner }} | +{{ $object.LastModified }} | ++ + + Actions arrow_drop_down + + + + + | +
Oh noes... No objects in {{ .BucketName }} yet...
{{ end }} - + -