Commit 3c135bb5 authored by Benjamin Rokseth's avatar Benjamin Rokseth
Browse files

Add bucket name to route and validate it

parent 8ed470e0
......@@ -3,6 +3,7 @@ package main
import (
"crypto/rand"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
......@@ -11,14 +12,16 @@ import (
"mime"
"net/http"
"os"
"regexp"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
//"github.com/h2non/bimg" // TODO: image processing -- sudo apt install libvips-tools
minio "github.com/minio/minio-go"
//"github.com/h2non/bimg" // TODO: image processing -- sudo apt install libvips-tools
)
const maxUploadSize = 64 * 1024 * 1024 // 64 MB
var validBucketName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9\.\-\_\:]{1,61}[A-Za-z0-9]$`)
type server struct {
minio *minio.Client // handle to minio bucket storage
......@@ -55,11 +58,12 @@ func (s *server) setupRouter() *chi.Mux {
r.HandleFunc("/_status", s.statusHandler)
r.Route("/api", func(r chi.Router) {
r.Route("/images", func(r chi.Router) {
r.Get("/{id}", s.getImage)
r.Post("/", s.uploadImage)
r.Put("/", s.updateImage)
r.Delete("/{id}", s.deleteImage)
r.Route("/{bucket}", func(r chi.Router) {
r.Get("/{id}", s.getImage)
r.Post("/", s.uploadImage)
r.Put("/", s.updateImage)
r.Delete("/{id}", s.deleteImage)
})
})
})
......@@ -77,15 +81,22 @@ func (s *server) statusHandler(w http.ResponseWriter, r *http.Request) {
}
type Image struct {
Name string `json:"name"`
URL string `json:"url"`
Size int64 `json:"size"`
File io.Reader `json:"-"`
Name string `json:"name"`
Bucket string `json:"bucket"`
URL string `json:"url"`
Size int64 `json:"size"`
FileType string `json:"filetype"`
File io.Reader `json:"-"`
}
func (s *server) getImage(w http.ResponseWriter, r *http.Request) {
bucket := chi.URLParam(r, "bucket")
if err := isValidBucketName(bucket); err != nil {
http.Error(w, "Invalid bucket name", http.StatusBadRequest)
return
}
id := chi.URLParam(r, "id")
info, err := s.minio.StatObject("images", id, minio.StatObjectOptions{})
info, err := s.minio.StatObject(bucket, id, minio.StatObjectOptions{})
if err != nil {
if info.Size == 0 {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
......@@ -94,7 +105,7 @@ func (s *server) getImage(w http.ResponseWriter, r *http.Request) {
}
return
}
obj, err := s.minio.GetObject("images", id, minio.GetObjectOptions{})
obj, err := s.minio.GetObject(bucket, id, minio.GetObjectOptions{})
if err != nil {
log.Println(err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
......@@ -111,6 +122,11 @@ func (s *server) getImage(w http.ResponseWriter, r *http.Request) {
}
func (s *server) uploadImage(w http.ResponseWriter, r *http.Request) {
bucket := chi.URLParam(r, "bucket")
if err := isValidBucketName(bucket); err != nil {
http.Error(w, "Invalid bucket name", http.StatusBadRequest)
return
}
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
if err := r.ParseMultipartForm(maxUploadSize); err != nil {
http.Error(w, "FILE_TOO_BIG", http.StatusBadRequest)
......@@ -141,22 +157,26 @@ func (s *server) uploadImage(w http.ResponseWriter, r *http.Request) {
return
}
fileName := uid + fileEndings[0]
fmt.Printf("FileType: %s, File: %s\n", filetype, fileName)
// Get length of multipart form
fileLen, _ := file.Seek(0, 2)
_, _ = file.Seek(0, 0)
img := Image{
Name: fileName,
URL: fmt.Sprintf("http://localhost:9000/images/%s", fileName),
Size: fileLen,
File: file,
}
Name: fileName,
Bucket: bucket,
URL: fmt.Sprintf("http://localhost:9000/images/%s/%s", bucket, fileName),
Size: fileLen,
FileType: filetype,
File: file,
}
fmt.Printf("%+v\n", img)
// upload to minio
n, err := s.minio.PutObject("images", img.Name, img.File, img.Size, minio.PutObjectOptions{
opts := minio.PutObjectOptions{
ContentType: filetype,
UserMetadata: map[string]string{"X-AMZ-META-FOO": "Bar"}, // TODO: Add metadata from params
})
UserMetadata: map[string]string{"X-Amz-Meta-Foo": "Bar"}, // TODO: Add metadata from params
}
n, err := s.minio.PutObject(img.Bucket, img.Name, img.File, img.Size, opts)
if err != nil {
log.Printf("Failed upload: %s", err)
http.Error(w, "CANT_WRITE_FILE", http.StatusInternalServerError)
......@@ -173,14 +193,18 @@ func (s *server) uploadImage(w http.ResponseWriter, r *http.Request) {
// TODO UPDATE
func (s *server) updateImage(w http.ResponseWriter, r *http.Request) {
//id := chi.URLParam(r, "id")
w.Write([]byte("Not implemented"))
}
func (s *server) deleteImage(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
bucket := chi.URLParam(r, "bucket")
if err := isValidBucketName(bucket); err != nil {
http.Error(w, "Invalid bucket name", http.StatusBadRequest)
return
}
// perhaps GetObject instead?
info, err := s.minio.StatObject("images", id, minio.StatObjectOptions{})
info, err := s.minio.StatObject(bucket, id, minio.StatObjectOptions{})
if err != nil {
if info.Size == 0 {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
......@@ -191,8 +215,8 @@ func (s *server) deleteImage(w http.ResponseWriter, r *http.Request) {
}
fmt.Println(info)
if err := s.minio.RemoveObject("images", id); err != nil {
log.Printf("Could not remove object: %s", err)
if err := s.minio.RemoveObject(bucket, id); err != nil {
log.Printf("Could not remove object: %s bucket: %s\n", err, bucket)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
......@@ -205,6 +229,13 @@ func randID(l int) string {
return fmt.Sprintf("%x", b)
}
func isValidBucketName(b string) error {
if !validBucketName.MatchString(b) {
return errors.New("Bucket name contains invalid characters")
}
return nil
}
func main() {
var (
minioURL = flag.String("murl", "localhost:9000", "Address of minio storage API")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment