diff options
| author | Max Resnick <max@ofmax.li> | 2020-05-23 07:56:12 -0700 |
|---|---|---|
| committer | Max Resnick <max@ofmax.li> | 2020-06-22 22:37:17 -0700 |
| commit | 85e7eaa3a1c9024c02cc9a63744cdfb144cc3737 (patch) | |
| tree | a44855abcf5424126c54ee636463dc866e558561 /internal | |
| parent | 4e77ad5762539d8f9edf40d2668a998c38e834d3 (diff) | |
| download | iserv-85e7eaa3a1c9024c02cc9a63744cdfb144cc3737.tar.gz | |
adds tmpl, css, and fileserver
Diffstat (limited to '')
| -rw-r--r-- | internal/db/redis/image.go | 21 | ||||
| -rw-r--r-- | internal/db/redis/redis.go | 2 | ||||
| -rw-r--r-- | internal/fs/fs.go | 18 | ||||
| -rw-r--r-- | internal/image/handler.go | 33 | ||||
| -rw-r--r-- | internal/image/image_test.go | 42 | ||||
| -rw-r--r-- | internal/image/model.go | 1 | ||||
| -rw-r--r-- | internal/image/repo.go | 1 | ||||
| -rw-r--r-- | internal/image/service.go | 46 |
8 files changed, 140 insertions, 24 deletions
diff --git a/internal/db/redis/image.go b/internal/db/redis/image.go index 6814fb7..1f4e8fa 100644 --- a/internal/db/redis/image.go +++ b/internal/db/redis/image.go @@ -9,7 +9,7 @@ import ( "git.ofmax.li/iserv/internal/image" ) -const V1FilePathFmt = "filepath:up/%s" +const V1FilePathFmt = "v1imagepost:%s" // ImageRepo deps. for storage type ImageRepo struct { @@ -23,13 +23,30 @@ func NewRedisImageRepo(conn *redis.Pool) *ImageRepo { } } +func fileKey(filename, V1FilePathFmt string) string { + return fmt.Sprintf(V1FilePathFmt, filename) +} + func (r *ImageRepo) AddNewFile(filename string, meta *image.PostMeta, timeout int) error { conn := r.db.Get() defer conn.Close() - key := fmt.Sprintf(V1FilePathFmt, filename) + key := fileKey(filename, V1FilePathFmt) _, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(meta)...) if err != nil { log.Fatal(err) } return err } + +func (r *ImageRepo) GetFile(fileUrl string) (*image.PostMeta, error) { + conn := r.db.Get() + defer conn.Close() + imageMeta := &image.PostMeta{} + key := fileKey(fileUrl, V1FilePathFmt) + res, err := redis.Values(conn.Do("HGETALL", key)) + if err != nil { + return &image.PostMeta{}, err + } + err = redis.ScanStruct(res, imageMeta) + return imageMeta, err +} diff --git a/internal/db/redis/redis.go b/internal/db/redis/redis.go index d769bb4..f9147be 100644 --- a/internal/db/redis/redis.go +++ b/internal/db/redis/redis.go @@ -5,7 +5,7 @@ import ( "github.com/gomodule/redigo/redis" - "gitlab.com/grumps/environ" + "go.ofmax.li/environ" ) var redisServer = environ.GetEnv("AUTH_REDIS", "localhost:6379") diff --git a/internal/fs/fs.go b/internal/fs/fs.go new file mode 100644 index 0000000..7b2704b --- /dev/null +++ b/internal/fs/fs.go @@ -0,0 +1,18 @@ +package fs + +import ( + "net/http" + "strings" + + "github.com/go-chi/chi" +) + +func NewHandler(path string) http.HandlerFunc { + filesDir := http.Dir(path) + return func(w http.ResponseWriter, r *http.Request) { + rctx := chi.RouteContext(r.Context()) + pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*") + fs := http.StripPrefix(pathPrefix, http.FileServer(filesDir)) + fs.ServeHTTP(w, r) + } +} diff --git a/internal/image/handler.go b/internal/image/handler.go index 8988a58..2db4c40 100644 --- a/internal/image/handler.go +++ b/internal/image/handler.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "log" "net/http" + + "github.com/go-chi/chi" ) var fileTypes = map[string]string{ @@ -27,9 +29,22 @@ type imageHandler struct { service Servicer } +// GetImage handler for returning an image func (h *imageHandler) GetImage(w http.ResponseWriter, r *http.Request) { - log.Print("serving image") - http.ServeFile(w, r, "foo.jpg") + fileID := chi.URLParam(r, "fileName") + fileMeta, err := h.service.GetFile(fileID) + if err != nil { + w.WriteHeader(400) + log.Printf("error: %+v", err) + w.Write([]byte("WTF Incorrect Content Type")) + } + fileUrl := fmt.Sprintf("/f/%s", fileMeta.FilePath) + data := struct { + ImageUrl string + }{ + fileUrl, + } + h.service.Render(w, "image.tmpl", data) } // PostImage handler for creating an image post @@ -39,6 +54,7 @@ func (h *imageHandler) PostImage(w http.ResponseWriter, r *http.Request) { file, handler, err := r.FormFile("file") if err != nil { log.Printf("%s", err) + return } defer file.Close() fileBytes, err := ioutil.ReadAll(file) @@ -63,9 +79,14 @@ func (h *imageHandler) PostImage(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Incorrect Content Type")) return } - fileID, err := h.service.NewID() - fileName := fmt.Sprintf("%s.%s", fileID, extension) - h.service.AddFile(fileName, fileBytes) + fileName, fileID, err := h.service.AddFile(extension, fileType, fileBytes) + if err != nil { + log.Printf("failed to write file") + w.WriteHeader(500) + w.Write([]byte("An Internal Error Occured")) + return + } w.WriteHeader(201) - w.Write([]byte("ok")) + w.Write([]byte(fmt.Sprintf("%s %s", fileName, fileID))) + return } diff --git a/internal/image/image_test.go b/internal/image/image_test.go index 4a1aea9..3425213 100644 --- a/internal/image/image_test.go +++ b/internal/image/image_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io/ioutil" + "log" "mime/multipart" "net/http" "net/http/httptest" @@ -11,6 +12,7 @@ import ( "os" "path" "path/filepath" + "runtime" "strings" "testing" @@ -20,8 +22,22 @@ import ( db "git.ofmax.li/iserv/internal/db/redis" "git.ofmax.li/iserv/internal/image" + "go.ofmax.li/tmpl" ) +var ( + projectRoot string +) + +func init() { + _, filename, _, _ := runtime.Caller(0) + projectRoot := path.Join(path.Dir(filename), "../..") + err := os.Chdir(projectRoot) + if err != nil { + panic(err) + } +} + // from go lang src https://golang.org/src/mime/multipart/writer.go // there seems to be no way to set the content type. var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") @@ -67,7 +83,7 @@ func prepareRequest(t *testing.T, filename, mimeType string) (*multipart.Writer, return writer, body, err } -func TestImage(t *testing.T) { +func TestPostImage(t *testing.T) { // setup redis for tests conn := redigomock.NewConn() pool := &redis.Pool{ @@ -75,7 +91,7 @@ func TestImage(t *testing.T) { Dial: func() (redis.Conn, error) { return conn, nil }, MaxIdle: 10, } - conn.Command("HMSET", redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData()) + conn.Command("HMSET", redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData(), redigomock.NewAnyData()) repo := db.NewRedisImageRepo(pool) // setup image package @@ -83,13 +99,20 @@ func TestImage(t *testing.T) { if err != nil { t.Fatal(err) } + cwd, _ := os.Getwd() + base := filepath.Join(cwd, "templates") + renderer, err := tmpl.NewHTMLTmpl(base) + if err != nil { + log.Fatal(err) + } defer os.RemoveAll(imageBuildDir) - imageService := image.NewService(repo, imageBuildDir) + imageService := image.NewService(repo, imageBuildDir, renderer) imageHandler := image.NewHandler(imageService) handler := http.HandlerFunc(imageHandler.PostImage) // prep test with png - writer, body, err := prepareRequest(t, "t.png", "image/png") + testImagePath := filepath.Join(projectRoot, "internal/image/t.png") + writer, body, err := prepareRequest(t, testImagePath, "image/png") if err != nil { t.Fatal(err) } @@ -107,7 +130,8 @@ func TestImage(t *testing.T) { } // incorrect mime - writer, body, err = prepareRequest(t, "t.pdf", "application/pdf") + testImagePDF := filepath.Join(projectRoot, "internal/image/t.pdf") + writer, body, err = prepareRequest(t, testImagePDF, "application/pdf") if err != nil { t.Fatal(err) } @@ -124,7 +148,7 @@ func TestImage(t *testing.T) { } // sneaky mimetype check - writer, body, err = prepareRequest(t, "t.png", "image/jpeg") + writer, body, err = prepareRequest(t, testImagePath, "image/jpeg") if err != nil { t.Fatal(err) } @@ -141,7 +165,6 @@ func TestImage(t *testing.T) { } // test image write path && ext - // TODO pngs, err := filepath.Glob(filepath.Join(imageBuildDir)) if err != nil { t.Fatal(err) @@ -149,4 +172,9 @@ func TestImage(t *testing.T) { if len(pngs) != 1 { t.Error("expected only 1 image to be found") } + +} + +func TestGetImage(t *testing.T) { + } diff --git a/internal/image/model.go b/internal/image/model.go index 002ad8a..0d1cd60 100644 --- a/internal/image/model.go +++ b/internal/image/model.go @@ -5,4 +5,5 @@ type PostMeta struct { FilePath string `redis:"file_path"` CreatedAt string `redis:"created_at"` UserID string `redis:"user_id"` + MimeType string `redis:"mime_type"` } diff --git a/internal/image/repo.go b/internal/image/repo.go index a65ad49..39f6a56 100644 --- a/internal/image/repo.go +++ b/internal/image/repo.go @@ -3,4 +3,5 @@ package image // Repo storage interface type Repo interface { AddNewFile(filename string, meta *PostMeta, timeout int) error + GetFile(fileUrl string) (*PostMeta, error) } diff --git a/internal/image/service.go b/internal/image/service.go index dabbfe7..f953bfa 100644 --- a/internal/image/service.go +++ b/internal/image/service.go @@ -1,24 +1,32 @@ package image import ( + "fmt" "io/ioutil" "log" + "net/http" "path" "time" "github.com/matoous/go-nanoid" + "github.com/pkg/errors" + + "go.ofmax.li/tmpl" ) // Servicer image management type Servicer interface { NewID() (string, error) - AddFile(filename string, fileBytes []byte) error + AddFile(extension, fileType string, fileBytes []byte) (string, string, error) + GetFile(fileUrl string) (*PostMeta, error) + Render(w http.ResponseWriter, templateName string, data interface{}) error } // NewService new image service -func NewService(repo Repo, storagePath string) *Service { +func NewService(repo Repo, storagePath string, renderer *tmpl.HTML) *Service { return &Service{repo, storagePath, + renderer, } } @@ -26,6 +34,12 @@ func NewService(repo Repo, storagePath string) *Service { type Service struct { db Repo storagePath string + tmpl *tmpl.HTML +} + +// Render renders templates +func (is *Service) Render(w http.ResponseWriter, templateName string, data interface{}) error { + return is.tmpl.Render(w, templateName, data) } // NewID create an uniqueish ID @@ -34,22 +48,38 @@ func (is *Service) NewID() (string, error) { } // AddFile writes to disk, writes meta to db -func (is *Service) AddFile(fileName string, fileBytes []byte) error { +func (is *Service) AddFile(extension, fileType string, fileBytes []byte) (string, string, error) { + fileID, err := is.NewID() + if err != nil { + return "", "", errors.Wrap(err, "generated id for fileID failed") + } + fileName := fmt.Sprintf("%s.%s", fileID, extension) filePath := path.Join(is.storagePath, fileName) if err := ioutil.WriteFile(filePath, fileBytes, 0750); err != nil { log.Fatal(err) - return err + return "", "", errors.Wrap(err, "couldn't write image file") + } + postID, err := is.NewID() + if err != nil { + return "", "", errors.Wrap(err, "generating postid for uuid") } t := time.Now().UTC() postMeta := &PostMeta{ FilePath: fileName, CreatedAt: t.Format(time.RFC3339), UserID: "1", + MimeType: fileType, } - is.db.AddNewFile(fileName, postMeta, 946080000) - if err := is.db.AddNewFile(fileName, postMeta, 946080000); err != nil { + is.db.AddNewFile(postID, postMeta, 946080000) + if err := is.db.AddNewFile(postID, postMeta, 946080000); err != nil { log.Fatal(err) - return err + return "", "", errors.Wrap(err, "couldn't write to redis") } - return nil + return fileName, postID, nil +} + +// GetFile fetch file from db interface +func (is *Service) GetFile(fileUrl string) (*PostMeta, error) { + result, err := is.db.GetFile(fileUrl) + return result, err } |