diff --git a/README.md b/README.md index 9a004a8..32701e8 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Builds available [here](https://cdn.seedno.de/builds/roulette). ## Filtering -You can provide a comma-delimited string of patterns to match via the `include=` query parameter. +You can provide a comma-delimited string of patterns to match via the `include=` query parameter, assuming the `-f|--filter` flag is enabled. Only filenames matching one or more of the patterns will be served. @@ -28,7 +28,7 @@ Both filtering parameters ignore the file extension and full path; they only com ## Sorting -You can specify a sorting pattern via the `sort=` query parameter. +You can specify a sorting pattern via the `sort=` query parameter, assuming the `-s|--sort` flag is enabled. A value of `asc` means files will be served in ascending order (lowest-numbered to highest). @@ -58,9 +58,11 @@ Available Commands: version Print version Flags: + -f, --filter enable filtering via query parameters -h, --help help for roulette -p, --port uint16 port to listen on (default 8080) -r, --recursive recurse into subdirectories + -s, --sort enable sorting via query parameters -v, --verbose log accessed files to stdout Use "roulette [command] --help" for more information about a command. diff --git a/cmd/root.go b/cmd/root.go index f9d08d4..f30461d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -11,8 +11,10 @@ import ( "github.com/spf13/cobra" ) +var Filter bool var Port uint16 var Recursive bool +var Sort bool var Verbose bool var rootCmd = &cobra.Command{ @@ -36,8 +38,10 @@ func Execute() { } func init() { + rootCmd.Flags().BoolVarP(&Filter, "filter", "f", false, "enable filtering via query parameters") rootCmd.Flags().Uint16VarP(&Port, "port", "p", 8080, "port to listen on") rootCmd.Flags().BoolVarP(&Recursive, "recursive", "r", false, "recurse into subdirectories") + rootCmd.Flags().BoolVarP(&Sort, "sort", "s", false, "enable sorting via query parameters") rootCmd.Flags().BoolVarP(&Verbose, "verbose", "v", false, "log accessed files to stdout") rootCmd.Flags().SetInterspersed(true) } diff --git a/cmd/version.go b/cmd/version.go index 58dec6c..09b3807 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" ) -var Version = "0.14.2" +var Version = "0.15.0" func init() { rootCmd.AddCommand(versionCmd) diff --git a/cmd/web.go b/cmd/web.go index ab457ff..93951a6 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -48,7 +48,35 @@ func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { const LOGDATE string = "2006-01-02T15:04:05.000-07:00" const PREFIX string = "/src" -func stripQueryParam(inUrl string) (string, error) { +func splitQueryParams(query string) []string { + if query == "" { + return []string{} + } + + return strings.Split(query, ",") +} + +func generateQueryParams(filters *Filters, sort string) string { + switch { + case Filter && !Sort: + return fmt.Sprintf("?include=%v&exclude=%v", + filters.GetIncludes(), + filters.GetExcludes(), + ) + case !Filter && Sort: + return fmt.Sprintf("?sort=%v", sort) + case Filter && Sort: + return fmt.Sprintf("?include=%v&exclude=%v&sort=%v", + filters.GetIncludes(), + filters.GetExcludes(), + sort, + ) + } + + return "" +} + +func stripQueryParams(inUrl string) (string, error) { url, err := url.Parse(inUrl) if err != nil { return "", err @@ -82,7 +110,24 @@ func serveHtml(w http.ResponseWriter, r *http.Request, filePath string) error { htmlBody += `
` - htmlBody += fmt.Sprintf(` @@ -97,7 +142,7 @@ func serveHtml(w http.ResponseWriter, r *http.Request, filePath string) error { } func serveStaticFile(w http.ResponseWriter, r *http.Request, paths []string) error { - strippedUrl, err := stripQueryParam(r.URL.Path) + strippedUrl, err := stripQueryParams(r.URL.Path) if err != nil { return err } @@ -155,29 +200,21 @@ func serveStaticFileHandler(paths []string) appHandler { } } -func parseQueryParams(query string) []string { - if query == "" { - return []string{} - } - - return strings.Split(query, ",") -} - func serveHtmlHandler(paths []string) appHandler { return func(w http.ResponseWriter, r *http.Request) error { - refererUri, err := stripQueryParam(refererToUri(r.Referer())) + refererUri, err := stripQueryParams(refererToUri(r.Referer())) if err != nil { return err } filters := Filters{} - filters.Includes = parseQueryParams(r.URL.Query().Get("include")) - filters.Excludes = parseQueryParams(r.URL.Query().Get("exclude")) + filters.Includes = splitQueryParams(r.URL.Query().Get("include")) + filters.Excludes = splitQueryParams(r.URL.Query().Get("exclude")) - sort := r.URL.Query().Get("sort") + sortOrder := r.URL.Query().Get("sort") switch { - case r.URL.Path == "/" && sort == "asc" && refererUri != "": + case r.URL.Path == "/" && sortOrder == "asc" && refererUri != "": query, err := url.QueryUnescape(refererUri) if err != nil { return err @@ -194,7 +231,7 @@ func serveHtmlHandler(paths []string) appHandler { } if filePath == "" { - filePath, err = pickFile(paths, &filters, sort) + filePath, err = pickFile(paths, &filters, sortOrder) switch { case err != nil && err == ErrNoImagesFound: http.NotFound(w, r) @@ -215,10 +252,14 @@ func serveHtmlHandler(paths []string) appHandler { } } - newUrl := fmt.Sprintf("%v%v?include=%v&exclude=%v&sort=%v", r.URL.Host, filePath, filters.GetIncludes(), filters.GetExcludes(), sort) + newUrl := fmt.Sprintf("%v%v%v", + r.URL.Host, + filePath, + generateQueryParams(&filters, sortOrder), + ) http.Redirect(w, r, newUrl, http.StatusSeeOther) - case r.URL.Path == "/" && sort == "asc" && refererUri == "": - filePath, err := pickFile(paths, &filters, sort) + case r.URL.Path == "/" && sortOrder == "asc" && refererUri == "": + filePath, err := pickFile(paths, &filters, sortOrder) if err != nil && err == ErrNoImagesFound { http.NotFound(w, r) @@ -237,9 +278,13 @@ func serveHtmlHandler(paths []string) appHandler { return err } - newUrl := fmt.Sprintf("%v%v?include=%v&exclude=%v&sort=%v", r.URL.Host, filePath, filters.GetIncludes(), filters.GetExcludes(), sort) + newUrl := fmt.Sprintf("%v%v%v", + r.URL.Host, + filePath, + generateQueryParams(&filters, sortOrder), + ) http.Redirect(w, r, newUrl, http.StatusSeeOther) - case r.URL.Path == "/" && sort == "desc" && refererUri != "": + case r.URL.Path == "/" && sortOrder == "desc" && refererUri != "": query, err := url.QueryUnescape(refererUri) if err != nil { return err @@ -256,7 +301,7 @@ func serveHtmlHandler(paths []string) appHandler { } if filePath == "" { - filePath, err = pickFile(paths, &filters, sort) + filePath, err = pickFile(paths, &filters, sortOrder) switch { case err != nil && err == ErrNoImagesFound: http.NotFound(w, r) @@ -277,10 +322,14 @@ func serveHtmlHandler(paths []string) appHandler { } } - newUrl := fmt.Sprintf("%v%v?include=%v&exclude=%v&sort=%v", r.URL.Host, filePath, filters.GetIncludes(), filters.GetExcludes(), sort) + newUrl := fmt.Sprintf("%v%v%v", + r.URL.Host, + filePath, + generateQueryParams(&filters, sortOrder), + ) http.Redirect(w, r, newUrl, http.StatusSeeOther) - case r.URL.Path == "/" && sort == "desc" && refererUri == "": - filePath, err := pickFile(paths, &filters, sort) + case r.URL.Path == "/" && sortOrder == "desc" && refererUri == "": + filePath, err := pickFile(paths, &filters, sortOrder) if err != nil && err == ErrNoImagesFound { http.NotFound(w, r) @@ -299,10 +348,14 @@ func serveHtmlHandler(paths []string) appHandler { return err } - newUrl := fmt.Sprintf("%v%v?include=%v&exclude=%v&sort=%v", r.URL.Host, filePath, filters.GetIncludes(), filters.GetExcludes(), sort) + newUrl := fmt.Sprintf("%v%v%v", + r.URL.Host, + filePath, + generateQueryParams(&filters, sortOrder), + ) http.Redirect(w, r, newUrl, http.StatusSeeOther) case r.URL.Path == "/": - filePath, err := pickFile(paths, &filters, sort) + filePath, err := pickFile(paths, &filters, sortOrder) if err != nil && err == ErrNoImagesFound { http.NotFound(w, r) @@ -311,7 +364,11 @@ func serveHtmlHandler(paths []string) appHandler { return err } - newUrl := fmt.Sprintf("%v%v?include=%v&exclude=%v&sort=%v", r.URL.Host, filePath, filters.GetIncludes(), filters.GetExcludes(), sort) + newUrl := fmt.Sprintf("%v%v%v", + r.URL.Host, + filePath, + generateQueryParams(&filters, sortOrder), + ) http.Redirect(w, r, newUrl, http.StatusSeeOther) default: filePath := r.URL.Path