2023-09-11 17:09:08 +00:00
|
|
|
/*
|
2024-01-14 18:39:14 +00:00
|
|
|
Copyright © 2024 Seednode <seednode@seedno.de>
|
2023-09-11 17:09:08 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2023-09-12 18:06:45 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2023-09-11 17:09:08 +00:00
|
|
|
"net/http"
|
2023-09-12 18:06:45 +00:00
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2023-09-11 17:09:08 +00:00
|
|
|
|
|
|
|
"github.com/julienschmidt/httprouter"
|
2023-09-12 18:06:45 +00:00
|
|
|
"github.com/yosssi/gohtml"
|
2023-09-12 21:32:19 +00:00
|
|
|
"seedno.de/seednode/roulette/types"
|
2023-09-11 17:09:08 +00:00
|
|
|
)
|
|
|
|
|
2023-10-20 22:18:35 +00:00
|
|
|
func paginateIndex(page int, fileCount int, ending bool) string {
|
2023-09-30 13:51:13 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
var html strings.Builder
|
|
|
|
|
2023-09-30 13:51:13 +00:00
|
|
|
if ending {
|
|
|
|
html.WriteString("<tr><td style=\"border-bottom:none;\">")
|
|
|
|
} else {
|
|
|
|
html.WriteString("<tr><td>")
|
|
|
|
}
|
|
|
|
|
2023-12-22 16:22:42 +00:00
|
|
|
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\">First</button>",
|
2023-10-10 02:52:06 +00:00
|
|
|
Prefix,
|
2023-12-22 16:22:42 +00:00
|
|
|
AdminPrefix,
|
2023-09-30 13:51:13 +00:00
|
|
|
firstPage))
|
|
|
|
|
2023-12-22 16:22:42 +00:00
|
|
|
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\"%s>Prev</button>",
|
2023-10-10 02:52:06 +00:00
|
|
|
Prefix,
|
2023-12-22 16:22:42 +00:00
|
|
|
AdminPrefix,
|
2023-09-30 13:51:13 +00:00
|
|
|
prevPage,
|
|
|
|
prevStatus))
|
|
|
|
|
2023-12-22 16:22:42 +00:00
|
|
|
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\"%s>Next</button>",
|
2023-10-10 02:52:06 +00:00
|
|
|
Prefix,
|
2023-12-22 16:22:42 +00:00
|
|
|
AdminPrefix,
|
2023-09-30 13:51:13 +00:00
|
|
|
nextPage,
|
|
|
|
nextStatus))
|
|
|
|
|
2023-12-22 16:22:42 +00:00
|
|
|
html.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '%s%s/index/html/%d';\">Last</button>",
|
2023-10-10 02:52:06 +00:00
|
|
|
Prefix,
|
2023-12-22 16:22:42 +00:00
|
|
|
AdminPrefix,
|
2023-09-30 13:51:13 +00:00
|
|
|
lastPage))
|
|
|
|
|
|
|
|
html.WriteString("</td></tr>\n")
|
|
|
|
|
|
|
|
return html.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httprouter.Handle {
|
2023-09-12 18:06:45 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
indexDump := index.List()
|
2023-09-12 18:06:45 +00:00
|
|
|
|
|
|
|
fileCount := len(indexDump)
|
|
|
|
|
|
|
|
var startIndex, stopIndex int
|
|
|
|
|
|
|
|
page, err := strconv.Atoi(p.ByName("page"))
|
|
|
|
if err != nil || page <= 0 {
|
|
|
|
startIndex = 0
|
|
|
|
stopIndex = fileCount
|
|
|
|
} else {
|
2023-09-26 08:49:20 +00:00
|
|
|
startIndex = ((page - 1) * PageLength)
|
|
|
|
stopIndex = (startIndex + PageLength)
|
2023-09-12 18:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if startIndex > (fileCount - 1) {
|
|
|
|
indexDump = []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if stopIndex > fileCount {
|
|
|
|
stopIndex = fileCount
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.SliceStable(indexDump, func(p, q int) bool {
|
|
|
|
return strings.ToLower(indexDump[p]) < strings.ToLower(indexDump[q])
|
|
|
|
})
|
|
|
|
|
|
|
|
var htmlBody strings.Builder
|
|
|
|
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
|
2023-09-13 14:26:15 +00:00
|
|
|
htmlBody.WriteString(faviconHtml)
|
2023-09-12 18:06:45 +00:00
|
|
|
htmlBody.WriteString(`<style>a{text-decoration:none;height:100%;width:100%;color:inherit;cursor:pointer}`)
|
2023-09-30 13:51:13 +00:00
|
|
|
htmlBody.WriteString(`table,td,tr{border:none;}td{border-bottom:1px solid black;}td{white-space:nowrap;padding:.5em}</style>`)
|
2023-09-12 18:06:45 +00:00
|
|
|
htmlBody.WriteString(fmt.Sprintf("<title>Index contains %d files</title></head><body><table>", fileCount))
|
2023-09-30 13:51:13 +00:00
|
|
|
|
2023-10-20 22:18:35 +00:00
|
|
|
if shouldPaginate && !DisableButtons {
|
|
|
|
htmlBody.WriteString(paginateIndex(page, fileCount, false))
|
2023-09-30 13:51:13 +00:00
|
|
|
}
|
|
|
|
|
2023-09-12 18:06:45 +00:00
|
|
|
if len(indexDump) > 0 {
|
|
|
|
for _, v := range indexDump[startIndex:stopIndex] {
|
|
|
|
var shouldSort = ""
|
|
|
|
|
|
|
|
if Sorting {
|
|
|
|
shouldSort = "?sort=asc"
|
|
|
|
}
|
2023-09-14 04:24:29 +00:00
|
|
|
htmlBody.WriteString(fmt.Sprintf("<tr><td><a href=\"%s%s%s%s\">%s</a></td></tr>\n", Prefix, mediaPrefix, v, shouldSort, v))
|
2023-09-12 18:06:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-20 22:18:35 +00:00
|
|
|
if shouldPaginate && !DisableButtons {
|
|
|
|
htmlBody.WriteString(paginateIndex(page, fileCount, true))
|
2023-09-12 18:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
htmlBody.WriteString(`</table></body></html>`)
|
|
|
|
|
2024-01-08 04:43:51 +00:00
|
|
|
written, err := io.WriteString(w, gohtml.Format(htmlBody.String()))
|
2023-09-12 18:06:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if Verbose {
|
2023-09-28 00:01:18 +00:00
|
|
|
fmt.Printf("%s | SERVE: HTML index page (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2024-01-08 04:43:51 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-12 18:06:45 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
func serveIndexJson(args []string, index *fileIndex, errorChannel chan<- error) httprouter.Handle {
|
2023-09-12 18:06:45 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
indexedFiles := index.List()
|
2023-09-12 18:06:45 +00:00
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
fileCount := len(indexedFiles)
|
2023-09-12 18:06:45 +00:00
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
sort.SliceStable(indexedFiles, func(p, q int) bool {
|
|
|
|
return strings.ToLower(indexedFiles[p]) < strings.ToLower(indexedFiles[q])
|
2023-09-12 18:06:45 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
var startIndex, stopIndex int
|
|
|
|
|
|
|
|
page, err := strconv.Atoi(p.ByName("page"))
|
|
|
|
if err != nil || page <= 0 {
|
|
|
|
startIndex = 0
|
|
|
|
stopIndex = fileCount
|
|
|
|
} else {
|
2023-09-26 08:49:20 +00:00
|
|
|
startIndex = ((page - 1) * PageLength)
|
|
|
|
stopIndex = (startIndex + PageLength)
|
2023-09-12 18:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if startIndex > (fileCount - 1) {
|
2023-09-28 15:09:45 +00:00
|
|
|
indexedFiles = []string{}
|
2023-09-12 18:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if stopIndex > fileCount {
|
|
|
|
stopIndex = fileCount
|
|
|
|
}
|
|
|
|
|
2023-09-28 15:09:45 +00:00
|
|
|
response, err := json.MarshalIndent(indexedFiles[startIndex:stopIndex], "", " ")
|
2023-09-12 18:06:45 +00:00
|
|
|
if err != nil {
|
2023-09-15 19:28:21 +00:00
|
|
|
errorChannel <- err
|
2023-09-12 18:06:45 +00:00
|
|
|
|
|
|
|
serverError(w, r, nil)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-10-04 20:47:43 +00:00
|
|
|
written, err := w.Write(response)
|
|
|
|
if err != nil {
|
|
|
|
errorChannel <- err
|
|
|
|
}
|
2023-09-12 18:06:45 +00:00
|
|
|
|
|
|
|
if Verbose {
|
2023-09-26 10:29:55 +00:00
|
|
|
fmt.Printf("%s | SERVE: JSON index page (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2023-10-04 20:47:43 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-12 18:06:45 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-10-04 20:47:43 +00:00
|
|
|
func serveAvailableExtensions(errorChannel chan<- error) httprouter.Handle {
|
2023-09-13 03:35:15 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
2023-10-04 20:47:43 +00:00
|
|
|
written, err := w.Write([]byte(types.SupportedFormats.GetExtensions()))
|
|
|
|
if err != nil {
|
|
|
|
errorChannel <- err
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
if Verbose {
|
2023-09-26 10:29:55 +00:00
|
|
|
fmt.Printf("%s | SERVE: Available extension list (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2023-10-04 20:47:43 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-13 04:35:17 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
2023-09-13 03:35:15 +00:00
|
|
|
}
|
2023-09-13 04:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-10-04 20:47:43 +00:00
|
|
|
func serveEnabledExtensions(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
2023-09-13 04:35:17 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
2023-10-04 20:47:43 +00:00
|
|
|
written, err := w.Write([]byte(formats.GetExtensions()))
|
|
|
|
if err != nil {
|
|
|
|
errorChannel <- err
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
|
|
|
if Verbose {
|
2023-09-26 10:29:55 +00:00
|
|
|
fmt.Printf("%s | SERVE: Registered extension list (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2023-10-04 20:47:43 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-13 03:35:15 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
func serveAvailableMediaTypes(errorChannel chan<- error) httprouter.Handle {
|
2023-09-13 03:35:15 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
written, err := w.Write([]byte(types.SupportedFormats.GetMediaTypes()))
|
2023-10-04 20:47:43 +00:00
|
|
|
if err != nil {
|
|
|
|
errorChannel <- err
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
if Verbose {
|
2023-10-09 14:47:17 +00:00
|
|
|
fmt.Printf("%s | SERVE: Available media type list (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2023-10-04 20:47:43 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-13 04:35:17 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
2023-09-13 03:35:15 +00:00
|
|
|
}
|
2023-09-13 04:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
func serveEnabledMediaTypes(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
2023-09-13 04:35:17 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
|
|
|
startTime := time.Now()
|
2023-09-13 03:35:15 +00:00
|
|
|
|
2023-12-17 11:16:46 +00:00
|
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
written, err := w.Write([]byte(formats.GetMediaTypes()))
|
2023-10-04 20:47:43 +00:00
|
|
|
if err != nil {
|
|
|
|
errorChannel <- err
|
|
|
|
}
|
2023-09-13 03:35:15 +00:00
|
|
|
|
|
|
|
if Verbose {
|
2023-10-09 14:47:17 +00:00
|
|
|
fmt.Printf("%s | SERVE: Registered media type list (%s) to %s in %s\n",
|
2023-09-13 14:26:15 +00:00
|
|
|
startTime.Format(logDate),
|
2023-10-04 20:47:43 +00:00
|
|
|
humanReadableSize(written),
|
2023-09-13 03:35:15 +00:00
|
|
|
realIP(r),
|
|
|
|
time.Since(startTime).Round(time.Microsecond),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-13 17:00:24 +00:00
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func registerInfoHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) {
|
2023-09-28 15:09:45 +00:00
|
|
|
if Index {
|
2023-12-17 12:35:03 +00:00
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/index/html", serveIndexHtml(args, index, false))
|
2023-09-13 17:00:24 +00:00
|
|
|
if PageLength != 0 {
|
2023-12-17 12:35:03 +00:00
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/index/html/:page", serveIndexHtml(args, index, true))
|
2023-09-13 17:00:24 +00:00
|
|
|
}
|
|
|
|
|
2023-12-17 12:35:03 +00:00
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/index/json", serveIndexJson(args, index, errorChannel))
|
2023-09-13 17:00:24 +00:00
|
|
|
if PageLength != 0 {
|
2023-12-17 12:35:03 +00:00
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/index/json/:page", serveIndexJson(args, index, errorChannel))
|
2023-09-13 17:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 12:35:03 +00:00
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/extensions/available", serveAvailableExtensions(errorChannel))
|
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/extensions/enabled", serveEnabledExtensions(formats, errorChannel))
|
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/types/available", serveAvailableMediaTypes(errorChannel))
|
|
|
|
registerHandler(mux, Prefix+AdminPrefix+"/types/enabled", serveEnabledMediaTypes(formats, errorChannel))
|
2023-09-13 17:00:24 +00:00
|
|
|
}
|