mirror of
				https://github.com/mickael-kerjean/filestash.git
				synced 2025-11-01 02:43:35 +08:00 
			
		
		
		
	feature (webdav): shared link is a fully fledge webdav server
This commit is contained in:
		| @ -1,10 +1,14 @@ | |||||||
| package common | package common | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"io/ioutil" |  | ||||||
| 	"time" | 	"time" | ||||||
|  | 	"log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	LOG_INFO = "INFO" | ||||||
|  | 	LOG_WARNING = "WARNING" | ||||||
|  | 	LOG_ERROR = "ERROR" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type LogEntry struct { | type LogEntry struct { | ||||||
| @ -23,7 +27,34 @@ type LogEntry struct { | |||||||
| 	Backend    string    `json:"backend"` | 	Backend    string    `json:"backend"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func Debug_reader(r io.Reader) { | func Log(ctx *App, str string, level string){ | ||||||
| 	a, _ := ioutil.ReadAll(r) | 	if ctx.Config.Log.Enable == false { | ||||||
| 	fmt.Println("> DEBUG:", string(a)) | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	shouldDisplay := func(r string, l string) bool { | ||||||
|  | 		levels := []string{"DEBUG", "INFO", "WARNING", "ERROR"} | ||||||
|  |  | ||||||
|  | 		configLevel := -1 | ||||||
|  | 		currentLevel := 0 | ||||||
|  |  | ||||||
|  | 		for i:=0; i <= len(levels); i++ { | ||||||
|  | 			if levels[i] == l { | ||||||
|  | 				currentLevel = i | ||||||
|  | 			} | ||||||
|  | 			if levels[i] == r { | ||||||
|  | 				configLevel = i | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if currentLevel <= configLevel { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	}(ctx.Config.Log.Level, level) | ||||||
|  |  | ||||||
|  | 	if shouldDisplay { | ||||||
|  | 		log.Printf("%s %s\n", level, str) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								server/ctrl/static.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								server/ctrl/static.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | package ctrl | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	. "github.com/mickael-kerjean/nuage/server/common" | ||||||
|  | 	"mime" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func StaticHandler(_path string, ctx App) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { | ||||||
|  | 		header := res.Header() | ||||||
|  | 		header.Set("Content-Type", mime.TypeByExtension(filepath.Ext(req.URL.Path))) | ||||||
|  | 		header.Set("Cache-Control", "max-age=2592000") | ||||||
|  | 		SecureHeader(&header) | ||||||
|  |  | ||||||
|  | 		if strings.HasSuffix(req.URL.Path, "/") { | ||||||
|  | 			http.NotFound(res, req) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		absPath := ctx.Helpers.AbsolutePath(_path) | ||||||
|  | 		fsrv := http.FileServer(http.Dir(absPath)) | ||||||
|  | 		_, err := os.Open(path.Join(absPath, req.URL.Path+".gz")) | ||||||
|  | 		if err == nil && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { | ||||||
|  | 			res.Header().Set("Content-Encoding", "gzip") | ||||||
|  | 			req.URL.Path += ".gz" | ||||||
|  | 		} | ||||||
|  | 		fsrv.ServeHTTP(res, req) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func DefaultHandler(_path string, ctx App) http.Handler { | ||||||
|  | 	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { | ||||||
|  | 		if req.Method != "GET" { | ||||||
|  | 			http.Error(res, "Invalid request method.", 405) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		header := res.Header() | ||||||
|  | 		header.Set("Content-Type", "text/html") | ||||||
|  | 		SecureHeader(&header) | ||||||
|  |  | ||||||
|  | 		p := _path | ||||||
|  | 		if _, err := os.Open(path.Join(ctx.Config.Runtime.Dirname, p+".gz")); err == nil && strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { | ||||||
|  | 			res.Header().Set("Content-Encoding", "gzip") | ||||||
|  | 			p += ".gz" | ||||||
|  | 		} | ||||||
|  | 		http.ServeFile(res, req, ctx.Helpers.AbsolutePath(p)) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func SecureHeader(header *http.Header) { | ||||||
|  | 	header.Set("X-XSS-Protection", "1; mode=block") | ||||||
|  | 	header.Set("X-Content-Type-Options", "nosniff") | ||||||
|  | 	header.Set("X-Frame-Options", "DENY") | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								server/ctrl/webdav.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								server/ctrl/webdav.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | package ctrl | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"log" | ||||||
|  | 	. "github.com/mickael-kerjean/nuage/server/common" | ||||||
|  | 	"github.com/mickael-kerjean/nuage/server/model" | ||||||
|  | 	"golang.org/x/net/webdav" | ||||||
|  | 	"github.com/mickael-kerjean/mux" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func WebdavHandler(ctx App, res http.ResponseWriter, req *http.Request) { | ||||||
|  | 	accept := req.Header.Get("Accept") | ||||||
|  | 	if strings.HasPrefix(accept, "text/html") { | ||||||
|  | 		DefaultHandler("./data/public/index.html", ctx).ServeHTTP(res, req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if ctx.Backend == nil { | ||||||
|  | 		http.NotFound(res, req) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s := model.Share{ Id: mux.Vars(req)["id"] } | ||||||
|  | 	log.Println("> webdav: "+ s.Id)	 | ||||||
|  |  | ||||||
|  | 	h := &webdav.Handler{ | ||||||
|  | 		Prefix: "/s/",// + s.Id, | ||||||
|  | 		FileSystem: model.NewWebdavFs(ctx.Backend), | ||||||
|  | 		LockSystem: webdav.NewMemLS(), | ||||||
|  | 		Logger: func(r *http.Request, err error) { | ||||||
|  | 			Log(&ctx, "Webdav", "INFO") | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	h.ServeHTTP(res, req) | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								server/model/webdav.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								server/model/webdav.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"context" | ||||||
|  | 	. "github.com/mickael-kerjean/nuage/server/common" | ||||||
|  | 	"golang.org/x/net/webdav" | ||||||
|  | 	//"log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WebdavFs struct { | ||||||
|  | 	backend IBackend | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewWebdavFs(b IBackend) WebdavFs { | ||||||
|  | 	return WebdavFs{backend: b} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebdavFs) Mkdir(ctx context.Context, name string, perm os.FileMode) error { | ||||||
|  | 	return w.backend.Mkdir(name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebdavFs) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { | ||||||
|  | 	f, err := os.OpenFile(name, flag, perm) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return f, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebdavFs) RemoveAll(ctx context.Context, name string) error { | ||||||
|  | 	return w.backend.Rm(name) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebdavFs) Rename(ctx context.Context, oldName, newName string) error { | ||||||
|  | 	return w.backend.Mv(oldName, newName) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (w WebdavFs) Stat(ctx context.Context, name string) (os.FileInfo, error) { | ||||||
|  | 	files, err := w.backend.Ls(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for i:=0; i < len(files); i++ { | ||||||
|  | 		if files[i].Name() == "test" { | ||||||
|  | 			return files[i], nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil, os.ErrNotExist | ||||||
|  | } | ||||||
| @ -35,10 +35,13 @@ func Init(a *App) *http.Server { | |||||||
| 	share.HandleFunc("/{id}", APIHandler(ShareDelete, *a)).Methods("DELETE") | 	share.HandleFunc("/{id}", APIHandler(ShareDelete, *a)).Methods("DELETE") | ||||||
| 	share.HandleFunc("/{id}/proof", APIHandler(ShareVerifyProof, *a)).Methods("POST") | 	share.HandleFunc("/{id}/proof", APIHandler(ShareVerifyProof, *a)).Methods("POST") | ||||||
|  |  | ||||||
|  | 	// WEBDAV | ||||||
|  | 	r.PathPrefix("/s/{id}").Handler(APIHandler(WebdavHandler, *a)) | ||||||
|  | 	 | ||||||
| 	// APP | 	// APP | ||||||
| 	r.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a)).Methods("GET") | 	r.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a)).Methods("GET") | ||||||
| 	r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a)).Methods("GET") | 	r.PathPrefix("/assets").Handler(StaticHandler("./data/public/", *a)).Methods("GET") | ||||||
| 	r.NotFoundHandler = DefaultHandler("./data/public/index.html", *a) | 	r.PathPrefix("/").Handler(DefaultHandler("./data/public/index.html", *a)).Methods("GET") | ||||||
|  |  | ||||||
| 	srv := &http.Server{ | 	srv := &http.Server{ | ||||||
| 		Addr:    ":" + strconv.Itoa(a.Config.General.Port), | 		Addr:    ":" + strconv.Itoa(a.Config.General.Port), | ||||||
|  | |||||||
| @ -35,11 +35,6 @@ func StaticHandler(_path string, ctx App) http.Handler { | |||||||
|  |  | ||||||
| func DefaultHandler(_path string, ctx App) http.Handler { | func DefaultHandler(_path string, ctx App) http.Handler { | ||||||
| 	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { | 	return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { | ||||||
| 		if req.Method != "GET" { |  | ||||||
| 			http.Error(res, "Invalid request method.", 405) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		header := res.Header() | 		header := res.Header() | ||||||
| 		header.Set("Content-Type", "text/html") | 		header.Set("Content-Type", "text/html") | ||||||
| 		SecureHeader(&header) | 		SecureHeader(&header) | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								server/services/webdav.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								server/services/webdav.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | package services | ||||||
|  |  | ||||||
|  | // type Webdav struct { | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func NewWebdav() Webdav { | ||||||
|  | // 	return Webdav{} | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func (w Webdav) Mkdir(ctx context.Context, name string, perm os.FileMode) error { | ||||||
|  | // 	return nil | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func (w Webdav) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) { | ||||||
|  | // 	return File{}, nil | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func (w Webdav) RemoveAll(ctx context.Context, name string) error { | ||||||
|  | // 	return nil | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func (w Webdav) Rename(ctx context.Context, oldName, newName string) error { | ||||||
|  | // 	return nil | ||||||
|  | // } | ||||||
|  |  | ||||||
|  | // func (w Webdav) Stat(ctx context.Context, name string) (os.FileInfo, error) {	 | ||||||
|  | // 	return nil, nil | ||||||
|  | // } | ||||||
		Reference in New Issue
	
	Block a user
	 Mickael KERJEAN
					Mickael KERJEAN