Restructure app with modules
This commit is contained in:
parent
8e94fc2781
commit
8f47c50f72
20 changed files with 396 additions and 316 deletions
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package adapters
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package adapters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
168
api.go
168
api.go
|
@ -1,168 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/minio/minio-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CopyObjectInfo is the information about an object to copy
|
|
||||||
type CopyObjectInfo struct {
|
|
||||||
BucketName string `json:"bucketName"`
|
|
||||||
ObjectName string `json:"objectName"`
|
|
||||||
SourceBucketName string `json:"sourceBucketName"`
|
|
||||||
SourceObjectName string `json:"sourceObjectName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateBucketHandler creates a new bucket
|
|
||||||
func (s *Server) CreateBucketHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var bucket minio.BucketInfo
|
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&bucket)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error decoding json"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.S3.MakeBucket(bucket.Name, "")
|
|
||||||
if err != nil {
|
|
||||||
msg := "error making bucket"
|
|
||||||
handleHTTPError(w, msg, err, 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 {
|
|
||||||
msg := "error encoding json"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateObjectHandler allows to upload a new object
|
|
||||||
func (s *Server) CreateObjectHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(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 {
|
|
||||||
msg := "error decoding json"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var copyConds = minio.NewCopyConditions()
|
|
||||||
objectSource := fmt.Sprintf("/%s/%s", copy.SourceBucketName, copy.SourceObjectName)
|
|
||||||
err = s.S3.CopyObject(copy.BucketName, copy.ObjectName, objectSource, copyConds)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error copying object"
|
|
||||||
handleHTTPError(w, msg, err, 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 {
|
|
||||||
msg := "error encoding json"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := r.ParseMultipartForm(32 << 20)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error parsing form"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, handler, err := r.FormFile("file")
|
|
||||||
if err != nil {
|
|
||||||
msg := "error getting form file"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
_, err = s.S3.PutObject(vars["bucketName"], handler.Filename, file, "application/octet-stream")
|
|
||||||
if err != nil {
|
|
||||||
msg := "error putting object"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteBucketHandler deletes a bucket
|
|
||||||
func (s *Server) DeleteBucketHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
|
|
||||||
err := s.S3.RemoveBucket(vars["bucketName"])
|
|
||||||
if err != nil {
|
|
||||||
msg := "error removing bucket"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteObjectHandler deletes an object
|
|
||||||
func (s *Server) DeleteObjectHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
|
|
||||||
err := s.S3.RemoveObject(vars["bucketName"], vars["objectName"])
|
|
||||||
if err != nil {
|
|
||||||
msg := "error removing object"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetObjectHandler downloads an object to the client
|
|
||||||
func (s *Server) GetObjectHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
objectName := vars["objectName"]
|
|
||||||
|
|
||||||
object, err := s.S3.GetObject(vars["bucketName"], objectName)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error getting object"
|
|
||||||
handleHTTPError(w, msg, err, 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 {
|
|
||||||
msg := "error copying object"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
40
buckets/create-handler.go
Normal file
40
buckets/create-handler.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package buckets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateHandler creates a new bucket
|
||||||
|
func CreateHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var bucket minio.BucketInfo
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&bucket)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error decoding json"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s3.MakeBucket(bucket.Name, "")
|
||||||
|
if err != nil {
|
||||||
|
msg := "error making bucket"
|
||||||
|
web.HandleHTTPError(w, msg, err, 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 {
|
||||||
|
msg := "error encoding json"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
25
buckets/delete-handler.go
Normal file
25
buckets/delete-handler.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package buckets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteHandler deletes a bucket
|
||||||
|
func DeleteHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
|
err := s3.RemoveBucket(vars["bucketName"])
|
||||||
|
if err != nil {
|
||||||
|
msg := "error removing bucket"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package datasources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
67
main.go
67
main.go
|
@ -6,18 +6,15 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio-go"
|
"github.com/mastertinner/s3-manager/adapters"
|
||||||
|
"github.com/mastertinner/s3-manager/buckets"
|
||||||
|
"github.com/mastertinner/s3-manager/datasources"
|
||||||
|
"github.com/mastertinner/s3-manager/objects"
|
||||||
|
"github.com/mastertinner/s3-manager/views"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is a server containing a minio client
|
|
||||||
type Server struct {
|
|
||||||
S3 *minio.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := &Server{
|
s3 := datasources.NewMinioClient()
|
||||||
S3: NewMinioClient(),
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := log.New(os.Stdout, "request: ", log.Lshortfile)
|
logger := log.New(os.Stdout, "request: ", log.Lshortfile)
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
|
@ -25,39 +22,63 @@ func main() {
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/").
|
Path("/").
|
||||||
Handler(Adapt(IndexHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
|
views.IndexHandler(),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/buckets").
|
Path("/buckets").
|
||||||
Handler(Adapt(s.BucketsPageHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
|
views.BucketsHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
router.
|
router.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/buckets/{bucketName}").
|
Path("/buckets/{bucketName}").
|
||||||
Handler(Adapt(s.BucketPageHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
|
views.BucketHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
|
||||||
api := router.PathPrefix("/api").Subrouter()
|
api := router.PathPrefix("/api").Subrouter()
|
||||||
|
|
||||||
buckets := api.PathPrefix("/buckets").Subrouter()
|
br := api.PathPrefix("/buckets").Subrouter()
|
||||||
buckets.
|
br.
|
||||||
Methods("POST").
|
Methods("POST").
|
||||||
Path("").
|
Path("").
|
||||||
Handler(Adapt(s.CreateBucketHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
buckets.
|
buckets.CreateHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
br.
|
||||||
Methods("DELETE").
|
Methods("DELETE").
|
||||||
Path("/{bucketName}").
|
Path("/{bucketName}").
|
||||||
Handler(Adapt(s.DeleteBucketHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
buckets.
|
buckets.DeleteHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
br.
|
||||||
Methods("POST").
|
Methods("POST").
|
||||||
Path("/{bucketName}/objects").
|
Path("/{bucketName}/objects").
|
||||||
Handler(Adapt(s.CreateObjectHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
buckets.
|
objects.CreateHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
br.
|
||||||
Methods("GET").
|
Methods("GET").
|
||||||
Path("/{bucketName}/objects/{objectName}").
|
Path("/{bucketName}/objects/{objectName}").
|
||||||
Handler(Adapt(s.GetObjectHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
buckets.
|
objects.GetHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
br.
|
||||||
Methods("DELETE").
|
Methods("DELETE").
|
||||||
Path("/{bucketName}/objects/{objectName}").
|
Path("/{bucketName}/objects/{objectName}").
|
||||||
Handler(Adapt(s.DeleteObjectHandler(), Logging(logger)))
|
Handler(adapters.Adapt(
|
||||||
|
objects.DeleteHandler(s3),
|
||||||
|
adapters.Logging(logger),
|
||||||
|
))
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
if len(port) == 0 {
|
if len(port) == 0 {
|
||||||
|
|
80
objects/create-handler.go
Normal file
80
objects/create-handler.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CopyObjectInfo is the information about an object to copy
|
||||||
|
type CopyObjectInfo struct {
|
||||||
|
BucketName string `json:"bucketName"`
|
||||||
|
ObjectName string `json:"objectName"`
|
||||||
|
SourceBucketName string `json:"sourceBucketName"`
|
||||||
|
SourceObjectName string `json:"sourceObjectName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateHandler allows to upload a new object
|
||||||
|
func CreateHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(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 {
|
||||||
|
msg := "error decoding json"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyConds = minio.NewCopyConditions()
|
||||||
|
objectSource := fmt.Sprintf("/%s/%s", copy.SourceBucketName, copy.SourceObjectName)
|
||||||
|
err = s3.CopyObject(copy.BucketName, copy.ObjectName, objectSource, copyConds)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error copying object"
|
||||||
|
web.HandleHTTPError(w, msg, err, 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 {
|
||||||
|
msg := "error encoding json"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := r.ParseMultipartForm(32 << 20)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error parsing form"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusUnprocessableEntity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, handler, err := r.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
msg := "error getting form file"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = s3.PutObject(vars["bucketName"], handler.Filename, file, "application/octet-stream")
|
||||||
|
if err != nil {
|
||||||
|
msg := "error putting object"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
25
objects/delete-handler.go
Normal file
25
objects/delete-handler.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteHandler deletes an object
|
||||||
|
func DeleteHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
|
err := s3.RemoveObject(vars["bucketName"], vars["objectName"])
|
||||||
|
if err != nil {
|
||||||
|
msg := "error removing object"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
}
|
36
objects/get-handler.go
Normal file
36
objects/get-handler.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetHandler downloads an object to the client
|
||||||
|
func GetHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
objectName := vars["objectName"]
|
||||||
|
|
||||||
|
object, err := s3.GetObject(vars["bucketName"], objectName)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error getting object"
|
||||||
|
web.HandleHTTPError(w, msg, err, 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 {
|
||||||
|
msg := "error copying object"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
9
objects/with-icon.go
Normal file
9
objects/with-icon.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import minio "github.com/minio/minio-go"
|
||||||
|
|
||||||
|
// WithIcon is a minio object with an added icon
|
||||||
|
type WithIcon struct {
|
||||||
|
minio.ObjectInfo
|
||||||
|
Icon string
|
||||||
|
}
|
116
pages.go
116
pages.go
|
@ -1,116 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/minio/minio-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BucketPage defines the details page of a bucket
|
|
||||||
type BucketPage struct {
|
|
||||||
BucketName string
|
|
||||||
Objects []ObjectWithIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectWithIcon is a minio object with an added icon
|
|
||||||
type ObjectWithIcon struct {
|
|
||||||
minio.ObjectInfo
|
|
||||||
Icon string
|
|
||||||
}
|
|
||||||
|
|
||||||
// BucketPageHandler shows the details page of a bucket
|
|
||||||
func (s *Server) BucketPageHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
bucketName := mux.Vars(r)["bucketName"]
|
|
||||||
var objects []ObjectWithIcon
|
|
||||||
|
|
||||||
lp := path.Join("templates", "layout.html")
|
|
||||||
bp := path.Join("templates", "bucket.html")
|
|
||||||
|
|
||||||
t, err := template.ParseFiles(lp, bp)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error parsing templates"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
doneCh := make(chan struct{})
|
|
||||||
objectCh := s.S3.ListObjectsV2(bucketName, "", false, doneCh)
|
|
||||||
for object := range objectCh {
|
|
||||||
if object.Err != nil {
|
|
||||||
msg := "error listing objects"
|
|
||||||
handleHTTPError(w, msg, err, 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 {
|
|
||||||
msg := "error executing template"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// BucketsPageHandler shows all buckets
|
|
||||||
func (s *Server) BucketsPageHandler() http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
lp := path.Join("templates", "layout.html")
|
|
||||||
ip := path.Join("templates", "index.html")
|
|
||||||
|
|
||||||
t, err := template.ParseFiles(lp, ip)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error parsing templates"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buckets, err := s.S3.ListBuckets()
|
|
||||||
if err != nil {
|
|
||||||
msg := "error listing buckets"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, "layout", buckets)
|
|
||||||
if err != nil {
|
|
||||||
msg := "error executing template"
|
|
||||||
handleHTTPError(w, msg, err, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexHandler forwards to "/buckets"
|
|
||||||
func IndexHandler() http.Handler {
|
|
||||||
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
|
|
||||||
func icon(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"
|
|
||||||
}
|
|
60
views/bucket-handler.go
Normal file
60
views/bucket-handler.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mastertinner/s3-manager/objects"
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketPage defines the details page of a bucket
|
||||||
|
type BucketPage struct {
|
||||||
|
BucketName string
|
||||||
|
Objects []objects.WithIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
// BucketHandler shows the details page of a bucket
|
||||||
|
func BucketHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
bucketName := mux.Vars(r)["bucketName"]
|
||||||
|
var objs []objects.WithIcon
|
||||||
|
|
||||||
|
lp := path.Join("views", "layout.html")
|
||||||
|
p := path.Join("views", "bucket.html")
|
||||||
|
|
||||||
|
t, err := template.ParseFiles(lp, p)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error parsing templates"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doneCh := make(chan struct{})
|
||||||
|
objectCh := s3.ListObjectsV2(bucketName, "", false, doneCh)
|
||||||
|
for object := range objectCh {
|
||||||
|
if object.Err != nil {
|
||||||
|
msg := "error listing objects"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objectWithIcon := objects.WithIcon{object, icon(object.Key)}
|
||||||
|
objs = append(objs, objectWithIcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketPage := BucketPage{
|
||||||
|
BucketName: bucketName,
|
||||||
|
Objects: objs,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.ExecuteTemplate(w, "layout", bucketPage)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error executing template"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
39
views/buckets-handler.go
Normal file
39
views/buckets-handler.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/mastertinner/s3-manager/web"
|
||||||
|
minio "github.com/minio/minio-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketsHandler shows all buckets
|
||||||
|
func BucketsHandler(s3 *minio.Client) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
lp := path.Join("views", "layout.html")
|
||||||
|
p := path.Join("views", "buckets.html")
|
||||||
|
|
||||||
|
t, err := template.ParseFiles(lp, p)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error parsing templates"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buckets, err := s3.ListBuckets()
|
||||||
|
if err != nil {
|
||||||
|
msg := "error listing buckets"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.ExecuteTemplate(w, "layout", buckets)
|
||||||
|
if err != nil {
|
||||||
|
msg := "error executing template"
|
||||||
|
web.HandleHTTPError(w, msg, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
19
views/icon.go
Normal file
19
views/icon.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import "path"
|
||||||
|
|
||||||
|
// icon returns an icon for a file type
|
||||||
|
func icon(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"
|
||||||
|
}
|
10
views/index-handler.go
Normal file
10
views/index-handler.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// IndexHandler forwards to "/buckets"
|
||||||
|
func IndexHandler() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.Redirect(w, r, "/buckets", http.StatusPermanentRedirect)
|
||||||
|
})
|
||||||
|
}
|
|
@ -10,15 +10,15 @@
|
||||||
<title>S3 Manager</title>
|
<title>S3 Manager</title>
|
||||||
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.1/css/materialize.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
{{ template "content" . }}
|
{{ template "content" . }}
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.1/js/materialize.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$('.modal').modal();
|
$('.modal').modal();
|
|
@ -1,12 +1,12 @@
|
||||||
package main
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleHTTPError handles HTTP errors
|
// HandleHTTPError handles HTTP errors
|
||||||
func handleHTTPError(w http.ResponseWriter, msg string, err error, statusCode int) {
|
func HandleHTTPError(w http.ResponseWriter, msg string, err error, statusCode int) {
|
||||||
http.Error(w, msg, statusCode)
|
http.Error(w, msg, statusCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(msg+":", err.Error())
|
log.Println(msg+":", err.Error())
|
Loading…
Reference in a new issue