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"`
}
// 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
// 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.
func NewFileInfo(fs afero.Fs, path string, modify bool, checker rules.Checker) (*FileInfo, error) {
if !checker.Check(path) {
func NewFileInfo(opts FileOptions) (*FileInfo, error) {
if !opts.Checker.Check(opts.Path) {
return nil, os.ErrPermission
}
info, err := fs.Stat(path)
info, err := opts.Fs.Stat(opts.Path)
if err != nil {
return nil, err
}
file := &FileInfo{
Fs: fs,
Path: path,
Fs: opts.Fs,
Path: opts.Path,
Name: info.Name(),
ModTime: info.ModTime(),
Mode: info.Mode(),
@ -62,13 +71,15 @@ func NewFileInfo(fs afero.Fs, path string, modify bool, checker rules.Checker) (
Extension: filepath.Ext(info.Name()),
}
if file.IsDir {
return file, file.readListing(checker)
}
if opts.Expand {
if file.IsDir {
return file, file.readListing(opts.Checker)
}
err = file.detectType(modify)
if err != nil {
return nil, err
err = file.detectType(opts.Modify)
if err != nil {
return nil, 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(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(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("/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
}

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 (
"errors"
"fmt"
"net/http"
"net/url"
"path/filepath"
@ -60,7 +61,13 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
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 {
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 {
// Checks are always done with paths with "/" as path separator.
path = strings.Replace(path, "\\", "/", -1)
fmt.Println(path)
if !d.Check(path) {
return nil
}
@ -82,7 +92,6 @@ func addFile(ar archiver.Writer, d *data, path string) error {
return err
}
// open the file
file, err := d.user.Fs.Open(path)
if err != nil {
return err
@ -92,7 +101,7 @@ func addFile(ar archiver.Writer, d *data, path string) error {
err = ar.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: path,
CustomName: strings.TrimPrefix(path, "/"),
},
ReadCloser: file,
})

View File

@ -16,7 +16,13 @@ import (
)
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 {
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) {
s, err := d.store.Share.Gets(r.URL.Path)
var shareGetsHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
s, err := d.store.Share.Gets(r.URL.Path, d.user.ID)
if err == errors.ErrNotExist {
return renderJSON(w, r, []*share.Link{})
}
@ -32,13 +32,6 @@ var shareGetHandler = withPermShare(func(w http.ResponseWriter, r *http.Request,
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)
})
@ -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 s *share.Link
expire := r.URL.Query().Get("expires")
rawExpire := r.URL.Query().Get("expires")
unit := r.URL.Query().Get("unit")
if expire == "" {
if rawExpire == "" {
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 {
w.Write([]byte(d.settings.BaseURL + "/share/" + s.Hash))
return 0, nil
@ -76,14 +69,10 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
str := base64.URLEncoding.EncodeToString(bytes)
s = &share.Link{
Path: r.URL.Path,
Hash: str,
Expires: expire != "",
}
var expire int64 = 0
if expire != "" {
num, err := strconv.Atoi(expire)
if rawExpire != "" {
num, err := strconv.Atoi(rawExpire)
if err != nil {
return http.StatusInternalServerError, err
}
@ -100,7 +89,14 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
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 {

View File

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

View File

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

View File

@ -1,10 +1,16 @@
package share
import (
"time"
"github.com/filebrowser/filebrowser/errors"
)
// StorageBackend is the interface to implement for a share storage.
type StorageBackend interface {
GetByHash(hash string) (*Link, error)
GetPermanent(path string) (*Link, error)
Gets(path string) ([]*Link, error)
GetPermanent(path string, id uint) (*Link, error)
Gets(path string, id uint) ([]*Link, error)
Save(s *Link) error
Delete(hash string) error
}
@ -21,17 +27,40 @@ func NewStorage(back StorageBackend) *Storage {
// GetByHash wraps a StorageBackend.GetByHash.
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
func (s *Storage) GetPermanent(path string) (*Link, error) {
return s.back.GetPermanent(path)
func (s *Storage) GetPermanent(path string, id uint) (*Link, error) {
return s.back.GetPermanent(path, id)
}
// Gets wraps a StorageBackend.Gets
func (s *Storage) Gets(path string) ([]*Link, error) {
return s.back.Gets(path)
func (s *Storage) Gets(path string, id uint) ([]*Link, error) {
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

View File

@ -21,9 +21,9 @@ func (s shareBackend) GetByHash(hash string) (*share.Link, error) {
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
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 {
return nil, errors.ErrNotExist
}
@ -31,9 +31,9 @@ func (s shareBackend) GetPermanent(path string) (*share.Link, error) {
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
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 {
return v, errors.ErrNotExist
}