2023-09-11 16:25:39 +00:00
|
|
|
/*
|
2024-01-14 18:39:14 +00:00
|
|
|
Copyright © 2024 Seednode <seednode@seedno.de>
|
2023-09-11 16:25:39 +00:00
|
|
|
*/
|
|
|
|
|
2023-09-12 21:32:19 +00:00
|
|
|
package types
|
2023-09-11 16:25:39 +00:00
|
|
|
|
|
|
|
import (
|
2024-01-30 19:07:59 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
|
|
|
"net/http"
|
2023-09-12 18:06:45 +00:00
|
|
|
"path/filepath"
|
2023-09-13 04:35:17 +00:00
|
|
|
"slices"
|
|
|
|
"strings"
|
2023-09-11 16:25:39 +00:00
|
|
|
)
|
|
|
|
|
2024-01-30 19:27:45 +00:00
|
|
|
const NonceLength = 6
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
var SupportedFormats = make(Types)
|
2023-09-13 04:35:17 +00:00
|
|
|
|
2023-09-13 01:56:39 +00:00
|
|
|
type Type interface {
|
2023-10-04 22:33:03 +00:00
|
|
|
// Returns either "inline" or "embed", depending on whether the file
|
|
|
|
// should be displayed inline (e.g. code) or embedded (e.g. images)
|
2023-09-15 20:13:45 +00:00
|
|
|
Type() string
|
2023-10-04 22:33:03 +00:00
|
|
|
|
2024-01-30 19:07:59 +00:00
|
|
|
// Adds a CSP header and returns a nonce to be used in generated pages
|
|
|
|
CSP(http.ResponseWriter) string
|
|
|
|
|
2023-10-04 22:33:03 +00:00
|
|
|
// Returns a CSS string used to format the corresponding page
|
2024-01-30 19:07:59 +00:00
|
|
|
CSS() string
|
2023-10-04 22:33:03 +00:00
|
|
|
|
|
|
|
// Returns an HTML <title> element for the specified file
|
2023-09-15 19:28:21 +00:00
|
|
|
Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) (string, error)
|
2023-10-04 22:33:03 +00:00
|
|
|
|
|
|
|
// Returns an HTML <body> element used to display the specified file
|
2024-01-30 19:07:59 +00:00
|
|
|
Body(rootUrl, fileUri, filePath, fileName, prefix, mime, nonce string) (string, error)
|
2023-10-04 22:33:03 +00:00
|
|
|
|
|
|
|
// Returns a map of file extensions to MIME type strings.
|
2023-09-13 01:56:39 +00:00
|
|
|
Extensions() map[string]string
|
2023-10-04 22:33:03 +00:00
|
|
|
|
2023-10-18 01:38:45 +00:00
|
|
|
// Given a file extension, returns the corresponding media type,
|
2023-10-04 22:33:03 +00:00
|
|
|
// if one exists. Otherwise, returns an empty string.
|
2023-10-09 14:47:17 +00:00
|
|
|
MediaType(extension string) string
|
2023-10-04 22:33:03 +00:00
|
|
|
|
|
|
|
// Optional function used to validate whether a given file matches this format.
|
|
|
|
// If no validation checks are needed, this function should always return true.
|
2023-09-13 01:56:39 +00:00
|
|
|
Validate(filePath string) bool
|
2023-09-11 16:25:39 +00:00
|
|
|
}
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
type Types map[string]Type
|
2023-09-11 16:25:39 +00:00
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func (t Types) Add(format Type) {
|
2023-09-14 22:37:22 +00:00
|
|
|
for k := range format.Extensions() {
|
2023-10-04 19:09:49 +00:00
|
|
|
_, exists := t[k]
|
2023-09-12 18:06:45 +00:00
|
|
|
if !exists {
|
2023-10-04 19:09:49 +00:00
|
|
|
t[k] = format
|
2023-09-11 16:25:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func (t Types) FileType(path string) Type {
|
|
|
|
fileType, exists := t[filepath.Ext(path)]
|
2023-09-12 18:06:45 +00:00
|
|
|
if exists {
|
2023-09-14 22:37:22 +00:00
|
|
|
return fileType
|
2023-09-11 16:25:39 +00:00
|
|
|
}
|
|
|
|
|
2023-09-14 22:37:22 +00:00
|
|
|
return nil
|
2023-09-11 16:25:39 +00:00
|
|
|
}
|
2023-09-13 04:35:17 +00:00
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func (t Types) Register(format Type) {
|
2023-09-13 14:26:15 +00:00
|
|
|
t.Add(format)
|
2023-09-13 04:35:17 +00:00
|
|
|
}
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func (t Types) Validate(path string) bool {
|
|
|
|
format, exists := t[filepath.Ext(path)]
|
2023-09-15 00:10:55 +00:00
|
|
|
if !exists {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return format.Validate(path)
|
|
|
|
}
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
func (t Types) GetExtensions() string {
|
2023-09-13 04:35:17 +00:00
|
|
|
var output strings.Builder
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
extensions := make([]string, len(t))
|
2023-09-13 04:35:17 +00:00
|
|
|
|
|
|
|
i := 0
|
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
for k := range t {
|
2023-09-13 04:35:17 +00:00
|
|
|
extensions[i] = k
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
slices.Sort(extensions)
|
|
|
|
|
|
|
|
for _, v := range extensions {
|
|
|
|
output.WriteString(v + "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return output.String()
|
|
|
|
}
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
func (t Types) GetMediaTypes() string {
|
2023-09-13 04:35:17 +00:00
|
|
|
var output strings.Builder
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
var mediaTypes []string
|
2023-09-13 04:35:17 +00:00
|
|
|
|
2023-10-04 19:09:49 +00:00
|
|
|
for _, j := range t {
|
2023-09-14 22:37:22 +00:00
|
|
|
extensions := j.Extensions()
|
2024-01-15 14:51:41 +00:00
|
|
|
|
2023-09-14 22:37:22 +00:00
|
|
|
for _, v := range extensions {
|
2024-01-15 14:51:41 +00:00
|
|
|
if v != "" {
|
|
|
|
mediaTypes = append(mediaTypes, v)
|
|
|
|
}
|
2023-09-14 22:37:22 +00:00
|
|
|
}
|
2023-09-13 04:35:17 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
mediaTypes = removeDuplicateStr(mediaTypes)
|
2023-09-14 22:37:22 +00:00
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
slices.Sort(mediaTypes)
|
2023-09-13 04:35:17 +00:00
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
for _, v := range mediaTypes {
|
2023-09-13 04:35:17 +00:00
|
|
|
output.WriteString(v + "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return output.String()
|
|
|
|
}
|
2023-09-14 22:37:22 +00:00
|
|
|
|
|
|
|
func removeDuplicateStr(strSlice []string) []string {
|
|
|
|
allKeys := make(map[string]bool)
|
|
|
|
list := []string{}
|
|
|
|
for _, item := range strSlice {
|
|
|
|
if _, value := allKeys[item]; !value {
|
|
|
|
allKeys[item] = true
|
|
|
|
list = append(list, item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
2024-01-30 19:07:59 +00:00
|
|
|
|
2024-01-30 19:27:45 +00:00
|
|
|
func GetNonce() string {
|
|
|
|
b := make([]byte, NonceLength)
|
2024-01-30 19:07:59 +00:00
|
|
|
if _, err := rand.Read(b); err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return hex.EncodeToString(b)
|
|
|
|
}
|