Allow empty paths, notifying if no valid files are found

This commit is contained in:
Seednode 2024-01-05 18:50:45 -06:00
parent ccff56d28d
commit 5167ff457e
4 changed files with 61 additions and 33 deletions

View File

@ -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{}) {

View File

@ -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 {

View File

@ -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")

View File

@ -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))
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()
} }
return prefix + path 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)