mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-04-05 23:46:16 +00:00
* Implemented alternate path handling for file retrieval * Improved error messaging for file not found scenarios
140 lines
3.5 KiB
Go
140 lines
3.5 KiB
Go
package providers
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// LocalFSProvider serves files from a local directory.
|
|
type LocalFSProvider struct {
|
|
path string
|
|
stripPrefix string
|
|
fs fs.FS
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewLocalFSProvider creates a new LocalFSProvider for the given directory path.
|
|
// The path must be an absolute path to an existing directory.
|
|
func NewLocalFSProvider(path string) (*LocalFSProvider, error) {
|
|
// Validate that the path exists and is a directory
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to stat directory: %w", err)
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
return nil, fmt.Errorf("path is not a directory: %s", path)
|
|
}
|
|
|
|
// Convert to absolute path
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get absolute path: %w", err)
|
|
}
|
|
|
|
return &LocalFSProvider{
|
|
path: absPath,
|
|
fs: os.DirFS(absPath),
|
|
}, nil
|
|
}
|
|
|
|
// 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) {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
// Apply prefix stripping by prepending the prefix to the requested path
|
|
actualPath := name
|
|
alternatePath := ""
|
|
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)
|
|
alternatePath = cleanName
|
|
} else {
|
|
actualPath = cleanName
|
|
}
|
|
}
|
|
|
|
// First try the actual path with prefix
|
|
if file, err := p.fs.Open(actualPath); err == nil {
|
|
return file, nil
|
|
}
|
|
|
|
// If alternate path is different, try it as well
|
|
if alternatePath != "" && alternatePath != actualPath {
|
|
if file, err := p.fs.Open(alternatePath); err == nil {
|
|
return file, nil
|
|
}
|
|
}
|
|
|
|
// If both attempts fail, return the error from the first attempt
|
|
return nil, fmt.Errorf("file not found: %s", name)
|
|
}
|
|
|
|
// Close releases any resources held by the provider.
|
|
// For local filesystem, this is a no-op since os.DirFS doesn't hold resources.
|
|
func (p *LocalFSProvider) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// Type returns "local".
|
|
func (p *LocalFSProvider) Type() string {
|
|
return "local"
|
|
}
|
|
|
|
// Path returns the absolute path to the directory being served.
|
|
func (p *LocalFSProvider) Path() string {
|
|
return p.path
|
|
}
|
|
|
|
// Reload refreshes the filesystem view.
|
|
// 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 {
|
|
return fmt.Errorf("failed to stat directory: %w", err)
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
return fmt.Errorf("path is no longer a directory: %s", p.path)
|
|
}
|
|
|
|
// Recreate the DirFS
|
|
p.fs = os.DirFS(p.path)
|
|
|
|
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
|
|
}
|