feat: some more updates

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
Henrique Dias 2019-01-05 09:41:09 +00:00
parent 40b4e2e678
commit 0133b7e73e
11 changed files with 162 additions and 59 deletions

View File

@ -38,22 +38,31 @@ type FileInfo struct {
Checksums map[string]string `json:"checksums,omitempty"` Checksums map[string]string `json:"checksums,omitempty"`
} }
// FileOptions are the options when getting a file info.
type FileOptions struct {
Fs afero.Fs
Path string
Modify bool
Expand bool
Checker rules.Checker
}
// NewFileInfo creates a File object from a path and a given user. This File // NewFileInfo creates a File object from a path and a given user. This File
// object will be automatically filled depending on if it is a directory // object will be automatically filled depending on if it is a directory
// or a file. If it's a video file, it will also detect any subtitles. // or a file. If it's a video file, it will also detect any subtitles.
func NewFileInfo(fs afero.Fs, path string, modify bool, checker rules.Checker) (*FileInfo, error) { func NewFileInfo(opts FileOptions) (*FileInfo, error) {
if !checker.Check(path) { if !opts.Checker.Check(opts.Path) {
return nil, os.ErrPermission return nil, os.ErrPermission
} }
info, err := fs.Stat(path) info, err := opts.Fs.Stat(opts.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
file := &FileInfo{ file := &FileInfo{
Fs: fs, Fs: opts.Fs,
Path: path, Path: opts.Path,
Name: info.Name(), Name: info.Name(),
ModTime: info.ModTime(), ModTime: info.ModTime(),
Mode: info.Mode(), Mode: info.Mode(),
@ -62,14 +71,16 @@ func NewFileInfo(fs afero.Fs, path string, modify bool, checker rules.Checker) (
Extension: filepath.Ext(info.Name()), Extension: filepath.Ext(info.Name()),
} }
if opts.Expand {
if file.IsDir { if file.IsDir {
return file, file.readListing(checker) return file, file.readListing(opts.Checker)
} }
err = file.detectType(modify) err = file.detectType(opts.Modify)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return file, err return file, err
} }

@ -1 +1 @@
Subproject commit a71029771582dbc85baa5a4eb984d800311b01c5 Subproject commit a53f11bb786b409a1320866113276acbddbf97ac

View File

@ -39,7 +39,7 @@ func NewHandler(storage *storage.Storage) (http.Handler, error) {
api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("PUT") api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("PUT")
api.PathPrefix("/resources").Handler(handle(resourcePatchHandler, "/api/resources", storage)).Methods("PATCH") api.PathPrefix("/resources").Handler(handle(resourcePatchHandler, "/api/resources", storage)).Methods("PATCH")
api.PathPrefix("/share").Handler(handle(shareGetHandler, "/api/share", storage)).Methods("GET") api.PathPrefix("/share").Handler(handle(shareGetsHandler, "/api/share", storage)).Methods("GET")
api.PathPrefix("/share").Handler(handle(sharePostHandler, "/api/share", storage)).Methods("POST") api.PathPrefix("/share").Handler(handle(sharePostHandler, "/api/share", storage)).Methods("POST")
api.PathPrefix("/share").Handler(handle(shareDeleteHandler, "/api/share", storage)).Methods("DELETE") api.PathPrefix("/share").Handler(handle(shareDeleteHandler, "/api/share", storage)).Methods("DELETE")
@ -50,7 +50,9 @@ func NewHandler(storage *storage.Storage) (http.Handler, error) {
api.PathPrefix("/command").Handler(handle(commandsHandler, "/api/command", storage)).Methods("GET") api.PathPrefix("/command").Handler(handle(commandsHandler, "/api/command", storage)).Methods("GET")
api.PathPrefix("/search").Handler(handle(searchHandler, "/api/search", storage)).Methods("GET") api.PathPrefix("/search").Handler(handle(searchHandler, "/api/search", storage)).Methods("GET")
public := api.PathPrefix("/public").Subrouter()
public.PathPrefix("/dl").Handler(handle(publicDlHandler, "/api/public/dl/", storage)).Methods("GET")
public.PathPrefix("/share").Handler(handle(publicShareHandler, "/api/public/share/", storage)).Methods("GET")
return r, nil return r, nil
} }

50
http/public.go Normal file
View File

@ -0,0 +1,50 @@
package http
import (
"net/http"
"github.com/filebrowser/filebrowser/files"
)
var withHashFile = func(fn handleFunc) handleFunc {
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
link, err := d.store.Share.GetByHash(r.URL.Path)
if err != nil {
return errToStatus(err), err
}
user, err := d.store.Users.Get(link.UserID)
if err != nil {
return errToStatus(err), err
}
d.user = user
file, err := files.NewFileInfo(files.FileOptions{
Fs: d.user.Fs,
Path: link.Path,
Modify: d.user.Perm.Modify,
Expand: false,
Checker: d,
})
if err != nil {
return errToStatus(err), err
}
d.raw = file
return fn(w, r, d)
}
}
var publicShareHandler = withHashFile(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
return renderJSON(w, r, d.raw)
})
var publicDlHandler = withHashFile(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
file := d.raw.(*files.FileInfo)
if !file.IsDir {
return rawFileHandler(w, r, file)
}
return rawDirHandler(w, r, d, file)
})

