Add --prefix flag for easy reverse proxying

This commit is contained in:
Seednode 2023-09-13 23:24:29 -05:00
parent d612144be1
commit fdd33376ce
12 changed files with 88 additions and 54 deletions

View File

@ -114,6 +114,7 @@ Flags:
--cache-file string path to optional persistent cache file
-f, --filter enable filtering
--flash enable support for shockwave flash files (via ruffle.rs)
--handlers display registered handlers (for debugging)
-h, --help help for roulette
--images enable support for image files
-i, --info expose informational endpoints
@ -121,6 +122,7 @@ Flags:
--minimum-files uint32 skip directories with file counts below this value (default 1)
--page-length uint32 pagination length for statistics and debug pages
-p, --port uint16 port to listen on (default 8080)
--prefix string path with which to prefix all listeners (for reverse proxying)
--profile register net/http/pprof handlers
-r, --recursive recurse into subdirectories
--refresh-interval string force refresh interval equal to this duration (minimum 500ms)

View File

@ -152,5 +152,5 @@ func registerCacheHandlers(mux *httprouter.Router, args []string, cache *fileCac
cache.generate(args, formats)
}
mux.GET("/clear_cache", serveCacheClear(args, cache, formats))
register(mux, Prefix+"/clear_cache", serveCacheClear(args, cache, formats))
}

View File

