package megauploader //go:generate rice embed-go // this file contains all the web-related code for MegaUploader import ( "encoding/json" "fmt" "net/http" "os" "path" "path/filepath" "strings" rice "github.com/GeertJohan/go.rice" "github.com/c2h5oh/datasize" ) // MegaUploader acts as controller for all the application. Since this is inherently a web-focused // application, it will include http utils type MegaUploader struct { Conf Config } // SetupRoutes adds API routes func (mu *MegaUploader) SetupRoutes() { http.HandleFunc("/", requireUserMiddleware(mu.home)) http.HandleFunc("/upload/", requireUserMiddleware(mu.uploadUI)) static := rice.MustFindBox("res/static") http.HandleFunc("/static/", requireUserMiddleware( http.StripPrefix("/static/", http.FileServer(static.HTTPBox())).ServeHTTP, )) http.HandleFunc("/api/share", requireUserMiddleware(mu.listShares)) http.HandleFunc("/api/share/", requireUserMiddleware(mu.getShare)) http.HandleFunc("/api/upload/", requireUserMiddleware(mu.upload)) } func getUser(r *http.Request) (string, error) { user := r.Header.Get("X-Forwarded-User") if user == "" { return "", fmt.Errorf("User not set") } return user, nil } func requireUserMiddleware(inner func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { _, err := getUser(r) if err != nil { http.Error(w, "Only logged-in users are authorized", http.StatusUnauthorized) return } inner(w, r) } } func (mu *MegaUploader) listShares(w http.ResponseWriter, r *http.Request) { user, _ := getUser(r) // user is set: checked by middleware shares := mu.Conf.GetAuthShares(user) serialized, err := json.Marshal(shares) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Set("Content-type", "application/json") w.Write(serialized) } func (mu *MegaUploader) getShare(w http.ResponseWriter, r *http.Request) { sharename := r.URL.Path[strings.LastIndexByte(r.URL.Path, '/')+1:] share, err := mu.Conf.GetShare(sharename) if err != nil { http.Error(w, fmt.Sprintf("Share '%s' not found: %s", sharename, err), http.StatusNotFound) return } user, _ := getUser(r) // user is set: checked by middleware if !share.IsAuthorized(user) { http.Error(w, "You are not authorized to this share", http.StatusForbidden) return } serialized, err := json.Marshal(share) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Set("Content-type", "application/json") w.Write(serialized) } // on success, redirect to view URL func (mu *MegaUploader) upload(w http.ResponseWriter, r *http.Request) { user, _ := getUser(r) // user is set: checked by middleware sharename := r.URL.Path[strings.LastIndexByte(r.URL.Path, '/')+1:] share, err := mu.Conf.GetAuthShare(sharename, user) if err != nil { http.Error(w, fmt.Sprintf("Share '%s' not found: %s", sharename, err), http.StatusNotFound) return } sizelimit := uint64(20 * datasize.MB) if share.SizeLimit.Bytes() > 0 { sizelimit = share.SizeLimit.Bytes() } err = r.ParseMultipartForm(int64(sizelimit)) if err != nil { http.Error(w, "Bad request", http.StatusBadRequest) return } file, header, err := r.FormFile("file") if err != nil { http.Error(w, "No file uploaded", http.StatusBadRequest) return } fname, err := share.Upload(file, filepath.Base(header.Filename)) if err != nil { fmt.Fprintln(os.Stderr, err) http.Error(w, "Error uploading", http.StatusInternalServerError) return } fname = filepath.Base(fname) u, err := mu.Conf.GetShareURL(sharename) if err != nil { w.Write([]byte(fname)) return } u.Path = path.Join(u.Path, fname) w.Write([]byte(u.String())) }