gomvn/routes/dirlist.go

130 lines
3.0 KiB
Go
Raw Permalink Normal View History

2024-03-18 13:20:37 +00:00
package routes
import (
_ "embed"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter"
"html/template"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strings"
)
//go:embed dirlist.go.html
var dirListHtml string
var dirListTemplate = template.Must(template.New("dirlist").Parse(dirListHtml))
func (r *routeCtx) handleFiles(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
if containsDotDot(req.URL.Path) {
http.Error(rw, "invalid URL path", http.StatusBadRequest)
return
}
if strings.HasSuffix(req.URL.Path, "/") {
r.handleDirList(rw, req)
return
}
open, err := os.Open(filepath.Join(r.basePath, req.URL.Path))
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
stat, err := open.Stat()
if err != nil {
http.Error(rw, "500 Internal Server Error: Failed to stat file", http.StatusInternalServerError)
return
}
http.ServeContent(rw, req, open.Name(), stat.ModTime(), open)
}
type fileInfo struct {
Name string
URL string
Size string
ModTime string
}
func (r *routeCtx) handleDirList(rw http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodHead {
return
}
2024-03-18 13:20:37 +00:00
openDir, err := os.ReadDir(filepath.Join(r.basePath, req.URL.Path))
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
fileInfos := make([]*fileInfo, len(openDir))
for i := range openDir {
info, err := openDir[i].Info()
if err != nil {
http.Error(rw, "500 Internal Server Error: Failed to stat file", http.StatusInternalServerError)
return
}
url := path.Join(req.URL.Path, info.Name())
name := path.Base(url)
size := ""
if info.IsDir() {
url += "/"
name += "/"
} else {
size = humanize.IBytes(uint64(info.Size()))
}
fileInfos[i] = &fileInfo{
Name: name,
URL: url,
Size: size,
ModTime: info.ModTime().Format("2006-01-02 15:04:05 -0700"),
}
}
err = dirListTemplate.Execute(rw, map[string]any{
"Name": r.name,
"Path": req.URL.Path,
"Files": fileInfos,
})
if err != nil {
log.Println("[GoMVN] Index template error: ", err)
}
}
func (r *routeCtx) handlePut(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
p, err := r.pathUtils.ParsePath(req)
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
p = filepath.Join(r.basePath, p)
err = os.MkdirAll(filepath.Dir(p), os.ModePerm)
if err != nil {
http.Error(rw, "500 Failed to create directory", http.StatusInternalServerError)
return
}
create, err := os.Create(p)
if err != nil {
http.Error(rw, "500 Failed to open file", http.StatusInternalServerError)
return
}
_, err = io.Copy(create, req.Body)
if err != nil {
http.Error(rw, "500 Failed to write file", http.StatusInternalServerError)
return
}
}
func containsDotDot(v string) bool {
if !strings.Contains(v, "..") {
return false
}
for _, ent := range strings.FieldsFunc(v, isSlashRune) {
if ent == ".." {
return true
}
}
return false
}
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }