Documentation
¶
Overview ¶
Example (Basic) ¶
Example_basic demonstrates serving files from a local directory.
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
"github.com/gorilla/mux"
)
func main() {
service := staticweb.NewService(nil)
// Using mock provider for example purposes
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"index.html": []byte("<html>test</html>"),
})
_ = service.Mount(staticweb.MountConfig{
URLPrefix: "/static",
Provider: provider,
})
router := mux.NewRouter()
router.PathPrefix("/").Handler(service.Handler())
fmt.Println("Serving files from ./public at /static")
}
Output: Serving files from ./public at /static
Example (ExtensionCache) ¶
Example_extensionCache demonstrates extension-based caching.
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
)
func main() {
service := staticweb.NewService(nil)
// Using mock provider for example purposes
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"index.html": []byte("<html>test</html>"),
"app.js": []byte("console.log('test')"),
})
// Different cache times per file type
cacheRules := map[string]int{
".html": 3600, // 1 hour
".js": 86400, // 1 day
".css": 86400, // 1 day
".png": 604800, // 1 week
}
service.Mount(staticweb.MountConfig{
URLPrefix: "/",
Provider: provider,
CachePolicy: staticweb.ExtensionCache(cacheRules, 3600), // default 1 hour
})
fmt.Println("Extension-based caching configured")
}
Output: Extension-based caching configured
Example (Multiple) ¶
Example_multiple demonstrates multiple mount points with different policies.
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
)
func main() {
service := staticweb.NewService(&staticweb.ServiceConfig{
DefaultCacheTime: 3600,
})
// Assets with long cache (using mock for example)
assetsProvider := staticwebtesting.NewMockProvider(map[string][]byte{
"app.js": []byte("console.log('test')"),
})
service.Mount(staticweb.MountConfig{
URLPrefix: "/assets",
Provider: assetsProvider,
CachePolicy: staticweb.SimpleCache(604800), // 1 week
})
// HTML with short cache (using mock for example)
htmlProvider := staticwebtesting.NewMockProvider(map[string][]byte{
"index.html": []byte("<html>test</html>"),
})
service.Mount(staticweb.MountConfig{
URLPrefix: "/",
Provider: htmlProvider,
CachePolicy: staticweb.SimpleCache(300), // 5 minutes
})
fmt.Println("Multiple mount points configured")
}
Output: Multiple mount points configured
Example (Reload) ¶
Example_reload demonstrates reloading content when files change.
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
)
func main() {
service := staticweb.NewService(nil)
// Create a provider
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"version.txt": []byte("v1.0.0"),
})
service.Mount(staticweb.MountConfig{
URLPrefix: "/static",
Provider: provider,
})
// Simulate updating the file
provider.AddFile("version.txt", []byte("v2.0.0"))
// Reload to pick up changes (in real usage with zip files)
err := service.Reload()
if err != nil {
fmt.Printf("Failed to reload: %v\n", err)
} else {
fmt.Println("Successfully reloaded static files")
}
}
Output: Successfully reloaded static files
Example (ReloadZip) ¶
Example_reloadZip demonstrates reloading a zip file provider.
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
)
func main() {
service := staticweb.NewService(nil)
// In production, you would use:
// provider, _ := staticweb.ZipProvider("./dist.zip")
// For this example, we use a mock
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"app.js": []byte("console.log('v1')"),
})
service.Mount(staticweb.MountConfig{
URLPrefix: "/app",
Provider: provider,
})
fmt.Println("Serving from zip file")
// When the zip file is updated, call Reload()
// service.Reload()
}
Output: Serving from zip file
Example (Spa) ¶
Example_spa demonstrates an SPA with HTML fallback routing.
package main
import (
"fmt"
"net/http"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
"github.com/gorilla/mux"
)
func main() {
service := staticweb.NewService(nil)
// Using mock provider for example purposes
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"index.html": []byte("<html>app</html>"),
})
_ = service.Mount(staticweb.MountConfig{
URLPrefix: "/",
Provider: provider,
FallbackStrategy: staticweb.HTMLFallback("index.html"),
})
router := mux.NewRouter()
// API routes take precedence
router.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("users"))
})
// Static files handle all other routes
router.PathPrefix("/").Handler(service.Handler())
fmt.Println("SPA with fallback to index.html")
}
Output: SPA with fallback to index.html
Example (Zip) ¶
Example_zip demonstrates serving from a zip file (concept).
package main
import (
"fmt"
"github.com/bitechdev/ResolveSpec/pkg/server/staticweb"
staticwebtesting "github.com/bitechdev/ResolveSpec/pkg/server/staticweb/testing"
)
func main() {
service := staticweb.NewService(nil)
// For actual usage, you would use:
// provider, err := staticweb.ZipProvider("./static.zip")
// For this example, we use a mock
provider := staticwebtesting.NewMockProvider(map[string][]byte{
"file.txt": []byte("content"),
})
service.Mount(staticweb.MountConfig{
URLPrefix: "/static",
Provider: provider,
})
fmt.Println("Serving from zip file")
}
Output: Serving from zip file
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CachePolicy ¶
type CachePolicy interface {
// GetCacheTime returns the cache duration in seconds for the given path.
// A value of 0 means no caching.
// A negative value can be used to indicate browser should revalidate.
GetCacheTime(path string) int
// GetCacheHeaders returns additional cache-related HTTP headers for the given path.
// Common headers include "Cache-Control", "Expires", "ETag", etc.
// Returns nil if no additional headers are needed.
GetCacheHeaders(path string) map[string]string
}
CachePolicy defines how files should be cached by browsers and proxies. Implementations must be safe for concurrent use.
func ExtensionCache ¶
func ExtensionCache(rules map[string]int, defaultTime int) CachePolicy
ExtensionCache creates an extension-based cache policy. rules maps file extensions (with leading dot) to cache times in seconds. defaultTime is used for files that don't match any rule.
func SimpleCache ¶
func SimpleCache(seconds int) CachePolicy
SimpleCache creates a simple cache policy with the given TTL in seconds.
type FallbackStrategy ¶
type FallbackStrategy interface {
// ShouldFallback determines if a fallback should be attempted for the given path.
// Returns true if the request should be handled by fallback logic.
ShouldFallback(path string) bool
// GetFallbackPath returns the path to serve instead of the originally requested path.
// This is only called if ShouldFallback returns true.
GetFallbackPath(path string) string
}
FallbackStrategy handles requests for files that don't exist. This is commonly used for Single Page Applications (SPAs) that use client-side routing. Implementations must be safe for concurrent use.
func DefaultExtensionFallback ¶
func DefaultExtensionFallback(fallbackPath string) FallbackStrategy
DefaultExtensionFallback creates an extension-based fallback with common web asset extensions.
func ExtensionFallback ¶
func ExtensionFallback(staticExtensions []string, fallbackPath string) FallbackStrategy
ExtensionFallback creates an extension-based fallback strategy. staticExtensions is a list of file extensions that should NOT use fallback. fallbackPath is the file to serve when fallback is triggered.
func HTMLFallback ¶
func HTMLFallback(indexFile string) FallbackStrategy
HTMLFallback creates a fallback strategy for SPAs that serves the given index file.
type FileSystemProvider ¶
type FileSystemProvider interface {
// Open opens the named file.
// The name is always a slash-separated path relative to the filesystem root.
Open(name string) (fs.File, error)
// Close releases any resources held by the provider.
// After Close is called, the provider should not be used.
Close() error
// Type returns the provider type (e.g., "local", "zip", "embed", "http", "s3").
// This is primarily for debugging and logging purposes.
Type() string
}
FileSystemProvider abstracts the source of files (local, zip, embedded, future: http, s3) Implementations must be safe for concurrent use.
func EmbedProvider ¶
func EmbedProvider(embedFS *embed.FS, zipFile string) (FileSystemProvider, error)
EmbedProvider creates a FileSystemProvider for an embedded filesystem. 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. The embedFS parameter can be any fs.FS, but is typically *embed.FS.
func LocalProvider ¶
func LocalProvider(path string) (FileSystemProvider, error)
LocalProvider creates a FileSystemProvider for a local directory.
func ZipProvider ¶
func ZipProvider(zipPath string) (FileSystemProvider, error)
ZipProvider creates a FileSystemProvider for a zip file.
type MIMETypeResolver ¶
type MIMETypeResolver interface {
// GetMIMEType returns the MIME type for the given file path.
// Returns empty string if the MIME type cannot be determined.
GetMIMEType(path string) string
// RegisterMIMEType registers a custom MIME type for the given file extension.
// The extension should include the leading dot (e.g., ".webp").
RegisterMIMEType(extension, mimeType string)
}
MIMETypeResolver determines the Content-Type for files. Implementations must be safe for concurrent use.
func DefaultMIMEResolver ¶
func DefaultMIMEResolver() MIMETypeResolver
DefaultMIMEResolver creates a MIME resolver with common web file types.
type MountConfig ¶
type MountConfig struct {
// URLPrefix is the URL path prefix where the filesystem should be mounted.
// Must start with "/" (e.g., "/static", "/", "/assets").
// Requests starting with this prefix will be handled by this mount point.
URLPrefix string
// Provider is the filesystem provider that supplies the files.
// Required.
Provider FileSystemProvider
// CachePolicy determines how files should be cached.
// If nil, the service's default cache policy is used.
CachePolicy CachePolicy
// MIMEResolver determines Content-Type headers for files.
// If nil, the service's default MIME resolver is used.
MIMEResolver MIMETypeResolver
// FallbackStrategy handles requests for missing files.
// If nil, no fallback is performed and 404 responses are returned.
FallbackStrategy FallbackStrategy
}
MountConfig configures a single mount point. A mount point connects a URL prefix to a filesystem provider with optional policies.
type ReloadableProvider ¶
type ReloadableProvider interface {
FileSystemProvider
// Reload refreshes the provider's content from the underlying source.
// For zip files, this reopens the zip archive.
// For local directories, this refreshes the filesystem view.
// Returns an error if the reload fails.
Reload() error
}
ReloadableProvider is an optional interface that providers can implement to support reloading/refreshing their content. This is useful for development workflows where the underlying files may change.
type ServiceConfig ¶
type ServiceConfig struct {
// DefaultCacheTime is the default cache duration in seconds.
// Used when a mount point doesn't specify a custom CachePolicy.
// Default: 172800 (48 hours)
DefaultCacheTime int
// DefaultMIMETypes is a map of file extensions to MIME types.
// These are added to the default MIME resolver.
// Extensions should include the leading dot (e.g., ".webp").
DefaultMIMETypes map[string]string
}
ServiceConfig configures the static file service.
func DefaultServiceConfig ¶
func DefaultServiceConfig() *ServiceConfig
DefaultServiceConfig returns a ServiceConfig with sensible defaults.
func (*ServiceConfig) Validate ¶
func (c *ServiceConfig) Validate() error
Validate checks if the ServiceConfig is valid.
type StaticFileService ¶
type StaticFileService interface {
// Mount adds a new mount point with the given configuration.
// Returns an error if the URLPrefix is already mounted or if the config is invalid.
Mount(config MountConfig) error
// Unmount removes the mount point at the given URL prefix.
// Returns an error if no mount point exists at that prefix.
// Automatically calls Close() on the provider to release resources.
Unmount(urlPrefix string) error
// ListMounts returns a sorted list of all mounted URL prefixes.
ListMounts() []string
// Reload reinitializes all filesystem providers.
// This can be used to pick up changes in the underlying filesystems.
// Not all providers may support reloading.
Reload() error
// Close releases all resources held by the service and all mounted providers.
// After Close is called, the service should not be used.
Close() error
// Handler returns an http.Handler that serves static files from all mount points.
// The handler performs longest-prefix matching to find the appropriate mount point.
// If no mount point matches, the handler returns without writing a response,
// allowing other handlers (like API routes) to process the request.
Handler() http.Handler
}
StaticFileService manages multiple mount points and serves static files. The service is safe for concurrent use.
func NewService ¶
func NewService(config *ServiceConfig) StaticFileService
NewService creates a new static file service with the given configuration. If config is nil, default configuration is used.