summaryrefslogtreecommitdiff
path: root/server/server.go
diff options
context:
space:
mode:
authorhorus_arch2015-04-19 22:09:52 +0200
committerhorus_arch2015-04-19 22:09:52 +0200
commit01e9a34952bd6ddd383680b0ca2312e476ad07a6 (patch)
tree00902575e5c271cc5d35ea65aa8795b8caeb97bc /server/server.go
downloadmandible-01e9a34952bd6ddd383680b0ca2312e476ad07a6.tar.gz
Initial commit.
Diffstat (limited to 'server/server.go')
-rw-r--r--server/server.go307
1 files changed, 307 insertions, 0 deletions
diff --git a/server/server.go b/server/server.go
new file mode 100644
index 0000000..4cfafe7
--- /dev/null
+++ b/server/server.go
@@ -0,0 +1,307 @@
+package server
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+
+ "mandible/config"
+ "mandible/imageprocessor"
+ "mandible/imagestore"
+ "mandible/uploadedfile"
+)
+
+var mainTempl = template.Must(template.New("global").ParseGlob("templates/*.html"))
+
+type Server struct {
+ Config *config.Configuration
+ HTTPClient *http.Client
+ imageStore imagestore.ImageStore
+ hashGenerator *imagestore.HashGenerator
+}
+
+func CreateServer(c *config.Configuration) *Server {
+ factory := imagestore.NewFactory(c)
+ httpclient := &http.Client{}
+ stores := factory.NewImageStores()
+ store := stores[0]
+
+ hashGenerator := factory.NewHashGenerator(store)
+ return &Server{c, httpclient, store, hashGenerator}
+}
+
+func (s *Server) uploadFile(uploadFile io.Reader, w http.ResponseWriter, fileName string, thumbs []*uploadedfile.ThumbFile) {
+ w.Header().Set("Content-Type", "application/json")
+
+ tmpFile, err := ioutil.TempFile(os.TempDir(), "image")
+ if err != nil {
+ fmt.Println(err)
+ ErrorResponse(w, "Unable to write to /tmp", http.StatusInternalServerError)
+ return
+ }
+
+ defer tmpFile.Close()
+
+ _, err = io.Copy(tmpFile, uploadFile)
+
+ if err != nil {
+ fmt.Println(err)
+ ErrorResponse(w, "Unable to copy image to disk!", http.StatusInternalServerError)
+ return
+ }
+
+ upload, err := uploadedfile.NewUploadedFile(fileName, tmpFile.Name(), thumbs)
+ defer upload.Clean()
+
+ if err != nil {
+ ErrorResponse(w, "Error detecting mime type!", http.StatusInternalServerError)
+ return
+ }
+
+ processor, err := imageprocessor.Factory(s.Config.MaxFileSize, upload)
+ if err != nil {
+ ErrorResponse(w, "Unable to process image!", http.StatusInternalServerError)
+ return
+ }
+
+ err = processor.Run(upload)
+ if err != nil {
+ ErrorResponse(w, "Unable to process image!", http.StatusInternalServerError)
+ return
+ }
+
+ upload.SetHash(s.hashGenerator.Get())
+
+ factory := imagestore.NewFactory(s.Config)
+ obj := factory.NewStoreObject(upload.GetHash(), upload.GetMime(), "original")
+ obj, err = s.imageStore.Save(upload.GetPath(), obj)
+ if err != nil {
+ ErrorResponse(w, "Unable to save image!", http.StatusInternalServerError)
+ return
+ }
+
+ thumbsResp := map[string]interface{}{}
+ for _, t := range upload.GetThumbs() {
+ thumbName := fmt.Sprintf("%s/%s", upload.GetHash(), t.GetName())
+ tObj := factory.NewStoreObject(thumbName, upload.GetMime(), "t")
+ tObj, err = s.imageStore.Save(t.GetPath(), tObj)
+ if err != nil {
+ ErrorResponse(w, "Unable to save thumbnail!", http.StatusInternalServerError)
+ return
+ }
+
+ thumbsResp[t.GetName()] = tObj.Url
+ }
+
+ size, err := upload.FileSize()
+ if err != nil {
+ ErrorResponse(w, "Unable to fetch image metadata!", http.StatusInternalServerError)
+ return
+ }
+
+ width, height, err := upload.Dimensions()
+
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ resp := map[string]interface{}{
+ "link": obj.Url,
+ "mime": obj.MimeType,
+ "name": fileName,
+ "size": size,
+ "width": width,
+ "height": height,
+ "thumbs": thumbsResp,
+ }
+
+ Response(w, resp)
+}
+
+func (s *Server) Start() {
+ fileHandler := func(w http.ResponseWriter, r *http.Request) {
+ uploadFile, header, err := r.FormFile("image")
+
+ if err != nil {
+ fmt.Println(err)
+ ErrorResponse(w, "Error processing file!", http.StatusInternalServerError)
+ return
+ }
+
+ thumbs, err := parseThumbs(r)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ s.uploadFile(uploadFile, w, header.Filename, thumbs)
+ uploadFile.Close()
+ }
+
+ urlHandler := func(w http.ResponseWriter, r *http.Request) {
+ uploadFile, err := s.download(r.FormValue("image"))
+
+ if err != nil {
+ ErrorResponse(w, "Error dowloading URL!", http.StatusInternalServerError)
+ return
+ }
+
+ thumbs, err := parseThumbs(r)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ s.uploadFile(uploadFile, w, "", thumbs)
+ uploadFile.Close()
+ }
+
+ base64Handler := func(w http.ResponseWriter, r *http.Request) {
+ b64data := r.FormValue("image")
+
+ uploadFile := base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64data))
+
+ thumbs, err := parseThumbs(r)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ s.uploadFile(uploadFile, w, "", thumbs)
+ }
+
+ rootHandler := func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ errorHandler(w, r, http.StatusNotFound)
+ return
+ }
+ templ := mainTempl.Lookup("index.html")
+ err := templ.ExecuteTemplate(w, "index.html", nil)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ apiHandler := func(w http.ResponseWriter, r *http.Request) {
+ templ := mainTempl.Lookup("api.html")
+ err := templ.ExecuteTemplate(w, "api.html", nil)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ cliHandler := func(w http.ResponseWriter, r *http.Request) {
+ templ := mainTempl.Lookup("cli.html")
+ err := templ.ExecuteTemplate(w, "cli.html", nil)
+ if err != nil {
+ ErrorResponse(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ }
+
+ http.HandleFunc("/cli", cliHandler)
+ http.HandleFunc("/api", apiHandler)
+
+ http.HandleFunc("/api/v1/file", fileHandler)
+ http.HandleFunc("/api/v1/url", urlHandler)
+ http.HandleFunc("/api/v1/base64", base64Handler)
+
+ staticDir := os.Getenv("STATIC_DIR")
+ if staticDir == "" {
+ log.Panic("No directory given for static assets.")
+ }
+ http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
+ http.Handle("/i/", http.StripPrefix("/i/", http.FileServer(http.Dir(staticDir+"/../files/i/"))))
+ http.HandleFunc("/", rootHandler)
+ http.HandleFunc("/health", healthHandler)
+
+ port := ":" + os.Getenv("PORT")
+ if port == ":" {
+ port = fmt.Sprintf(":%d", s.Config.Port)
+ }
+
+ http.ListenAndServe(port, nil)
+}
+
+func (s *Server) download(url string) (io.ReadCloser, error) {
+ req, err := http.NewRequest("GET", url, nil)
+
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Add("User-Agent", s.Config.UserAgent)
+
+ resp, err := s.HTTPClient.Do(req)
+
+ if err != nil {
+ // "HTTP protocol error" - maybe the server sent an invalid response or timed out
+ return nil, err
+ }
+
+ if 200 != resp.StatusCode {
+ return nil, errors.New("Non-200 status code received")
+ }
+
+ contentLength := resp.ContentLength
+
+ if contentLength == 0 {
+ return nil, errors.New("Empty file received")
+ }
+
+ return resp.Body, nil
+}
+
+func parseThumbs(r *http.Request) ([]*uploadedfile.ThumbFile, error) {
+ thumbString := r.FormValue("thumbs")
+ if thumbString == "" {
+ return []*uploadedfile.ThumbFile{}, nil
+ }
+
+ var t map[string]map[string]interface{}
+ err := json.Unmarshal([]byte(thumbString), &t)
+ if err != nil {
+ return nil, errors.New("Error parsing thumbnail JSON!")
+ }
+
+ var thumbs []*uploadedfile.ThumbFile
+ for name, thumb := range t {
+ width, wOk := thumb["width"].(float64)
+ if !wOk {
+ return nil, errors.New("Invalid thumbnail width!")
+ }
+
+ height, hOk := thumb["height"].(float64)
+ if !hOk {
+ return nil, errors.New("Invalid thumbnail height!")
+ }
+
+ shape, sOk := thumb["shape"].(string)
+ if !sOk {
+ return nil, errors.New("Invalid thumbnail shape!")
+ }
+
+ switch shape {
+ case "thumb":
+ case "square":
+ case "circle":
+ default:
+ return nil, errors.New("Invalid thumbnail shape!")
+ }
+
+ thumbs = append(thumbs, uploadedfile.NewThumbFile(int(width), int(height), name, shape, ""))
+ }
+
+ return thumbs, nil
+}