@ -65,7 +65,7 @@ func serveIndexHtml(args []string, cache *fileCache, paginate bool) httprouter.H
if Sorting {
shouldSort = "?sort=asc"
}
htmlBody.WriteString(fmt.Sprintf("<tr><td><a href=\"%s%s%s\">%s</a></td></tr>\n", mediaPrefix, v, shouldSort, v))
htmlBody.WriteString(fmt.Sprintf("<tr><td><a href=\"%s%s%s%s\">%s</a></td></tr>\n", Prefix, mediaPrefix, v, shouldSort, v))
}
}
if PageLength != 0 {
@ -274,19 +274,19 @@ func serveEnabledMimeTypes(formats *types.Types) httprouter.Handle {
func registerInfoHandlers(mux *httprouter.Router, args []string, cache *fileCache, formats *types.Types) {
if Cache {
mux.GET("/html/", serveIndexHtml(args, cache, false))
register(mux, Prefix+"/html/", serveIndexHtml(args, cache, false))
if PageLength != 0 {
mux.GET("/html/:page", serveIndexHtml(args, cache, true))
register(mux, Prefix+"/html/:page", serveIndexHtml(args, cache, true))
}
mux.GET("/json", serveIndexJson(args, cache))
register(mux, Prefix+"/json", serveIndexJson(args, cache))
if PageLength != 0 {
mux.GET("/json/:page", serveIndexJson(args, cache))
register(mux, Prefix+"/json/:page", serveIndexJson(args, cache))
}
}
mux.GET("/available_extensions", serveAvailableExtensions())
mux.GET("/enabled_extensions", serveEnabledExtensions(formats))
mux.GET("/available_mime_types", serveAvailableMimeTypes())
mux.GET("/enabled_mime_types", serveEnabledMimeTypes(formats))
register(mux, Prefix+"/available_extensions", serveAvailableExtensions())
register(mux, Prefix+"/enabled_extensions", serveEnabledExtensions(formats))
register(mux, Prefix+"/available_mime_types", serveAvailableMimeTypes())
register(mux, Prefix+"/enabled_mime_types", serveEnabledMimeTypes(formats))
}

View File

@ -11,9 +11,9 @@ import (
)
func registerProfileHandlers(mux *httprouter.Router) {
mux.HandlerFunc("GET", "/debug/pprof/", pprof.Index)
mux.HandlerFunc("GET", "/debug/pprof/cmdline", pprof.Cmdline)
mux.HandlerFunc("GET", "/debug/pprof/profile", pprof.Profile)
mux.HandlerFunc("GET", "/debug/pprof/symbol", pprof.Symbol)
mux.HandlerFunc("GET", "/debug/pprof/trace", pprof.Trace)
mux.HandlerFunc("GET", Prefix+"/debug/pprof/", pprof.Index)
mux.HandlerFunc("GET", Prefix+"/debug/pprof/cmdline", pprof.Cmdline)
mux.HandlerFunc("GET", Prefix+"/debug/pprof/profile", pprof.Profile)
mux.HandlerFunc("GET", Prefix+"/debug/pprof/symbol", pprof.Symbol)
mux.HandlerFunc("GET", Prefix+"/debug/pprof/trace", pprof.Trace)
}

View File

@ -12,7 +12,7 @@ import (
)
const (
ReleaseVersion string = "0.80.1"
ReleaseVersion string = "0.81.0"
)
var (
@ -23,12 +23,14 @@ var (
CacheFile string
Filtering bool
Flash bool
Handlers bool
Images bool
Info bool
MaximumFileCount uint32
MinimumFileCount uint32
PageLength uint32
Port uint16
Prefix string
Profile bool
Recursive bool
RefreshInterval string
@ -79,12 +81,14 @@ func init() {
rootCmd.Flags().StringVar(&CacheFile, "cache-file", "", "path to optional persistent cache file")
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")
rootCmd.Flags().BoolVar(&Flash, "flash", false, "enable support for shockwave flash files (via ruffle.rs)")
rootCmd.Flags().BoolVar(&Handlers, "handlers", false, "display registered handlers (for debugging)")
rootCmd.Flags().BoolVar(&Images, "images", false, "enable support for image files")
rootCmd.Flags().BoolVarP(&Info, "info", "i", false, "expose informational endpoints")
rootCmd.Flags().Uint32Var(&MaximumFileCount, "maximum-files", 1<<32-1, "skip directories with file counts above this value")
rootCmd.Flags().Uint32Var(&MinimumFileCount, "minimum-files", 1, "skip directories with file counts below this value")
rootCmd.Flags().Uint32Var(&PageLength, "page-length", 0, "pagination length for statistics and debug pages")
rootCmd.Flags().Uint16VarP(&Port, "port", "p", 8080, "port to listen on")
rootCmd.Flags().StringVar(&Prefix, "prefix", "", "path with which to prefix all listeners (for reverse proxying)")
rootCmd.Flags().BoolVar(&Profile, "profile", false, "register net/http/pprof handlers")
rootCmd.Flags().BoolVarP(&Recursive, "recursive", "r", false, "recurse into subdirectories")
rootCmd.Flags().StringVar(&RefreshInterval, "refresh-interval", "", "force refresh interval equal to this duration (minimum 500ms)")

View File

@ -40,7 +40,9 @@ const (
func serveStaticFile(paths []string, cache *fileCache) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
path := strings.TrimPrefix(r.URL.Path, sourcePrefix)
prefix := Prefix + sourcePrefix
path := strings.TrimPrefix(r.URL.Path, prefix)
prefixedFilePath, err := stripQueryParams(path)
if err != nil {
@ -51,7 +53,7 @@ func serveStaticFile(paths []string, cache *fileCache) httprouter.Handle {
return
}
filePath, err := filepath.EvalSymlinks(strings.TrimPrefix(prefixedFilePath, sourcePrefix))
filePath, err := filepath.EvalSymlinks(strings.TrimPrefix(prefixedFilePath, prefix))
if err != nil {
fmt.Println(err)
@ -134,7 +136,7 @@ func serveRoot(paths []string, regexes *regexes, cache *fileCache, formats *type
return
}
strippedRefererUri := strings.TrimPrefix(refererUri, mediaPrefix)
strippedRefererUri := strings.TrimPrefix(refererUri, Prefix+mediaPrefix)
filters := &filters{
included: splitQueryParams(r.URL.Query().Get("include"), regexes),
@ -187,8 +189,9 @@ func serveRoot(paths []string, regexes *regexes, cache *fileCache, formats *type
queryParams := generateQueryParams(filters, sortOrder, refreshInterval)
newUrl := fmt.Sprintf("http://%s%s%s",
newUrl := fmt.Sprintf("http://%s%s%s%s",
r.Host,
Prefix,
preparePath(filePath),
queryParams,
)
@ -205,7 +208,7 @@ func serveMedia(paths []string, regexes *regexes, formats *types.Types) httprout
sortOrder := sortOrder(r)
path := strings.TrimPrefix(r.URL.Path, mediaPrefix)
path := strings.TrimPrefix(strings.TrimPrefix(r.URL.Path, Prefix), mediaPrefix)
if runtime.GOOS == "windows" {
path = strings.TrimPrefix(path, "/")
@ -240,7 +243,7 @@ func serveMedia(paths []string, regexes *regexes, formats *types.Types) httprout
return
}
fileUri := generateFileUri(path)
fileUri := Prefix + "/" + generateFileUri(path)
fileName := filepath.Base(path)
@ -248,20 +251,20 @@ func serveMedia(paths []string, regexes *regexes, formats *types.Types) httprout
refreshTimer, refreshInterval := refreshInterval(r)
queryParams := generateQueryParams(filters, sortOrder, refreshInterval)
rootUrl := Prefix + "/" + generateQueryParams(filters, sortOrder, refreshInterval)
var htmlBody strings.Builder
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
htmlBody.WriteString(faviconHtml)
htmlBody.WriteString(fmt.Sprintf(`<style>%s</style>`, fileType.Css()))
htmlBody.WriteString((fileType.Title(queryParams, fileUri, path, fileName, mimeType)))
htmlBody.WriteString((fileType.Title(rootUrl, fileUri, path, fileName, Prefix, mimeType)))
htmlBody.WriteString(`</head><body>`)
if refreshInterval != "0ms" {
htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){setInterval(function(){window.location.href = '/%s';}, %d);};</script>",
queryParams,
htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){setInterval(function(){window.location.href = '%s';}, %d);};</script>",
rootUrl,
refreshTimer))
}
htmlBody.WriteString((fileType.Body(queryParams, fileUri, path, fileName, mimeType)))
htmlBody.WriteString((fileType.Body(rootUrl, fileUri, path, fileName, Prefix, mimeType)))
htmlBody.WriteString(`</body></html>`)
_, err = io.WriteString(w, gohtml.Format(htmlBody.String()))
@ -285,6 +288,25 @@ func serveVersion() httprouter.Handle {
}
}
func register(mux *httprouter.Router, path string, handle httprouter.Handle) {
mux.GET(path, handle)
if Handlers {
fmt.Printf("Registered handler for path %s\n", path)
}
}
func redirectRoot() httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
newUrl := fmt.Sprintf("http://%s%s",
r.Host,
Prefix,
)
http.Redirect(w, r, newUrl, RedirectStatusCode)
}
}
func ServePage(args []string) error {
timeZone := os.Getenv("TZ")
if timeZone != "" {
@ -363,17 +385,23 @@ func ServePage(args []string) error {
mux.PanicHandler = serverErrorHandler()
mux.GET("/", serveRoot(paths, regexes, cache, formats))
Prefix = strings.TrimSuffix(Prefix, "/")
mux.GET("/favicons/*favicon", serveFavicons())
register(mux, Prefix+"/", serveRoot(paths, regexes, cache, formats))
mux.GET("/favicon.ico", serveFavicons())
if Prefix != "" {
register(mux, "/", redirectRoot())
}
mux.GET(mediaPrefix+"/*media", serveMedia(paths, regexes, formats))
register(mux, Prefix+"/favicons/*favicon", serveFavicons())
mux.GET(sourcePrefix+"/*static", serveStaticFile(paths, cache))
register(mux, Prefix+"/favicon.ico", serveFavicons())
mux.GET("/version", serveVersion())
register(mux, Prefix+mediaPrefix+"/*media", serveMedia(paths, regexes, formats))
register(mux, Prefix+sourcePrefix+"/*static", serveStaticFile(paths, cache))
register(mux, Prefix+"/version", serveVersion())
if Cache {
registerCacheHandlers(mux, args, cache, formats)

View File

@ -22,13 +22,13 @@ func (t Format) Css() string {
return css.String()
}
func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<title>%s</title>`, fileName)
}
func (t Format) Body(queryParams, fileUri, filePath, fileName, mime string) string {
return fmt.Sprintf(`<a href="/%s"><audio controls autoplay loop preload="auto"><source src="%s" type="%s" alt="Roulette selected: %s">Your browser does not support the audio tag.</audio></a>`,
queryParams,
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<a href="%s"><audio controls autoplay loop preload="auto"><source src="%s" type="%s" alt="Roulette selected: %s">Your browser does not support the audio tag.</audio></a>`,
rootUrl,
fileUri,
mime,
fileName)

View File

@ -22,15 +22,15 @@ func (t Format) Css() string {
return css.String()
}
func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<title>%s</title>`, fileName)
}
func (t Format) Body(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
var html strings.Builder
html.WriteString(fmt.Sprintf(`<script src="https://unpkg.com/@ruffle-rs/ruffle"></script><script>window.RufflePlayer.config = {autoplay:"on"};</script><embed src="%s"></embed>`, fileUri))
html.WriteString(fmt.Sprintf(`<br /><button onclick="window.location.href = '/%s';">Next</button>`, queryParams))
html.WriteString(fmt.Sprintf(`<br /><button onclick="window.location.href = '%s';">Next</button>`, rootUrl))
return html.String()
}

View File

@ -37,7 +37,7 @@ func (t Format) Css() string {
return css.String()
}
func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
dimensions, err := ImageDimensions(filePath)
if err != nil {
fmt.Println(err)
@ -49,14 +49,14 @@ func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) str
dimensions.height)
}
func (t Format) Body(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
dimensions, err := ImageDimensions(filePath)
if err != nil {
fmt.Println(err)
}
return fmt.Sprintf(`<a href="/%s"><img src="%s" width="%d" height="%d" type="%s" alt="Roulette selected: %s"></a>`,
queryParams,
return fmt.Sprintf(`<a href="%s"><img src="%s" width="%d" height="%d" type="%s" alt="Roulette selected: %s"></a>`,
rootUrl,
fileUri,
dimensions.width,
dimensions.height,

View File

@ -27,18 +27,18 @@ func (t Format) Css() string {
return css.String()
}
func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<title>%s</title>`, fileName)
}
func (t Format) Body(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
body, err := os.ReadFile(filePath)
if err != nil {
body = []byte{}
}
return fmt.Sprintf(`<a href="/%s"><textarea autofocus readonly>%s</textarea></a>`,
queryParams,
return fmt.Sprintf(`<a href="%s"><textarea autofocus readonly>%s</textarea></a>`,
rootUrl,
body)
}

View File

@ -20,8 +20,8 @@ var SupportedFormats = &Types{
type Type interface {
Css() string
Title(queryParams, fileUri, filePath, fileName, mime string) string
Body(queryParams, fileUri, filePath, fileName, mime string) string
Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string
Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string
Extensions() map[string]string
MimeTypes() []string
Validate(filePath string) bool

View File

@ -24,13 +24,13 @@ func (t Format) Css() string {
return css.String()
}
func (t Format) Title(queryParams, fileUri, filePath, fileName, mime string) string {
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<title>%s</title>`, fileName)
}
func (t Format) Body(queryParams, fileUri, filePath, fileName, mime string) string {
return fmt.Sprintf(`<a href="/%s"><video controls autoplay loop preload="auto"><source src="%s" type="%s" alt="Roulette selected: %s">Your browser does not support the video tag.</video></a>`,
queryParams,
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime string) string {
return fmt.Sprintf(`<a href="%s"><video controls autoplay loop preload="auto"><source src="%s" type="%s" alt="Roulette selected: %s">Your browser does not support the video tag.</video></a>`,
rootUrl,
fileUri,
mime,
fileName)