View File

@ -2,6 +2,7 @@ package http
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"path/filepath" "path/filepath"
@ -60,7 +61,13 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
return http.StatusAccepted, nil return http.StatusAccepted, nil
} }
file, err := files.NewFileInfo(d.user.Fs, r.URL.Path, d.user.Perm.Modify, d) file, err := files.NewFileInfo(files.FileOptions{
Fs: d.user.Fs,
Path: r.URL.Path,
Modify: d.user.Perm.Modify,
Expand: false,
Checker: d,
})
if err != nil { if err != nil {
return errToStatus(err), err return errToStatus(err), err
} }
@ -73,6 +80,9 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
}) })
func addFile(ar archiver.Writer, d *data, path string) error { func addFile(ar archiver.Writer, d *data, path string) error {
// Checks are always done with paths with "/" as path separator.
path = strings.Replace(path, "\\", "/", -1)
fmt.Println(path)
if !d.Check(path) { if !d.Check(path) {
return nil return nil
} }
@ -82,7 +92,6 @@ func addFile(ar archiver.Writer, d *data, path string) error {
return err return err
} }
// open the file
file, err := d.user.Fs.Open(path) file, err := d.user.Fs.Open(path)
if err != nil { if err != nil {
return err return err
@ -92,7 +101,7 @@ func addFile(ar archiver.Writer, d *data, path string) error {
err = ar.Write(archiver.File{ err = ar.Write(archiver.File{
FileInfo: archiver.FileInfo{ FileInfo: archiver.FileInfo{
FileInfo: info, FileInfo: info,
CustomName: path, CustomName: strings.TrimPrefix(path, "/"),
}, },
ReadCloser: file, ReadCloser: file,
}) })

View File

@ -16,7 +16,13 @@ import (
) )
var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
file, err := files.NewFileInfo(d.user.Fs, r.URL.Path, d.user.Perm.Modify, d) file, err := files.NewFileInfo(files.FileOptions{
Fs: d.user.Fs,
Path: r.URL.Path,
Modify: d.user.Perm.Modify,
Expand: true,
Checker: d,
})
if err != nil { if err != nil {
return errToStatus(err), err return errToStatus(err), err
} }

View File

@ -22,8 +22,8 @@ func withPermShare(fn handleFunc) handleFunc {
}) })
} }
var shareGetHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var shareGetsHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
s, err := d.store.Share.Gets(r.URL.Path) s, err := d.store.Share.Gets(r.URL.Path, d.user.ID)
if err == errors.ErrNotExist { if err == errors.ErrNotExist {
return renderJSON(w, r, []*share.Link{}) return renderJSON(w, r, []*share.Link{})
} }
@ -32,13 +32,6 @@ var shareGetHandler = withPermShare(func(w http.ResponseWriter, r *http.Request,
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
for i, link := range s {
if link.Expires && link.ExpireDate.Before(time.Now()) {
d.store.Share.Delete(link.Hash)
s = append(s[:i], s[i+1:]...)
}
}
return renderJSON(w, r, s) return renderJSON(w, r, s)
}) })
@ -56,12 +49,12 @@ var shareDeleteHandler = withPermShare(func(w http.ResponseWriter, r *http.Reque
var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
var s *share.Link var s *share.Link
expire := r.URL.Query().Get("expires") rawExpire := r.URL.Query().Get("expires")
unit := r.URL.Query().Get("unit") unit := r.URL.Query().Get("unit")
if expire == "" { if rawExpire == "" {
var err error var err error
s, err = d.store.Share.GetPermanent(r.URL.Path) s, err = d.store.Share.GetPermanent(r.URL.Path, d.user.ID)
if err == nil { if err == nil {
w.Write([]byte(d.settings.BaseURL + "/share/" + s.Hash)) w.Write([]byte(d.settings.BaseURL + "/share/" + s.Hash))
return 0, nil return 0, nil
@ -76,14 +69,10 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
str := base64.URLEncoding.EncodeToString(bytes) str := base64.URLEncoding.EncodeToString(bytes)
s = &share.Link{ var expire int64 = 0
Path: r.URL.Path,
Hash: str,
Expires: expire != "",
}
if expire != "" { if rawExpire != "" {
num, err := strconv.Atoi(expire) num, err := strconv.Atoi(rawExpire)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
@ -100,7 +89,14 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
add = time.Hour * time.Duration(num) add = time.Hour * time.Duration(num)
} }
s.ExpireDate = time.Now().Add(add) expire = time.Now().Add(add).Unix()
}
s = &share.Link{
Path: r.URL.Path,
Hash: str,
Expire: expire,
UserID: d.user.ID,
} }
if err := d.store.Share.Save(s); err != nil { if err := d.store.Share.Save(s); err != nil {

View File

@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"os" "os"
"github.com/filebrowser/filebrowser/errors"
) )
func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) (int, error) { func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) (int, error) {
@ -27,9 +29,9 @@ func errToStatus(err error) int {
return http.StatusOK return http.StatusOK
case os.IsPermission(err): case os.IsPermission(err):
return http.StatusForbidden return http.StatusForbidden
case os.IsNotExist(err): case os.IsNotExist(err), err == errors.ErrNotExist:
return http.StatusNotFound return http.StatusNotFound
case os.IsExist(err): case os.IsExist(err), err == errors.ErrExist:
return http.StatusConflict return http.StatusConflict
default: default:
return http.StatusInternalServerError return http.StatusInternalServerError

View File

@ -1,11 +1,9 @@
package share package share
import "time"
// Link is the information needed to build a shareable link. // Link is the information needed to build a shareable link.
type Link struct { type Link struct {
Hash string `json:"hash" storm:"id,index"` Hash string `json:"hash" storm:"id,index"`
Path string `json:"path" storm:"index"` Path string `json:"path" storm:"index"`
Expires bool `json:"expires"` UserID uint `json:"userID"`
ExpireDate time.Time `json:"expireDate"` Expire int64 `json:"expire"`
} }

View File

@ -1,10 +1,16 @@
package share package share
import (
"time"
"github.com/filebrowser/filebrowser/errors"
)
// StorageBackend is the interface to implement for a share storage. // StorageBackend is the interface to implement for a share storage.
type StorageBackend interface { type StorageBackend interface {
GetByHash(hash string) (*Link, error) GetByHash(hash string) (*Link, error)
GetPermanent(path string) (*Link, error) GetPermanent(path string, id uint) (*Link, error)
Gets(path string) ([]*Link, error) Gets(path string, id uint) ([]*Link, error)
Save(s *Link) error Save(s *Link) error
Delete(hash string) error Delete(hash string) error
} }
@ -21,17 +27,40 @@ func NewStorage(back StorageBackend) *Storage {
// GetByHash wraps a StorageBackend.GetByHash. // GetByHash wraps a StorageBackend.GetByHash.
func (s *Storage) GetByHash(hash string) (*Link, error) { func (s *Storage) GetByHash(hash string) (*Link, error) {
return s.back.GetByHash(hash) link, err := s.back.GetByHash(hash)
if err != nil {
return nil, err
}
if link.Expire != 0 && link.Expire <= time.Now().Unix() {
s.Delete(link.Hash)
return nil, errors.ErrNotExist
}
return link, nil
} }
// GetPermanent wraps a StorageBackend.GetPermanent // GetPermanent wraps a StorageBackend.GetPermanent
func (s *Storage) GetPermanent(path string) (*Link, error) { func (s *Storage) GetPermanent(path string, id uint) (*Link, error) {
return s.back.GetPermanent(path) return s.back.GetPermanent(path, id)
} }
// Gets wraps a StorageBackend.Gets // Gets wraps a StorageBackend.Gets
func (s *Storage) Gets(path string) ([]*Link, error) { func (s *Storage) Gets(path string, id uint) ([]*Link, error) {
return s.back.Gets(path) links, err := s.back.Gets(path, id)
if err != nil {
return nil, err
}
for i, link := range links {
if link.Expire != 0 && link.Expire <= time.Now().Unix() {
s.Delete(link.Hash)
links = append(links[:i], links[i+1:]...)
}
}
return links, nil
} }
// Save wraps a StorageBackend.Save // Save wraps a StorageBackend.Save

View File

@ -21,9 +21,9 @@ func (s shareBackend) GetByHash(hash string) (*share.Link, error) {
return &v, err return &v, err
} }
func (s shareBackend) GetPermanent(path string) (*share.Link, error) { func (s shareBackend) GetPermanent(path string, id uint) (*share.Link, error) {
var v share.Link var v share.Link
err := s.db.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v) err := s.db.Select(q.Eq("Path", path), q.Eq("Expire", 0), q.Eq("UserID", id)).First(&v)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return nil, errors.ErrNotExist return nil, errors.ErrNotExist
} }
@ -31,9 +31,9 @@ func (s shareBackend) GetPermanent(path string) (*share.Link, error) {
return &v, err return &v, err
} }
func (s shareBackend) Gets(hash string) ([]*share.Link, error) { func (s shareBackend) Gets(path string, id uint) ([]*share.Link, error) {
var v []*share.Link var v []*share.Link
err := s.db.Find("Path", hash, &v) err := s.db.Select(q.Eq("Path", path), q.Eq("UserID", id)).Find(&v)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return v, errors.ErrNotExist return v, errors.ErrNotExist
} }