Allow empty paths, notifying if no valid files are found
This commit is contained in:
parent
ccff56d28d
commit
5167ff457e
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yosssi/gohtml"
|
"github.com/yosssi/gohtml"
|
||||||
|
@ -25,21 +24,17 @@ var (
|
||||||
ErrNoMediaFound = errors.New("no supported media formats found which match all criteria")
|
ErrNoMediaFound = errors.New("no supported media formats found which match all criteria")
|
||||||
)
|
)
|
||||||
|
|
||||||
func newErrorPage(title, body string) string {
|
|
||||||
var htmlBody strings.Builder
|
|
||||||
|
|
||||||
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
|
|
||||||
htmlBody.WriteString(faviconHtml)
|
|
||||||
htmlBody.WriteString(`<style>html,body,a{display:block;height:100%;width:100%;text-decoration:none;color:inherit;cursor:auto;}</style>`)
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("<title>%s</title></head>", title))
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("<body><a href=\"/\">%s</a></body></html>", body))
|
|
||||||
|
|
||||||
return htmlBody.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func notFound(w http.ResponseWriter, r *http.Request, path string) error {
|
func notFound(w http.ResponseWriter, r *http.Request, path string) error {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Header().Add("Content-Type", "text/html")
|
||||||
|
|
||||||
|
_, err := io.WriteString(w, gohtml.Format(newPage("Not Found", "404 Page not found")))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if Verbose {
|
if Verbose {
|
||||||
fmt.Printf("%s | ERROR: Unavailable file %s requested by %s\n",
|
fmt.Printf("%s | ERROR: Unavailable file %s requested by %s\n",
|
||||||
startTime.Format(logDate),
|
startTime.Format(logDate),
|
||||||
|
@ -48,20 +43,16 @@ func notFound(w http.ResponseWriter, r *http.Request, path string) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
w.Header().Add("Content-Type", "text/html")
|
|
||||||
|
|
||||||
_, err := io.WriteString(w, gohtml.Format(newErrorPage("Not Found", "404 Page not found")))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serverError(w http.ResponseWriter, r *http.Request, i interface{}) {
|
func serverError(w http.ResponseWriter, r *http.Request, i interface{}) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "text/html")
|
||||||
|
|
||||||
|
io.WriteString(w, gohtml.Format(newPage("Server Error", "An error has occurred. Please try again.")))
|
||||||
|
|
||||||
if Verbose {
|
if Verbose {
|
||||||
fmt.Printf("%s | ERROR: Invalid request for %s from %s\n",
|
fmt.Printf("%s | ERROR: Invalid request for %s from %s\n",
|
||||||
startTime.Format(logDate),
|
startTime.Format(logDate),
|
||||||
|
@ -69,10 +60,6 @@ func serverError(w http.ResponseWriter, r *http.Request, i interface{}) {
|
||||||
r.RemoteAddr,
|
r.RemoteAddr,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "text/html")
|
|
||||||
|
|
||||||
io.WriteString(w, gohtml.Format(newErrorPage("Server Error", "An error has occurred. Please try again.")))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func serverErrorHandler() func(http.ResponseWriter, *http.Request, interface{}) {
|
func serverErrorHandler() func(http.ResponseWriter, *http.Request, interface{}) {
|
||||||
|
|
20
cmd/files.go
20
cmd/files.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"math/big"
|
"math/big"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -205,6 +206,10 @@ func pathIsValid(path string, paths []string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasSupportedFiles(path string, formats types.Types) (bool, error) {
|
func hasSupportedFiles(path string, formats types.Types) (bool, error) {
|
||||||
|
if AllowEmpty {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
hasRegisteredFiles := make(chan bool, 1)
|
hasRegisteredFiles := make(chan bool, 1)
|
||||||
|
|
||||||
err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error {
|
err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error {
|
||||||
|
@ -463,7 +468,7 @@ Poll:
|
||||||
filesMatched+filesSkipped,
|
filesMatched+filesSkipped,
|
||||||
directoriesMatched,
|
directoriesMatched,
|
||||||
directoriesMatched+directoriesSkipped,
|
directoriesMatched+directoriesSkipped,
|
||||||
time.Since(startTime),
|
time.Since(startTime)-100*time.Millisecond,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +519,10 @@ func fileList(paths []string, filters *filters, sort string, index *fileIndex, f
|
||||||
func pickFile(list []string) (string, error) {
|
func pickFile(list []string) (string, error) {
|
||||||
fileCount := len(list)
|
fileCount := len(list)
|
||||||
|
|
||||||
if fileCount < 1 {
|
switch {
|
||||||
|
case fileCount < 1 && AllowEmpty:
|
||||||
|
return "", nil
|
||||||
|
case fileCount < 1:
|
||||||
return "", ErrNoMediaFound
|
return "", ErrNoMediaFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,6 +539,14 @@ func pickFile(list []string) (string, error) {
|
||||||
return list[val], nil
|
return list[val], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func preparePath(prefix, path string) string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return fmt.Sprintf("%s/%s", prefix, filepath.ToSlash(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + path
|
||||||
|
}
|
||||||
|
|
||||||
func normalizePath(path string) (string, error) {
|
func normalizePath(path string) (string, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,12 +14,13 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AllowedCharacters string = `^[A-z0-9.\-_]+$`
|
AllowedCharacters string = `^[A-z0-9.\-_]+$`
|
||||||
ReleaseVersion string = "3.7.2"
|
ReleaseVersion string = "3.8.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AdminPrefix string
|
AdminPrefix string
|
||||||
All bool
|
All bool
|
||||||
|
AllowEmpty bool
|
||||||
Audio bool
|
Audio bool
|
||||||
BinaryPrefix bool
|
BinaryPrefix bool
|
||||||
Bind string
|
Bind string
|
||||||
|
@ -111,6 +112,7 @@ func Execute() {
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.Flags().StringVar(&AdminPrefix, "admin-prefix", "", "string to prepend to administrative paths")
|
rootCmd.Flags().StringVar(&AdminPrefix, "admin-prefix", "", "string to prepend to administrative paths")
|
||||||
rootCmd.Flags().BoolVarP(&All, "all", "a", false, "enable all supported file types")
|
rootCmd.Flags().BoolVarP(&All, "all", "a", false, "enable all supported file types")
|
||||||
|
rootCmd.Flags().BoolVar(&AllowEmpty, "allow-empty", false, "allow specifying paths containing no supported files")
|
||||||
rootCmd.Flags().BoolVar(&Audio, "audio", false, "enable support for audio files")
|
rootCmd.Flags().BoolVar(&Audio, "audio", false, "enable support for audio files")
|
||||||
rootCmd.Flags().BoolVar(&BinaryPrefix, "binary-prefix", false, "use IEC binary prefixes instead of SI decimal prefixes")
|
rootCmd.Flags().BoolVar(&BinaryPrefix, "binary-prefix", false, "use IEC binary prefixes instead of SI decimal prefixes")
|
||||||
rootCmd.Flags().StringVarP(&Bind, "bind", "b", "0.0.0.0", "address to bind to")
|
rootCmd.Flags().StringVarP(&Bind, "bind", "b", "0.0.0.0", "address to bind to")
|
||||||
|
|
33
cmd/web.go
33
cmd/web.go
|
@ -42,12 +42,31 @@ const (
|
||||||
timeout time.Duration = 10 * time.Second
|
timeout time.Duration = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func preparePath(prefix, path string) string {
|
func newPage(title, body string) string {
|
||||||
if runtime.GOOS == "windows" {
|
var htmlBody strings.Builder
|
||||||
return fmt.Sprintf("%s/%s", prefix, filepath.ToSlash(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + path
|
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
|
||||||
|
htmlBody.WriteString(faviconHtml)
|
||||||
|
htmlBody.WriteString(`<style>html,body,a{display:block;height:100%;width:100%;text-decoration:none;color:inherit;cursor:auto;}</style>`)
|
||||||
|
htmlBody.WriteString(fmt.Sprintf("<title>%s</title></head>", title))
|
||||||
|
htmlBody.WriteString(fmt.Sprintf("<body><a href=\"/\">%s</a></body></html>", body))
|
||||||
|
|
||||||
|
return htmlBody.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func noFiles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
|
w.Write([]byte("No files found in the specified path(s).\n"))
|
||||||
|
|
||||||
|
if Verbose {
|
||||||
|
fmt.Printf("%s | SERVE: Empty path notice served to %s\n",
|
||||||
|
startTime.Format(logDate),
|
||||||
|
r.RemoteAddr,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveStaticFile(paths []string, index *fileIndex, errorChannel chan<- error) httprouter.Handle {
|
func serveStaticFile(paths []string, index *fileIndex, errorChannel chan<- error) httprouter.Handle {
|
||||||
|
@ -210,6 +229,10 @@ func serveRoot(paths []string, regexes *regexes, index *fileIndex, formats types
|
||||||
|
|
||||||
path, err = newFile(list, sortOrder, regexes, formats)
|
path, err = newFile(list, sortOrder, regexes, formats)
|
||||||
switch {
|
switch {
|
||||||
|
case path == "":
|
||||||
|
noFiles(w, r)
|
||||||
|
|
||||||
|
return
|
||||||
case err != nil && err == ErrNoMediaFound:
|
case err != nil && err == ErrNoMediaFound:
|
||||||
notFound(w, r, path)
|
notFound(w, r, path)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue