mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-20 09:34:27 +00:00
staticweb package for easier static web server hosting
This commit is contained in:
119
pkg/server/staticweb/providers/embed.go
Normal file
119
pkg/server/staticweb/providers/embed.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"sync"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/server/zipfs"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func NewEmbedFSProvider(embedFS fs.FS, zipFile string) (*EmbedFSProvider, error) {
|
||||
if embedFS == nil {
|
||||
return nil, fmt.Errorf("embedded filesystem cannot be nil")
|
||||
}
|
||||
|
||||
// Try to cast to *embed.FS for tracking purposes
|
||||
var embedFSPtr *embed.FS
|
||||
if efs, ok := embedFS.(*embed.FS); ok {
|
||||
embedFSPtr = efs
|
||||
}
|
||||
|
||||
provider := &EmbedFSProvider{
|
||||
embedFS: embedFSPtr,
|
||||
zipFile: zipFile,
|
||||
}
|
||||
|
||||
// If zipFile is specified, open it as a zip archive
|
||||
if zipFile != "" {
|
||||
// Read the zip file from the embedded FS
|
||||
// We need to check if the FS supports ReadFile
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
if readFileFS, ok := embedFS.(interface{ ReadFile(string) ([]byte, error) }); ok {
|
||||
data, err = readFileFS.ReadFile(zipFile)
|
||||
} else {
|
||||
// Fall back to Open and reading
|
||||
file, openErr := embedFS.Open(zipFile)
|
||||
if openErr != nil {
|
||||
return nil, fmt.Errorf("failed to open embedded zip file: %w", openErr)
|
||||
}
|
||||
defer file.Close()
|
||||
data, err = io.ReadAll(file)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read embedded zip file: %w", err)
|
||||
}
|
||||
|
||||
// Create a zip reader from the data
|
||||
reader := bytes.NewReader(data)
|
||||
zipReader, err := zip.NewReader(reader, int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create zip reader: %w", err)
|
||||
}
|
||||
|
||||
provider.zipReader = zipReader
|
||||
provider.fs = zipfs.NewZipFS(zipReader)
|
||||
} else {
|
||||
// Use the embedded FS directly
|
||||
provider.fs = embedFS
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// Open opens the named file from the embedded filesystem.
|
||||
func (p *EmbedFSProvider) Open(name string) (fs.File, error) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if p.fs == nil {
|
||||
return nil, fmt.Errorf("embedded filesystem is closed")
|
||||
}
|
||||
|
||||
return p.fs.Open(name)
|
||||
}
|
||||
|
||||
// Close releases any resources held by the provider.
|
||||
// For embedded filesystems, this is mostly a no-op since Go manages the lifecycle.
|
||||
func (p *EmbedFSProvider) Close() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Clear references to allow garbage collection
|
||||
p.fs = nil
|
||||
p.zipReader = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns "embed" or "embed-zip" depending on the configuration.
|
||||
func (p *EmbedFSProvider) Type() string {
|
||||
if p.zipFile != "" {
|
||||
return "embed-zip"
|
||||
}
|
||||
return "embed"
|
||||
}
|
||||
|
||||
// ZipFile returns the path to the zip file within the embedded FS, if any.
|
||||
func (p *EmbedFSProvider) ZipFile() string {
|
||||
return p.zipFile
|
||||
}
|
||||
Reference in New Issue
Block a user