2023-02-09 12:16:36 +01:00
|
|
|
package bundle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2023-10-11 11:18:32 +02:00
|
|
|
"io"
|
2023-02-09 12:16:36 +01:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"gitlab.com/wpetit/goweb/logger"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FileSystem struct {
|
|
|
|
prefix string
|
|
|
|
bundle Bundle
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fs *FileSystem) Open(name string) (http.File, error) {
|
|
|
|
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) ||
|
|
|
|
strings.Contains(name, "\x00") {
|
|
|
|
return nil, errors.New("http: invalid character in file path")
|
|
|
|
}
|
|
|
|
|
|
|
|
p := path.Join(fs.prefix, strings.TrimPrefix(name, "/"))
|
|
|
|
|
|
|
|
ctx := logger.With(
|
|
|
|
context.Background(),
|
|
|
|
logger.F("filename", name),
|
|
|
|
)
|
|
|
|
|
|
|
|
logger.Debug(ctx, "opening file")
|
|
|
|
|
|
|
|
readCloser, fileInfo, err := fs.bundle.File(p)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Error(ctx, "could not open bundle file", logger.E(err))
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "could not open bundle file '%s'", p)
|
|
|
|
}
|
|
|
|
defer readCloser.Close()
|
|
|
|
|
|
|
|
file := &File{
|
|
|
|
fi: fileInfo,
|
|
|
|
}
|
|
|
|
|
|
|
|
if fileInfo.IsDir() {
|
|
|
|
files, err := fs.bundle.Dir(p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(ctx, "could not read bundle directory", logger.E(err))
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "could not read bundle directory '%s'", p)
|
|
|
|
}
|
|
|
|
|
|
|
|
file.files = files
|
|
|
|
} else {
|
2023-10-11 11:18:32 +02:00
|
|
|
data, err := io.ReadAll(readCloser)
|
2023-02-09 12:16:36 +01:00
|
|
|
if err != nil {
|
|
|
|
logger.Error(ctx, "could not read bundle file", logger.E(err))
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "could not read bundle file '%s'", p)
|
|
|
|
}
|
|
|
|
|
|
|
|
file.Reader = bytes.NewReader(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
return file, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFileSystem(prefix string, bundle Bundle) *FileSystem {
|
|
|
|
return &FileSystem{prefix, bundle}
|
|
|
|
}
|
|
|
|
|
|
|
|
type File struct {
|
|
|
|
*bytes.Reader
|
|
|
|
fi os.FileInfo
|
|
|
|
files []os.FileInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// A noop-closer.
|
|
|
|
func (f *File) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
|
|
|
if f.fi.IsDir() && f.files != nil {
|
|
|
|
return f.files, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, os.ErrNotExist
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *File) Stat() (os.FileInfo, error) {
|
|
|
|
return f.fi, nil
|
|
|
|
}
|
2023-03-01 14:39:36 +01:00
|
|
|
|
|
|
|
var _ http.FileSystem = &FileSystem{}
|