mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-05 03:14:25 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2017465cb8 | |||
| d33747c2d3 |
@@ -34,6 +34,29 @@ type ReloadableProvider interface {
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// PrefixStrippingProvider is an optional interface that providers can implement
|
||||
// to support stripping path prefixes from requested paths.
|
||||
// This is useful when files are stored in a subdirectory but should be accessible
|
||||
// at the root level (e.g., files at "/dist/assets" accessible via "/assets").
|
||||
type PrefixStrippingProvider interface {
|
||||
// WithStripPrefix sets the prefix to strip from requested paths.
|
||||
// For example, WithStripPrefix("/dist") will make files at "/dist/assets"
|
||||
// accessible via "/assets".
|
||||
WithStripPrefix(prefix string)
|
||||
|
||||
// StripPrefix returns the configured strip prefix.
|
||||
StripPrefix() string
|
||||
}
|
||||
|
||||
// WithStripPrefix is a helper function that sets the strip prefix on a provider
|
||||
// if it implements PrefixStrippingProvider. Returns the provider for method chaining.
|
||||
func WithStripPrefix(provider FileSystemProvider, prefix string) FileSystemProvider {
|
||||
if p, ok := provider.(PrefixStrippingProvider); ok {
|
||||
p.WithStripPrefix(prefix)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
// CachePolicy defines how files should be cached by browsers and proxies.
|
||||
// Implementations must be safe for concurrent use.
|
||||
type CachePolicy interface {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/server/zipfs"
|
||||
@@ -15,16 +17,18 @@ import (
|
||||
// EmbedFSProvider serves files from an embedded filesystem.
|
||||
// It supports both direct embedded directories and embedded zip files.
|
||||
type EmbedFSProvider struct {
|
||||
embedFS *embed.FS
|
||||
zipFile string // Optional: path within embedded FS to zip file
|
||||
zipReader *zip.Reader
|
||||
fs fs.FS
|
||||
mu sync.RWMutex
|
||||
embedFS *embed.FS
|
||||
zipFile string // Optional: path within embedded FS to zip file
|
||||
stripPrefix string // Optional: prefix to strip from requested paths (e.g., "/dist")
|
||||
zipReader *zip.Reader
|
||||
fs fs.FS
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewEmbedFSProvider creates a new EmbedFSProvider.
|
||||
// If zipFile is empty, the embedded FS is used directly.
|
||||
// If zipFile is specified, it's treated as a path to a zip file within the embedded FS.
|
||||
// Use WithStripPrefix to configure path prefix stripping.
|
||||
func NewEmbedFSProvider(embedFS fs.FS, zipFile string) (*EmbedFSProvider, error) {
|
||||
if embedFS == nil {
|
||||
return nil, fmt.Errorf("embedded filesystem cannot be nil")
|
||||
@@ -81,6 +85,9 @@ func NewEmbedFSProvider(embedFS fs.FS, zipFile string) (*EmbedFSProvider, error)
|
||||
}
|
||||
|
||||
// Open opens the named file from the embedded filesystem.
|
||||
// If a strip prefix is configured, it prepends the prefix to the requested path.
|
||||
// For example, with stripPrefix="/dist", requesting "/assets/style.css" will
|
||||
// open "/dist/assets/style.css" from the embedded filesystem.
|
||||
func (p *EmbedFSProvider) Open(name string) (fs.File, error) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
@@ -89,7 +96,21 @@ func (p *EmbedFSProvider) Open(name string) (fs.File, error) {
|
||||
return nil, fmt.Errorf("embedded filesystem is closed")
|
||||
}
|
||||
|
||||
return p.fs.Open(name)
|
||||
// Apply prefix stripping by prepending the prefix to the requested path
|
||||
actualPath := name
|
||||
if p.stripPrefix != "" {
|
||||
// Clean the paths to handle leading/trailing slashes
|
||||
prefix := strings.Trim(p.stripPrefix, "/")
|
||||
cleanName := strings.TrimPrefix(name, "/")
|
||||
|
||||
if prefix != "" {
|
||||
actualPath = path.Join(prefix, cleanName)
|
||||
} else {
|
||||
actualPath = cleanName
|
||||
}
|
||||
}
|
||||
|
||||
return p.fs.Open(actualPath)
|
||||
}
|
||||
|
||||
// Close releases any resources held by the provider.
|
||||
@@ -117,3 +138,19 @@ func (p *EmbedFSProvider) Type() string {
|
||||
func (p *EmbedFSProvider) ZipFile() string {
|
||||
return p.zipFile
|
||||
}
|
||||
|
||||
// WithStripPrefix sets the prefix to strip from requested paths.
|
||||
// For example, WithStripPrefix("/dist") will make files at "/dist/assets"
|
||||
// accessible via "/assets".
|
||||
func (p *EmbedFSProvider) WithStripPrefix(prefix string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.stripPrefix = prefix
|
||||
}
|
||||
|
||||
// StripPrefix returns the configured strip prefix.
|
||||
func (p *EmbedFSProvider) StripPrefix() string {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.stripPrefix
|
||||
}
|
||||
|
||||
@@ -4,13 +4,18 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// LocalFSProvider serves files from a local directory.
|
||||
type LocalFSProvider struct {
|
||||
path string
|
||||
fs fs.FS
|
||||
path string
|
||||
stripPrefix string
|
||||
fs fs.FS
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewLocalFSProvider creates a new LocalFSProvider for the given directory path.
|
||||
@@ -39,8 +44,28 @@ func NewLocalFSProvider(path string) (*LocalFSProvider, error) {
|
||||
}
|
||||
|
||||
// Open opens the named file from the local directory.
|
||||
// If a strip prefix is configured, it prepends the prefix to the requested path.
|
||||
// For example, with stripPrefix="/dist", requesting "/assets/style.css" will
|
||||
// open "/dist/assets/style.css" from the local filesystem.
|
||||
func (p *LocalFSProvider) Open(name string) (fs.File, error) {
|
||||
return p.fs.Open(name)
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
// Apply prefix stripping by prepending the prefix to the requested path
|
||||
actualPath := name
|
||||
if p.stripPrefix != "" {
|
||||
// Clean the paths to handle leading/trailing slashes
|
||||
prefix := strings.Trim(p.stripPrefix, "/")
|
||||
cleanName := strings.TrimPrefix(name, "/")
|
||||
|
||||
if prefix != "" {
|
||||
actualPath = path.Join(prefix, cleanName)
|
||||
} else {
|
||||
actualPath = cleanName
|
||||
}
|
||||
}
|
||||
|
||||
return p.fs.Open(actualPath)
|
||||
}
|
||||
|
||||
// Close releases any resources held by the provider.
|
||||
@@ -63,6 +88,9 @@ func (p *LocalFSProvider) Path() string {
|
||||
// For local directories, os.DirFS automatically picks up changes,
|
||||
// so this recreates the DirFS to ensure a fresh view.
|
||||
func (p *LocalFSProvider) Reload() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Verify the directory still exists
|
||||
info, err := os.Stat(p.path)
|
||||
if err != nil {
|
||||
@@ -78,3 +106,19 @@ func (p *LocalFSProvider) Reload() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithStripPrefix sets the prefix to strip from requested paths.
|
||||
// For example, WithStripPrefix("/dist") will make files at "/dist/assets"
|
||||
// accessible via "/assets".
|
||||
func (p *LocalFSProvider) WithStripPrefix(prefix string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.stripPrefix = prefix
|
||||
}
|
||||
|
||||
// StripPrefix returns the configured strip prefix.
|
||||
func (p *LocalFSProvider) StripPrefix() string {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.stripPrefix
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/server/zipfs"
|
||||
@@ -12,10 +14,11 @@ import (
|
||||
|
||||
// ZipFSProvider serves files from a zip file.
|
||||
type ZipFSProvider struct {
|
||||
zipPath string
|
||||
zipReader *zip.ReadCloser
|
||||
zipFS *zipfs.ZipFS
|
||||
mu sync.RWMutex
|
||||
zipPath string
|
||||
stripPrefix string
|
||||
zipReader *zip.ReadCloser
|
||||
zipFS *zipfs.ZipFS
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewZipFSProvider creates a new ZipFSProvider for the given zip file path.
|
||||
@@ -40,6 +43,9 @@ func NewZipFSProvider(zipPath string) (*ZipFSProvider, error) {
|
||||
}
|
||||
|
||||
// Open opens the named file from the zip archive.
|
||||
// If a strip prefix is configured, it prepends the prefix to the requested path.
|
||||
// For example, with stripPrefix="/dist", requesting "/assets/style.css" will
|
||||
// open "/dist/assets/style.css" from the zip filesystem.
|
||||
func (p *ZipFSProvider) Open(name string) (fs.File, error) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
@@ -48,7 +54,21 @@ func (p *ZipFSProvider) Open(name string) (fs.File, error) {
|
||||
return nil, fmt.Errorf("zip filesystem is closed")
|
||||
}
|
||||
|
||||
return p.zipFS.Open(name)
|
||||
// Apply prefix stripping by prepending the prefix to the requested path
|
||||
actualPath := name
|
||||
if p.stripPrefix != "" {
|
||||
// Clean the paths to handle leading/trailing slashes
|
||||
prefix := strings.Trim(p.stripPrefix, "/")
|
||||
cleanName := strings.TrimPrefix(name, "/")
|
||||
|
||||
if prefix != "" {
|
||||
actualPath = path.Join(prefix, cleanName)
|
||||
} else {
|
||||
actualPath = cleanName
|
||||
}
|
||||
}
|
||||
|
||||
return p.zipFS.Open(actualPath)
|
||||
}
|
||||
|
||||
// Close releases resources held by the zip reader.
|
||||
@@ -100,3 +120,19 @@ func (p *ZipFSProvider) Reload() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithStripPrefix sets the prefix to strip from requested paths.
|
||||
// For example, WithStripPrefix("/dist") will make files at "/dist/assets"
|
||||
// accessible via "/assets".
|
||||
func (p *ZipFSProvider) WithStripPrefix(prefix string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.stripPrefix = prefix
|
||||
}
|
||||
|
||||
// StripPrefix returns the configured strip prefix.
|
||||
func (p *ZipFSProvider) StripPrefix() string {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
return p.stripPrefix
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user