Compare commits
5 Commits
06c6b70b2c
...
7cfc1f7a47
Author | SHA1 | Date |
---|---|---|
Seednode | 7cfc1f7a47 | |
Seednode | e08335168c | |
Seednode | 98c730e824 | |
Seednode | 76456f586d | |
Seednode | 61060f7895 |
13
README.md
13
README.md
|
@ -37,8 +37,7 @@ The restricted paths are:
|
||||||
- `/debug/pprof/trace`
|
- `/debug/pprof/trace`
|
||||||
- `/extensions/available`
|
- `/extensions/available`
|
||||||
- `/extensions/enabled`
|
- `/extensions/enabled`
|
||||||
- `/index/html`
|
- `/index/`
|
||||||
- `/index/json`
|
|
||||||
- `/index/rebuild`
|
- `/index/rebuild`
|
||||||
- `/types/available`
|
- `/types/available`
|
||||||
- `/types/enabled`
|
- `/types/enabled`
|
||||||
|
@ -64,7 +63,7 @@ Both filtering parameters ignore the file extension and full path; they only com
|
||||||
If the `--ignore` flag is passed, any directory containing a file named `.roulette-ignore` (configurable with `--ignore-file`) will be skipped during the scanning stage.
|
If the `--ignore` flag is passed, any directory containing a file named `.roulette-ignore` (configurable with `--ignore-file`) will be skipped during the scanning stage.
|
||||||
|
|
||||||
## Indexing
|
## Indexing
|
||||||
If the `-i|--indexing` flag is passed, all specified paths will be indexed on start.
|
If the `-i|--index` flag is passed, all specified paths will be indexed on start.
|
||||||
|
|
||||||
This will slightly increase the delay before the application begins responding to requests, but should significantly speed up subsequent requests.
|
This will slightly increase the delay before the application begins responding to requests, but should significantly speed up subsequent requests.
|
||||||
|
|
||||||
|
@ -81,11 +80,9 @@ Supported formats are `none`, `zlib`, and `zstd`.
|
||||||
Optionally, `--compression-fast` can be used to use the fastest instead of the best compression mode.
|
Optionally, `--compression-fast` can be used to use the fastest instead of the best compression mode.
|
||||||
|
|
||||||
## Info
|
## Info
|
||||||
If the `-i|--info` flag is passed, six additional endpoints are registered.
|
If the `-i|--info` flag is passed, five additional endpoints are registered.
|
||||||
|
|
||||||
The first of these—`/index/html` and `/index/json`—return the contents of the index, in HTML and JSON formats respectively.
|
The first of these—`/index/`—returns the contents of the index, in JSON format.
|
||||||
|
|
||||||
If `--page-length` is also set, these can be viewed in paginated form by appending a page number, e.g. `/index/html/5` for the fifth page.
|
|
||||||
|
|
||||||
This can prove useful when confirming whether the index is generated successfully, or whether a given file is in the index.
|
This can prove useful when confirming whether the index is generated successfully, or whether a given file is in the index.
|
||||||
|
|
||||||
|
@ -166,7 +163,6 @@ Flags:
|
||||||
-f, --filter enable filtering
|
-f, --filter enable filtering
|
||||||
--flash enable support for shockwave flash files (via ruffle.rs)
|
--flash enable support for shockwave flash files (via ruffle.rs)
|
||||||
--fun add a bit of excitement to your day
|
--fun add a bit of excitement to your day
|
||||||
--handlers display registered handlers (for debugging)
|
|
||||||
-h, --help help for roulette
|
-h, --help help for roulette
|
||||||
--ignore skip all directories containing a specified filename
|
--ignore skip all directories containing a specified filename
|
||||||
--ignore-file string filename used to indicate directory to be skipped (default ".roulette-ignore")
|
--ignore-file string filename used to indicate directory to be skipped (default ".roulette-ignore")
|
||||||
|
@ -176,7 +172,6 @@ Flags:
|
||||||
-i, --info expose informational endpoints
|
-i, --info expose informational endpoints
|
||||||
--max-file-count int skip directories with file counts above this value (default 2147483647)
|
--max-file-count int skip directories with file counts above this value (default 2147483647)
|
||||||
--min-file-count int skip directories with file counts below this value
|
--min-file-count int skip directories with file counts below this value
|
||||||
--page-length int pagination length for info pages
|
|
||||||
-p, --port int port to listen on (default 8080)
|
-p, --port int port to listen on (default 8080)
|
||||||
--prefix string root path for http handlers (for reverse proxying) (default "/")
|
--prefix string root path for http handlers (for reverse proxying) (default "/")
|
||||||
--profile register net/http/pprof handlers
|
--profile register net/http/pprof handlers
|
||||||
|
|
|
@ -5,7 +5,6 @@ Copyright © 2024 Seednode <seednode@seedno.de>
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"embed"
|
"embed"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -36,12 +35,7 @@ func serveFavicons(errorChannel chan<- error) httprouter.Handle {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = w.Header().Write(bytes.NewBufferString("Content-Length: " + strconv.Itoa(len(data))))
|
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
||||||
if err != nil {
|
|
||||||
errorChannel <- err
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = w.Write(data)
|
_, err = w.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
56
cmd/files.go
56
cmd/files.go
|
@ -25,10 +25,6 @@ import (
|
||||||
"seedno.de/seednode/roulette/types"
|
"seedno.de/seednode/roulette/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
filename = regexp.MustCompile(`(.+?)([0-9]*)(\..+)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
type scanStats struct {
|
type scanStats struct {
|
||||||
filesMatched chan int
|
filesMatched chan int
|
||||||
filesSkipped chan int
|
filesSkipped chan int
|
||||||
|
@ -79,14 +75,14 @@ func kill(path string, index *fileIndex) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFile(list []string, sortOrder string, formats types.Types) (string, error) {
|
func newFile(list []string, sortOrder string, filename *regexp.Regexp, formats types.Types) (string, error) {
|
||||||
path, err := pickFile(list)
|
path, err := pickFile(list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if sortOrder == "asc" || sortOrder == "desc" {
|
if sortOrder == "asc" || sortOrder == "desc" {
|
||||||
splitPath, err := split(path)
|
splitPath, err := split(path, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -125,8 +121,8 @@ func newFile(list []string, sortOrder string, formats types.Types) (string, erro
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextFile(filePath, sortOrder string, formats types.Types) (string, error) {
|
func nextFile(filePath, sortOrder string, filename *regexp.Regexp, formats types.Types) (string, error) {
|
||||||
splitPath, err := split(filePath)
|
splitPath, err := split(filePath, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -543,31 +539,39 @@ func validatePaths(args []string, formats types.Types) ([]string, error) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case pathMatches && hasSupportedFiles:
|
case pathMatches && hasSupportedFiles:
|
||||||
fmt.Printf("%s | PATHS: Added %s\n",
|
if Verbose {
|
||||||
time.Now().Format(logDate),
|
fmt.Printf("%s | PATHS: Added %s\n",
|
||||||
args[i],
|
time.Now().Format(logDate),
|
||||||
)
|
args[i],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
case !pathMatches && hasSupportedFiles:
|
case !pathMatches && hasSupportedFiles:
|
||||||
fmt.Printf("%s | PATHS: Added %s [resolved to %s]\n",
|
if Verbose {
|
||||||
time.Now().Format(logDate),
|
fmt.Printf("%s | PATHS: Added %s [resolved to %s]\n",
|
||||||
args[i],
|
time.Now().Format(logDate),
|
||||||
path,
|
args[i],
|
||||||
)
|
path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
case pathMatches && !hasSupportedFiles:
|
case pathMatches && !hasSupportedFiles:
|
||||||
fmt.Printf("%s | PATHS: Skipped %s (No supported files found)\n",
|
if Verbose {
|
||||||
time.Now().Format(logDate),
|
fmt.Printf("%s | PATHS: Skipped %s (No supported files found)\n",
|
||||||
args[i],
|
time.Now().Format(logDate),
|
||||||
)
|
args[i],
|
||||||
|
)
|
||||||
|
}
|
||||||
case !pathMatches && !hasSupportedFiles:
|
case !pathMatches && !hasSupportedFiles:
|
||||||
fmt.Printf("%s | PATHS: Skipped %s [resolved to %s] (No supported files found)\n",
|
if Verbose {
|
||||||
time.Now().Format(logDate),
|
fmt.Printf("%s | PATHS: Skipped %s [resolved to %s] (No supported files found)\n",
|
||||||
args[i],
|
time.Now().Format(logDate),
|
||||||
path,
|
args[i],
|
||||||
)
|
path,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -213,8 +212,6 @@ func serveIndexRebuild(args []string, index *fileIndex, formats types.Types, enc
|
||||||
time.Since(startTime).Round(time.Microsecond),
|
time.Since(startTime).Round(time.Microsecond),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
254
cmd/info.go
254
cmd/info.go
|
@ -7,194 +7,28 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/yosssi/gohtml"
|
|
||||||
"seedno.de/seednode/roulette/types"
|
"seedno.de/seednode/roulette/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func paginateIndex(page int, fileCount int, ending bool) string {
|
func serveIndex(args []string, index *fileIndex, errorChannel chan<- error) httprouter.Handle {
|
||||||
var firstPage int = 1
|
|
||||||
var lastPage int
|
|
||||||
|
|
||||||
if fileCount%PageLength == 0 {
|
|
||||||
lastPage = fileCount / PageLength
|
|
||||||
} else {
|
|
||||||
lastPage = (fileCount / PageLength) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var prevStatus, nextStatus string = "", ""
|
|
||||||
|
|
||||||
if page <= 1 {
|
|
||||||
prevStatus = " disabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
if page >= lastPage {
|
|
||||||
nextStatus = " disabled"
|
|
||||||
}
|
|
||||||
|
|
||||||
prevPage := page - 1
|
|
||||||
if prevPage < 1 {
|
|
||||||
prevPage = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
nextPage := page + 1
|
|
||||||
if nextPage > lastPage {
|
|
||||||
nextPage = fileCount / PageLength
|
|
||||||
}
|
|
||||||
|
|
||||||
var html strings.Builder
|
|
||||||
|
|
||||||
if ending {
|
|
||||||
html.WriteString("<tr><td style=\"border-bottom:none;\">")
|
|
||||||
} else {
|
|
||||||
html.WriteString("<tr><td>")
|
|
||||||
}
|
|
||||||
|
|
||||||
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\">First</button>",
|
|
||||||
Prefix,
|
|
||||||
AdminPrefix,
|
|
||||||
firstPage))
|
|
||||||
|
|
||||||
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\"%s>Prev</button>",
|
|
||||||
Prefix,
|
|
||||||
AdminPrefix,
|
|
||||||
prevPage,
|
|
||||||
prevStatus))
|
|
||||||
|
|
||||||
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\"%s>Next</button>",
|
|
||||||
Prefix,
|
|
||||||
AdminPrefix,
|
|
||||||
nextPage,
|
|
||||||
nextStatus))
|
|
||||||
|
|
||||||
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\">Last</button>",
|
|
||||||
Prefix,
|
|
||||||
AdminPrefix,
|
|
||||||
lastPage))
|
|
||||||
|
|
||||||
html.WriteString("</td></tr>\n")
|
|
||||||
|
|
||||||
return html.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
|
||||||
|
|
||||||
indexDump := index.List()
|
indexDump := index.List()
|
||||||
|
|
||||||
fileCount := len(indexDump)
|
|
||||||
|
|
||||||
var startIndex, stopIndex int
|
|
||||||
|
|
||||||
page, err := strconv.Atoi(p.ByName("page"))
|
|
||||||
if err != nil || page <= 0 {
|
|
||||||
startIndex = 0
|
|
||||||
stopIndex = fileCount
|
|
||||||
} else {
|
|
||||||
startIndex = ((page - 1) * PageLength)
|
|
||||||
stopIndex = (startIndex + PageLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
if startIndex > (fileCount - 1) {
|
|
||||||
indexDump = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if stopIndex > fileCount {
|
|
||||||
stopIndex = fileCount
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.SliceStable(indexDump, func(p, q int) bool {
|
sort.SliceStable(indexDump, func(p, q int) bool {
|
||||||
return strings.ToLower(indexDump[p]) < strings.ToLower(indexDump[q])
|
return strings.ToLower(indexDump[p]) < strings.ToLower(indexDump[q])
|
||||||
})
|
})
|
||||||
|
|
||||||
var htmlBody strings.Builder
|
|
||||||
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
|
|
||||||
htmlBody.WriteString(faviconHtml)
|
|
||||||
htmlBody.WriteString(`<style>a{text-decoration:none;height:100%;width:100%;color:inherit;cursor:pointer}`)
|
|
||||||
htmlBody.WriteString(`table,td,tr{border:none;}td{border-bottom:1px solid black;}td{white-space:nowrap;padding:.5em}</style>`)
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("<title>Index contains %d files</title></head><body><table>", fileCount))
|
|
||||||
|
|
||||||
if shouldPaginate && !DisableButtons {
|
|
||||||
htmlBody.WriteString(paginateIndex(page, fileCount, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(indexDump) > 0 {
|
|
||||||
for _, v := range indexDump[startIndex:stopIndex] {
|
|
||||||
var shouldSort = ""
|
|
||||||
|
|
||||||
if Sorting {
|
|
||||||
shouldSort = "?sort=asc"
|
|
||||||
}
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("<tr><td><a href=\"%s%s%s%s\">%s</a></td></tr>\n", Prefix, mediaPrefix, v, shouldSort, v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldPaginate && !DisableButtons {
|
|
||||||
htmlBody.WriteString(paginateIndex(page, fileCount, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlBody.WriteString(`</table></body></html>`)
|
|
||||||
|
|
||||||
written, err := io.WriteString(w, gohtml.Format(htmlBody.String()))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if Verbose {
|
|
||||||
fmt.Printf("%s | SERVE: HTML index page (%s) to %s in %s\n",
|
|
||||||
startTime.Format(logDate),
|
|
||||||
humanReadableSize(written),
|
|
||||||
realIP(r),
|
|
||||||
time.Since(startTime).Round(time.Microsecond),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveIndexJson(args []string, index *fileIndex, errorChannel chan<- error) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
indexedFiles := index.List()
|
response, err := json.MarshalIndent(indexDump, "", " ")
|
||||||
|
|
||||||
fileCount := len(indexedFiles)
|
|
||||||
|
|
||||||
sort.SliceStable(indexedFiles, func(p, q int) bool {
|
|
||||||
return strings.ToLower(indexedFiles[p]) < strings.ToLower(indexedFiles[q])
|
|
||||||
})
|
|
||||||
|
|
||||||
var startIndex, stopIndex int
|
|
||||||
|
|
||||||
page, err := strconv.Atoi(p.ByName("page"))
|
|
||||||
if err != nil || page <= 0 {
|
|
||||||
startIndex = 0
|
|
||||||
stopIndex = fileCount
|
|
||||||
} else {
|
|
||||||
startIndex = ((page - 1) * PageLength)
|
|
||||||
stopIndex = (startIndex + PageLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
if startIndex > (fileCount - 1) {
|
|
||||||
indexedFiles = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if stopIndex > fileCount {
|
|
||||||
stopIndex = fileCount
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := json.MarshalIndent(indexedFiles[startIndex:stopIndex], "", " ")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -203,6 +37,8 @@ func serveIndexJson(args []string, index *fileIndex, errorChannel chan<- error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response = append(response, []byte("\n")...)
|
||||||
|
|
||||||
written, err := w.Write(response)
|
written, err := w.Write(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
@ -219,35 +55,21 @@ func serveIndexJson(args []string, index *fileIndex, errorChannel chan<- error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveAvailableExtensions(errorChannel chan<- error) httprouter.Handle {
|
func serveExtensions(formats types.Types, available bool, errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
written, err := w.Write([]byte(types.SupportedFormats.GetExtensions()))
|
var extensions string
|
||||||
if err != nil {
|
|
||||||
errorChannel <- err
|
if available {
|
||||||
|
extensions = types.SupportedFormats.GetExtensions()
|
||||||
|
} else {
|
||||||
|
extensions = formats.GetExtensions()
|
||||||
}
|
}
|
||||||
|
|
||||||
if Verbose {
|
written, err := w.Write([]byte(extensions))
|
||||||
fmt.Printf("%s | SERVE: Available extension list (%s) to %s in %s\n",
|
|
||||||
startTime.Format(logDate),
|
|
||||||
humanReadableSize(written),
|
|
||||||
realIP(r),
|
|
||||||
time.Since(startTime).Round(time.Microsecond),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveEnabledExtensions(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
|
|
||||||
written, err := w.Write([]byte(formats.GetExtensions()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
}
|
}
|
||||||
|
@ -263,13 +85,21 @@ func serveEnabledExtensions(formats types.Types, errorChannel chan<- error) http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveAvailableMediaTypes(errorChannel chan<- error) httprouter.Handle {
|
func serveMediaTypes(formats types.Types, available bool, errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
written, err := w.Write([]byte(types.SupportedFormats.GetMediaTypes()))
|
var mediaTypes string
|
||||||
|
|
||||||
|
if available {
|
||||||
|
mediaTypes = types.SupportedFormats.GetMediaTypes()
|
||||||
|
} else {
|
||||||
|
mediaTypes = formats.GetMediaTypes()
|
||||||
|
}
|
||||||
|
|
||||||
|
written, err := w.Write([]byte(mediaTypes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
}
|
}
|
||||||
|
@ -285,43 +115,13 @@ func serveAvailableMediaTypes(errorChannel chan<- error) httprouter.Handle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveEnabledMediaTypes(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
||||||
startTime := time.Now()
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
|
|
||||||
written, err := w.Write([]byte(formats.GetMediaTypes()))
|
|
||||||
if err != nil {
|
|
||||||
errorChannel <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
if Verbose {
|
|
||||||
fmt.Printf("%s | SERVE: Registered media type list (%s) to %s in %s\n",
|
|
||||||
startTime.Format(logDate),
|
|
||||||
humanReadableSize(written),
|
|
||||||
realIP(r),
|
|
||||||
time.Since(startTime).Round(time.Microsecond),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerInfoHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) {
|
func registerInfoHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) {
|
||||||
if Index {
|
if Index {
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/index/html", serveIndexHtml(args, index, false))
|
mux.GET(Prefix+AdminPrefix+"/index", serveIndex(args, index, errorChannel))
|
||||||
if PageLength != 0 {
|
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/index/html/:page", serveIndexHtml(args, index, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/index/json", serveIndexJson(args, index, errorChannel))
|
|
||||||
if PageLength != 0 {
|
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/index/json/:page", serveIndexJson(args, index, errorChannel))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/extensions/available", serveAvailableExtensions(errorChannel))
|
mux.GET(Prefix+AdminPrefix+"/extensions/available", serveExtensions(formats, true, errorChannel))
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/extensions/enabled", serveEnabledExtensions(formats, errorChannel))
|
mux.GET(Prefix+AdminPrefix+"/extensions/enabled", serveExtensions(formats, false, errorChannel))
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/types/available", serveAvailableMediaTypes(errorChannel))
|
mux.GET(Prefix+AdminPrefix+"/types/available", serveMediaTypes(formats, true, errorChannel))
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/types/enabled", serveEnabledMediaTypes(formats, errorChannel))
|
mux.GET(Prefix+AdminPrefix+"/types/enabled", serveMediaTypes(formats, false, errorChannel))
|
||||||
}
|
}
|
||||||
|
|
12
cmd/root.go
12
cmd/root.go
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AllowedCharacters string = `^[A-z0-9.\-_]+$`
|
AllowedCharacters string = `^[A-z0-9.\-_]+$`
|
||||||
ReleaseVersion string = "5.4.3"
|
ReleaseVersion string = "6.1.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -38,7 +38,6 @@ var (
|
||||||
Filtering bool
|
Filtering bool
|
||||||
Flash bool
|
Flash bool
|
||||||
Fun bool
|
Fun bool
|
||||||
Handlers bool
|
|
||||||
Ignore bool
|
Ignore bool
|
||||||
IgnoreFile string
|
IgnoreFile string
|
||||||
Images bool
|
Images bool
|
||||||
|
@ -47,12 +46,10 @@ var (
|
||||||
Info bool
|
Info bool
|
||||||
MaxFileCount int
|
MaxFileCount int
|
||||||
MinFileCount int
|
MinFileCount int
|
||||||
PageLength int
|
|
||||||
Port int
|
Port int
|
||||||
Prefix string
|
Prefix string
|
||||||
Profile bool
|
Profile bool
|
||||||
Recursive bool
|
Recursive bool
|
||||||
Redact bool
|
|
||||||
Refresh bool
|
Refresh bool
|
||||||
Russian bool
|
Russian bool
|
||||||
Sorting bool
|
Sorting bool
|
||||||
|
@ -126,7 +123,7 @@ func init() {
|
||||||
rootCmd.Flags().BoolVar(&CaseSensitive, "case-sensitive", false, "use case-sensitive matching for filters")
|
rootCmd.Flags().BoolVar(&CaseSensitive, "case-sensitive", false, "use case-sensitive matching for filters")
|
||||||
rootCmd.Flags().BoolVar(&Code, "code", false, "enable support for source code files")
|
rootCmd.Flags().BoolVar(&Code, "code", false, "enable support for source code files")
|
||||||
rootCmd.Flags().StringVar(&CodeTheme, "code-theme", "solarized-dark256", "theme for source code syntax highlighting")
|
rootCmd.Flags().StringVar(&CodeTheme, "code-theme", "solarized-dark256", "theme for source code syntax highlighting")
|
||||||
rootCmd.Flags().IntVar(&Concurrency, "concurrency", 10240, "maximum concurrency for scan threads")
|
rootCmd.Flags().IntVar(&Concurrency, "concurrency", 1024, "maximum concurrency for scan threads")
|
||||||
rootCmd.Flags().BoolVarP(&Debug, "debug", "d", false, "display even more verbose logs")
|
rootCmd.Flags().BoolVarP(&Debug, "debug", "d", false, "display even more verbose logs")
|
||||||
rootCmd.Flags().BoolVar(&DisableButtons, "disable-buttons", false, "disable first/prev/next/last buttons")
|
rootCmd.Flags().BoolVar(&DisableButtons, "disable-buttons", false, "disable first/prev/next/last buttons")
|
||||||
rootCmd.Flags().BoolVar(&ExitOnError, "exit-on-error", false, "shut down webserver on error, instead of just printing error")
|
rootCmd.Flags().BoolVar(&ExitOnError, "exit-on-error", false, "shut down webserver on error, instead of just printing error")
|
||||||
|
@ -134,21 +131,18 @@ func init() {
|
||||||
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")
|
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(&Flash, "flash", false, "enable support for shockwave flash files (via ruffle.rs)")
|
||||||
rootCmd.Flags().BoolVar(&Fun, "fun", false, "add a bit of excitement to your day")
|
rootCmd.Flags().BoolVar(&Fun, "fun", false, "add a bit of excitement to your day")
|
||||||
rootCmd.Flags().BoolVar(&Handlers, "handlers", false, "display registered handlers (for debugging)")
|
|
||||||
rootCmd.Flags().BoolVar(&Ignore, "ignore", false, "skip all directories containing a specified filename")
|
rootCmd.Flags().BoolVar(&Ignore, "ignore", false, "skip all directories containing a specified filename")
|
||||||
rootCmd.Flags().StringVar(&IgnoreFile, "ignore-file", ".roulette-ignore", "filename used to indicate directory to be skipped")
|
rootCmd.Flags().StringVar(&IgnoreFile, "ignore-file", ".roulette-ignore", "filename used to indicate directory should be skipped")
|
||||||
rootCmd.Flags().BoolVar(&Images, "images", false, "enable support for image files")
|
rootCmd.Flags().BoolVar(&Images, "images", false, "enable support for image files")
|
||||||
rootCmd.Flags().BoolVar(&Index, "index", false, "generate index of supported file paths at startup")
|
rootCmd.Flags().BoolVar(&Index, "index", false, "generate index of supported file paths at startup")
|
||||||
rootCmd.Flags().StringVar(&IndexFile, "index-file", "", "path to optional persistent index file")
|
rootCmd.Flags().StringVar(&IndexFile, "index-file", "", "path to optional persistent index file")
|
||||||
rootCmd.Flags().BoolVarP(&Info, "info", "i", false, "expose informational endpoints")
|
rootCmd.Flags().BoolVarP(&Info, "info", "i", false, "expose informational endpoints")
|
||||||
rootCmd.Flags().IntVar(&MaxFileCount, "max-file-count", math.MaxInt32, "skip directories with file counts above this value")
|
rootCmd.Flags().IntVar(&MaxFileCount, "max-file-count", math.MaxInt32, "skip directories with file counts above this value")
|
||||||
rootCmd.Flags().IntVar(&MinFileCount, "min-file-count", 0, "skip directories with file counts below this value")
|
rootCmd.Flags().IntVar(&MinFileCount, "min-file-count", 0, "skip directories with file counts below this value")
|
||||||
rootCmd.Flags().IntVar(&PageLength, "page-length", 0, "pagination length for info pages")
|
|
||||||
rootCmd.Flags().IntVarP(&Port, "port", "p", 8080, "port to listen on")
|
rootCmd.Flags().IntVarP(&Port, "port", "p", 8080, "port to listen on")
|
||||||
rootCmd.Flags().StringVar(&Prefix, "prefix", "/", "root path for http handlers (for reverse proxying)")
|
rootCmd.Flags().StringVar(&Prefix, "prefix", "/", "root path for http handlers (for reverse proxying)")
|
||||||
rootCmd.Flags().BoolVar(&Profile, "profile", false, "register net/http/pprof handlers")
|
rootCmd.Flags().BoolVar(&Profile, "profile", false, "register net/http/pprof handlers")
|
||||||
rootCmd.Flags().BoolVarP(&Recursive, "recursive", "r", false, "recurse into subdirectories")
|
rootCmd.Flags().BoolVarP(&Recursive, "recursive", "r", false, "recurse into subdirectories")
|
||||||
rootCmd.Flags().BoolVar(&Redact, "redact", false, "redact admin prefix in log output")
|
|
||||||
rootCmd.Flags().BoolVar(&Refresh, "refresh", false, "enable automatic page refresh via query parameter")
|
rootCmd.Flags().BoolVar(&Refresh, "refresh", false, "enable automatic page refresh via query parameter")
|
||||||
rootCmd.Flags().BoolVar(&Russian, "russian", false, "remove selected images after serving")
|
rootCmd.Flags().BoolVar(&Russian, "russian", false, "remove selected images after serving")
|
||||||
rootCmd.Flags().BoolVarP(&Sorting, "sort", "s", false, "enable sorting")
|
rootCmd.Flags().BoolVarP(&Sorting, "sort", "s", false, "enable sorting")
|
||||||
|
|
13
cmd/sort.go
13
cmd/sort.go
|
@ -6,6 +6,7 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ func (splitPath *splitPath) decrement() string {
|
||||||
return fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
|
return fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func split(path string) (*splitPath, error) {
|
func split(path string, filename *regexp.Regexp) (*splitPath, error) {
|
||||||
split := filename.FindAllStringSubmatch(path, -1)
|
split := filename.FindAllStringSubmatch(path, -1)
|
||||||
|
|
||||||
if len(split) < 1 || len(split[0]) < 3 {
|
if len(split) < 1 || len(split[0]) < 3 {
|
||||||
|
@ -54,8 +55,8 @@ func split(path string) (*splitPath, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRange(path string, index *fileIndex) (string, string, error) {
|
func getRange(path string, index *fileIndex, filename *regexp.Regexp) (string, string, error) {
|
||||||
splitPath, err := split(path)
|
splitPath, err := split(path, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
@ -70,7 +71,7 @@ func getRange(path string, index *fileIndex) (string, string, error) {
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for _, val := range list {
|
for _, val := range list {
|
||||||
splitVal, err := split(val)
|
splitVal, err := split(val, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
@ -94,8 +95,8 @@ func pathUrlEscape(path string) string {
|
||||||
return strings.Replace(path, `'`, `%27`, -1)
|
return strings.Replace(path, `'`, `%27`, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func paginateSorted(path, first, last, queryParams string, formats types.Types) (string, error) {
|
func paginate(path, first, last, queryParams string, filename *regexp.Regexp, formats types.Types) (string, error) {
|
||||||
split, err := split(path)
|
split, err := split(path, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
70
cmd/web.go
70
cmd/web.go
|
@ -5,7 +5,6 @@ Copyright © 2024 Seednode <seednode@seedno.de>
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -32,8 +32,6 @@ import (
|
||||||
"seedno.de/seednode/roulette/types/video"
|
"seedno.de/seednode/roulette/types/video"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ()
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
logDate string = `2006-01-02T15:04:05.000-07:00`
|
logDate string = `2006-01-02T15:04:05.000-07:00`
|
||||||
sourcePrefix string = `/source`
|
sourcePrefix string = `/source`
|
||||||
|
@ -171,7 +169,7 @@ func serveStaticFile(paths []string, index *fileIndex, errorChannel chan<- error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveRoot(paths []string, index *fileIndex, formats types.Types, encoder *zstd.Encoder, errorChannel chan<- error) httprouter.Handle {
|
func serveRoot(paths []string, index *fileIndex, filename *regexp.Regexp, formats types.Types, encoder *zstd.Encoder, errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
refererUri, err := stripQueryParams(refererToUri(r.Referer()))
|
refererUri, err := stripQueryParams(refererToUri(r.Referer()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -196,7 +194,7 @@ func serveRoot(paths []string, index *fileIndex, formats types.Types, encoder *z
|
||||||
var path string
|
var path string
|
||||||
|
|
||||||
if refererUri != "" {
|
if refererUri != "" {
|
||||||
path, err = nextFile(strippedRefererUri, sortOrder, formats)
|
path, err = nextFile(strippedRefererUri, sortOrder, filename, formats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -220,7 +218,7 @@ func serveRoot(paths []string, index *fileIndex, formats types.Types, encoder *z
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err = newFile(list, sortOrder, formats)
|
path, err = newFile(list, sortOrder, filename, formats)
|
||||||
switch {
|
switch {
|
||||||
case path == "":
|
case path == "":
|
||||||
noFiles(w, r)
|
noFiles(w, r)
|
||||||
|
@ -251,7 +249,7 @@ func serveRoot(paths []string, index *fileIndex, formats types.Types, encoder *z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
func serveMedia(paths []string, index *fileIndex, filename *regexp.Regexp, formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
|
@ -314,7 +312,7 @@ func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChan
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType := format.MediaType(path)
|
mediaType := format.MediaType(filepath.Ext(path))
|
||||||
|
|
||||||
fileUri := Prefix + generateFileUri(path)
|
fileUri := Prefix + generateFileUri(path)
|
||||||
|
|
||||||
|
@ -347,7 +345,7 @@ func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChan
|
||||||
var first, last string
|
var first, last string
|
||||||
|
|
||||||
if Index && sortOrder != "" {
|
if Index && sortOrder != "" {
|
||||||
first, last, err = getRange(path, index)
|
first, last, err = getRange(path, index, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -358,7 +356,7 @@ func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChan
|
||||||
}
|
}
|
||||||
|
|
||||||
if Index && !DisableButtons && sortOrder != "" {
|
if Index && !DisableButtons && sortOrder != "" {
|
||||||
paginate, err := paginateSorted(path, first, last, queryParams, formats)
|
paginated, err := paginate(path, first, last, queryParams, filename, formats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -367,7 +365,7 @@ func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChan
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlBody.WriteString(paginate)
|
htmlBody.WriteString(paginated)
|
||||||
}
|
}
|
||||||
|
|
||||||
if refreshInterval != "0ms" {
|
if refreshInterval != "0ms" {
|
||||||
|
@ -388,7 +386,7 @@ func serveMedia(paths []string, index *fileIndex, formats types.Types, errorChan
|
||||||
|
|
||||||
formattedPage := gohtml.Format(htmlBody.String())
|
formattedPage := gohtml.Format(htmlBody.String())
|
||||||
|
|
||||||
written, err := io.WriteString(w, formattedPage)
|
written, err := io.WriteString(w, formattedPage+"\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -426,12 +424,7 @@ func serveVersion(errorChannel chan<- error) httprouter.Handle {
|
||||||
|
|
||||||
data := []byte(fmt.Sprintf("roulette v%s\n", ReleaseVersion))
|
data := []byte(fmt.Sprintf("roulette v%s\n", ReleaseVersion))
|
||||||
|
|
||||||
err := w.Header().Write(bytes.NewBufferString("Content-Length: " + strconv.Itoa(len(data))))
|
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
||||||
if err != nil {
|
|
||||||
errorChannel <- err
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
written, err := w.Write(data)
|
written, err := w.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -451,21 +444,6 @@ func serveVersion(errorChannel chan<- error) httprouter.Handle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerHandler(mux *httprouter.Router, path string, handle httprouter.Handle) {
|
|
||||||
mux.GET(path, handle)
|
|
||||||
|
|
||||||
if Redact && AdminPrefix != "" {
|
|
||||||
path = strings.ReplaceAll(path, AdminPrefix, "/<admin_prefix>")
|
|
||||||
}
|
|
||||||
|
|
||||||
if Handlers {
|
|
||||||
fmt.Printf("%s | SERVE: Registered handler for %s\n",
|
|
||||||
time.Now().Format(logDate),
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectRoot() httprouter.Handle {
|
func redirectRoot() httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
newUrl := fmt.Sprintf("http://%s%s",
|
newUrl := fmt.Sprintf("http://%s%s",
|
||||||
|
@ -539,10 +517,6 @@ func ServePage(args []string) error {
|
||||||
return ErrNoMediaFound
|
return ErrNoMediaFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasSuffix(Prefix, "/") {
|
|
||||||
Prefix = Prefix + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
listenHost := net.JoinHostPort(Bind, strconv.Itoa(Port))
|
listenHost := net.JoinHostPort(Bind, strconv.Itoa(Port))
|
||||||
|
|
||||||
index := &fileIndex{
|
index := &fileIndex{
|
||||||
|
@ -584,26 +558,32 @@ func ServePage(args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandler(mux, Prefix, serveRoot(paths, index, formats, encoder, errorChannel))
|
filename := regexp.MustCompile(`(.+?)([0-9]*)(\..+)`)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(Prefix, "/") {
|
||||||
|
Prefix = Prefix + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.GET(Prefix, serveRoot(paths, index, filename, formats, encoder, errorChannel))
|
||||||
|
|
||||||
Prefix = strings.TrimSuffix(Prefix, "/")
|
Prefix = strings.TrimSuffix(Prefix, "/")
|
||||||
|
|
||||||
if Prefix != "" {
|
if Prefix != "" {
|
||||||
registerHandler(mux, "/", redirectRoot())
|
mux.GET("/", redirectRoot())
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandler(mux, Prefix+"/favicons/*favicon", serveFavicons(errorChannel))
|
mux.GET(Prefix+"/favicons/*favicon", serveFavicons(errorChannel))
|
||||||
|
|
||||||
registerHandler(mux, Prefix+"/favicon.ico", serveFavicons(errorChannel))
|
mux.GET(Prefix+"/favicon.ico", serveFavicons(errorChannel))
|
||||||
|
|
||||||
registerHandler(mux, Prefix+mediaPrefix+"/*media", serveMedia(paths, index, formats, errorChannel))
|
mux.GET(Prefix+mediaPrefix+"/*media", serveMedia(paths, index, filename, formats, errorChannel))
|
||||||
|
|
||||||
registerHandler(mux, Prefix+sourcePrefix+"/*static", serveStaticFile(paths, index, errorChannel))
|
mux.GET(Prefix+sourcePrefix+"/*static", serveStaticFile(paths, index, errorChannel))
|
||||||
|
|
||||||
registerHandler(mux, Prefix+"/version", serveVersion(errorChannel))
|
mux.GET(Prefix+"/version", serveVersion(errorChannel))
|
||||||
|
|
||||||
if Index {
|
if Index {
|
||||||
registerHandler(mux, Prefix+AdminPrefix+"/index/rebuild", serveIndexRebuild(args, index, formats, encoder, errorChannel))
|
mux.GET(Prefix+AdminPrefix+"/index/rebuild", serveIndexRebuild(args, index, formats, encoder, errorChannel))
|
||||||
|
|
||||||
importIndex(paths, index, formats, encoder, errorChannel)
|
importIndex(paths, index, formats, encoder, errorChannel)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# set app name
|
||||||
|
ARG app=roulette
|
||||||
|
|
||||||
|
# create build stage
|
||||||
|
ARG TAG
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:$TAG AS build
|
||||||
|
ARG app
|
||||||
|
|
||||||
|
# install dependencies
|
||||||
|
RUN apk add --update-cache git upx
|
||||||
|
|
||||||
|
# clone
|
||||||
|
RUN git clone https://git.seedno.de/seednode/$app /src/$app
|
||||||
|
|
||||||
|
# build and compress the binary
|
||||||
|
WORKDIR /src/$app
|
||||||
|
ARG TARGETOS TARGETARCH
|
||||||
|
RUN CGO_ENABLED=0 \
|
||||||
|
GOOS=$TARGETOS \
|
||||||
|
GOARCH=$TARGETARCH \
|
||||||
|
go build -trimpath -ldflags "-s -w" -o $app \
|
||||||
|
&& upx --best --lzma $app \
|
||||||
|
&& chmod 500 $app
|
||||||
|
|
||||||
|
# set up final stage
|
||||||
|
FROM --platform=$BUILDPLATFORM alpine:latest
|
||||||
|
ARG app
|
||||||
|
|
||||||
|
# copy in user info
|
||||||
|
COPY --chown=root:root --chmod=0400 passwd /etc/passwd
|
||||||
|
|
||||||
|
# run as nonroot
|
||||||
|
USER root
|
||||||
|
|
||||||
|
# copy in binary
|
||||||
|
COPY --from=build --chown=root:root --chmod=0005 /src/$app/$app /$app
|
||||||
|
|
||||||
|
# copy in time zone info
|
||||||
|
COPY --from=build --chown=root:root --chmod=0004 /usr/local/go/lib/time/zoneinfo.zip /
|
||||||
|
|
||||||
|
# load time zone info
|
||||||
|
ENV ZONEINFO=/zoneinfo.zip
|
||||||
|
|
||||||
|
# listen on an unprivileged port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# run application
|
||||||
|
ENTRYPOINT ["/bin/ash"]
|
|
@ -37,3 +37,19 @@ docker buildx build --platform "${platforms}" \
|
||||||
$(if [ "${LATEST}" == "yes" ]; then echo "-t ${registry}/${image_name}:latest"; fi) \
|
$(if [ "${LATEST}" == "yes" ]; then echo "-t ${registry}/${image_name}:latest"; fi) \
|
||||||
-f Dockerfile . \
|
-f Dockerfile . \
|
||||||
--push
|
--push
|
||||||
|
|
||||||
|
# copy debug image to local image repository
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg TAG="${tag}" \
|
||||||
|
-t "${registry}/${image_name}:${image_version}-debug" \
|
||||||
|
$(if [ "${LATEST}" == "yes" ]; then echo "-t ${registry}/${image_name}:debug"; fi) \
|
||||||
|
-f Dockerfile.debug . \
|
||||||
|
--load
|
||||||
|
|
||||||
|
# push debug image to remote registry
|
||||||
|
docker buildx build --platform "${platforms}" \
|
||||||
|
--build-arg TAG="${tag}" \
|
||||||
|
-t "${registry}/${image_name}:${image_version}-debug" \
|
||||||
|
$(if [ "${LATEST}" == "yes" ]; then echo "-t ${registry}/${image_name}:debug"; fi) \
|
||||||
|
-f Dockerfile.debug . \
|
||||||
|
--push
|
||||||
|
|
|
@ -99,8 +99,11 @@ func (t Types) GetMediaTypes() string {
|
||||||
|
|
||||||
for _, j := range t {
|
for _, j := range t {
|
||||||
extensions := j.Extensions()
|
extensions := j.Extensions()
|
||||||
|
|
||||||
for _, v := range extensions {
|
for _, v := range extensions {
|
||||||
mediaTypes = append(mediaTypes, v)
|
if v != "" {
|
||||||
|
mediaTypes = append(mediaTypes, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue