wrip
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
module git.max-richter.dev/max/marka/server-new
|
|
||||||
|
|
||||||
go 1.25.1
|
|
@@ -1,16 +1,3 @@
|
|||||||
module git.max-richter.dev/max/marka/server
|
module git.max-richter.dev/max/marka/server-new
|
||||||
|
|
||||||
go 1.24.5
|
go 1.25.1
|
||||||
|
|
||||||
require git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e
|
|
||||||
|
|
||||||
require (
|
|
||||||
git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567 // indirect
|
|
||||||
git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e // indirect
|
|
||||||
git.max-richter.dev/max/marka/template v0.0.0-20250817132016-6db87db32567 // indirect
|
|
||||||
git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a // indirect
|
|
||||||
github.com/agext/levenshtein v1.2.3 // indirect
|
|
||||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
|
||||||
go.yaml.in/yaml/v4 v4.0.0-rc.1 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
|
||||||
)
|
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
package fsx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mime"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var textPlainExtensions = map[string]bool{
|
|
||||||
".txt": true,
|
|
||||||
".log": true,
|
|
||||||
".json": true,
|
|
||||||
".yaml": true,
|
|
||||||
".yml": true,
|
|
||||||
".toml": true,
|
|
||||||
".xml": true,
|
|
||||||
".csv": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
func ContentTypeFor(name string) string {
|
|
||||||
ext := strings.ToLower(filepath.Ext(name))
|
|
||||||
switch ext {
|
|
||||||
case ".md", ".markdown", ".mdown":
|
|
||||||
return "application/markdown"
|
|
||||||
}
|
|
||||||
if ct := mime.TypeByExtension(ext); ct != "" {
|
|
||||||
return ct
|
|
||||||
}
|
|
||||||
if textPlainExtensions[ext] {
|
|
||||||
return "text/plain; charset=utf-8"
|
|
||||||
}
|
|
||||||
return "application/octet-stream"
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
package fsx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CleanURLLike(p string) string {
|
|
||||||
p = strings.TrimSpace(p)
|
|
||||||
if p == "" || p == "/" {
|
|
||||||
return "/"
|
|
||||||
}
|
|
||||||
parts := []string{}
|
|
||||||
for seg := range strings.SplitSeq(strings.ReplaceAll(p, "/", "/"), "/") {
|
|
||||||
switch seg {
|
|
||||||
case "", ".":
|
|
||||||
continue
|
|
||||||
case "..":
|
|
||||||
if len(parts) > 0 {
|
|
||||||
parts = parts[:len(parts)-1]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
parts = append(parts, seg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "/" + strings.Join(parts, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SafeRel(root, requested string) (string, error) {
|
|
||||||
s := CleanURLLike(requested)
|
|
||||||
if after, ok := strings.CutPrefix(s, "/"); ok {
|
|
||||||
s = after
|
|
||||||
}
|
|
||||||
full := filepath.Join(root, filepath.FromSlash(s))
|
|
||||||
rel, err := filepath.Rel(root, full)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if rel == "." {
|
|
||||||
return "/", nil
|
|
||||||
}
|
|
||||||
sep := string(filepath.Separator)
|
|
||||||
if strings.HasPrefix(rel, "..") || strings.Contains(rel, ".."+sep) {
|
|
||||||
return "", errors.New("path escapes root")
|
|
||||||
}
|
|
||||||
return "/" + filepath.ToSlash(rel), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResponsePath(root, full string) string {
|
|
||||||
rel, err := filepath.Rel(root, full)
|
|
||||||
if err != nil || rel == "." {
|
|
||||||
return "/"
|
|
||||||
}
|
|
||||||
return "/" + filepath.ToSlash(rel)
|
|
||||||
}
|
|
@@ -1,38 +0,0 @@
|
|||||||
// Package handlers provides HTTP handlers for the file system.
|
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"git.max-richter.dev/max/marka/server/internal/fsx"
|
|
||||||
"git.max-richter.dev/max/marka/server/internal/httpx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type File struct{ root string }
|
|
||||||
|
|
||||||
func NewFile(root string) http.Handler { return &File{root: root} }
|
|
||||||
|
|
||||||
func (h *File) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
reqPath := r.URL.Path
|
|
||||||
if reqPath == "" {
|
|
||||||
reqPath = "/"
|
|
||||||
}
|
|
||||||
cleanRel, err := fsx.SafeRel(h.root, reqPath)
|
|
||||||
if err != nil {
|
|
||||||
httpx.WriteError(w, http.StatusBadRequest, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
target := filepath.Join(h.root, filepath.FromSlash(cleanRel))
|
|
||||||
|
|
||||||
switch r.Method {
|
|
||||||
case http.MethodGet:
|
|
||||||
h.get(w, r, target)
|
|
||||||
case http.MethodPost:
|
|
||||||
h.post(w, r, target)
|
|
||||||
default:
|
|
||||||
httpx.WriteError(w, http.StatusMethodNotAllowed, errors.New("method not allowed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Reference in New Issue
Block a user