feat: some more updates
License: MIT Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
parent
40b4e2e678
commit
0133b7e73e
@ -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,13 +71,15 @@ func NewFileInfo(fs afero.Fs, path string, modify bool, checker rules.Checker) (
|
|||||||
Extension: filepath.Ext(info.Name()),
|
Extension: filepath.Ext(info.Name()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.IsDir {
|
if opts.Expand {
|
||||||
return file, file.readListing(checker)
|
if file.IsDir {
|
||||||
}
|
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
|
||||||
|
|||||||
2
frontend
2
frontend
@ -1 +1 @@
|
|||||||
Subproject commit a71029771582dbc85baa5a4eb984d800311b01c5
|
Subproject commit a53f11bb786b409a1320866113276acbddbf97ac
|
||||||
@ -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
50
http/public.go
Normal 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)
|
||||||
|
})
|
||||||
15
http/raw.go
15
http/raw.go
@ -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,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user