package adapters import ( "errors" "mime" "os" "path/filepath" "strings" "time" ) func SafeRel(root, requested string) (string, error) { s := 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) } func sizeOrZero(fi os.FileInfo) int64 { if fi == nil { return 0 } return fi.Size() } func modTimeOrZero(fi os.FileInfo) time.Time { if fi == nil { return time.Time{} } return fi.ModTime() } 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" }