|
|
|
package fs
|
|
|
|
|
|
|
|
import (
|
|
|
|
_ "embed"
|
|
|
|
"fmt"
|
|
|
|
"github.com/veypi/OneAuth/libs/webdav"
|
|
|
|
"github.com/veypi/OneAuth/models"
|
|
|
|
"github.com/veypi/utils"
|
|
|
|
"github.com/veypi/utils/log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MountFunc
|
|
|
|
// urlPrefix urlRoot+urlPrefix 为serverHttp 匹配的url前缀
|
|
|
|
// mountPoint fs.RootDir+mountPoint 为文件存储路径
|
|
|
|
type MountFunc = func(w http.ResponseWriter, r *http.Request) (urlPrefix string, mountPoint string, ownerID string, actorID string, err error)
|
|
|
|
|
|
|
|
type FS struct {
|
|
|
|
handlerCache map[string]*webdav.Handler
|
|
|
|
RootDir string
|
|
|
|
UrlRoot string
|
|
|
|
MountFunc MountFunc
|
|
|
|
DisabledAlert bool
|
|
|
|
DisabledRecord bool
|
|
|
|
DisabledWrite bool
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:embed netErr.png
|
|
|
|
var errPng []byte
|
|
|
|
|
|
|
|
func (f *FS) mount(prefix, dir string) *webdav.Handler {
|
|
|
|
p := f.RootDir
|
|
|
|
if s, err := filepath.Abs(p); err == nil {
|
|
|
|
p = s
|
|
|
|
}
|
|
|
|
p = filepath.Join(p, dir)
|
|
|
|
ok, err := utils.PathExists(p)
|
|
|
|
if err != nil || !ok {
|
|
|
|
err = os.MkdirAll(p, 0777)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn().Msgf("create user dir failed: %s", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Debug().Msgf("mount %s", p)
|
|
|
|
fs := webdav.Dir(p)
|
|
|
|
|
|
|
|
h := &webdav.Handler{
|
|
|
|
Prefix: utils.PathJoin(f.UrlRoot, prefix),
|
|
|
|
FileSystem: fs,
|
|
|
|
LockSystem: webdav.NewMemLS(),
|
|
|
|
Logger: func(r *http.Request, err error) {
|
|
|
|
if err != nil {
|
|
|
|
msg := err.Error()
|
|
|
|
if !strings.HasSuffix(msg, "no such file or directory") && !strings.HasSuffix(msg, "is a directory") {
|
|
|
|
log.Debug().Msgf("\n%s %s %s %s", utils.CallPath(1), r.Method, r.RequestURI, msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
prefix, dir, ownerID, actorID, err := f.MountFunc(w, r)
|
|
|
|
id := fmt.Sprintf("%s_%s", ownerID, actorID)
|
|
|
|
if err != nil {
|
|
|
|
if !f.DisabledAlert {
|
|
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="davfs"`)
|
|
|
|
}
|
|
|
|
FileError(w, http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if f.handlerCache == nil {
|
|
|
|
f.handlerCache = map[string]*webdav.Handler{}
|
|
|
|
}
|
|
|
|
h := f.handlerCache[id]
|
|
|
|
if h == nil {
|
|
|
|
h = f.mount(prefix, dir)
|
|
|
|
h.OwnerID = ownerID
|
|
|
|
h.ActorID = actorID
|
|
|
|
f.handlerCache[id] = h
|
|
|
|
if h == nil {
|
|
|
|
if !f.DisabledAlert {
|
|
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="davfs"`)
|
|
|
|
}
|
|
|
|
FileError(w, http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !f.DisabledRecord {
|
|
|
|
f.setEvent(h)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *FS) setEvent(h *webdav.Handler) {
|
|
|
|
// 记录下载历史
|
|
|
|
h.On(webdav.EventAfterRead, webdav.ReadFunc(func(r *http.Request, path string) (int, error) {
|
|
|
|
defer handlePanic()
|
|
|
|
tf, err := getFile(r.Context(), path, h)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
err = addHistory(r, h, models.ActGet, tf.ID(), path)
|
|
|
|
return 0, err
|
|
|
|
}))
|
|
|
|
h.On(webdav.EventAfterUpdate, webdav.AfterUpdateFunc(func(r *http.Request, path string, size int64, md5Str string) (int, error) {
|
|
|
|
defer handlePanic()
|
|
|
|
tf, err := getFile(r.Context(), path, h)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
var delta = size - int64(tf.Size)
|
|
|
|
err = updateFile(r.Context(), tf.ID(), map[string]interface{}{"Size": size, "MD5": md5Str})
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
uid, _ := strconv.Atoi(h.OwnerID)
|
|
|
|
err = updateUserSize(uint(uid), delta)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return 0, addHistory(r, h, models.ActPut, tf.ID(), path)
|
|
|
|
}))
|
|
|
|
|
|
|
|
h.On(webdav.EventAfterDelete, webdav.DeleteFunc(func(r *http.Request, path string) (int, error) {
|
|
|
|
defer handlePanic()
|
|
|
|
return 0, removeFile(r, path, h)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func FileError(w http.ResponseWriter, code int) {
|
|
|
|
w.Header().Set("Content-Type", "image/png")
|
|
|
|
w.WriteHeader(code)
|
|
|
|
w.Write(errPng)
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlePanic() {
|
|
|
|
if e := recover(); e != nil {
|
|
|
|
log.Warn().Msgf("%v", e)
|
|
|
|
}
|
|
|
|
}
|