Files

67 lines
1.8 KiB
Go
Raw Permalink Normal View History

package webapp
import (
"context"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
// DirSource serves a plain local folder straight from disk — no bdrive remote
// or volume needed. Meant for debugging the webapp (and as a quick local
// markdown browser): the tree reflects the folder live, provenance is just
// file mtimes, and content streams from the filesystem.
type DirSource struct {
Root string
}
var skipNames = map[string]bool{".DS_Store": true, ".bdrive": true}
var skipDirs = map[string]bool{".git": true, ".bdrive": true}
func (d *DirSource) Files(_ context.Context) (map[string]FileInfo, error) {
files := make(map[string]FileInfo)
err := filepath.WalkDir(d.Root, func(p string, e fs.DirEntry, walkErr error) error {
if walkErr != nil {
return nil // skip unreadable entries
}
rel, err := filepath.Rel(d.Root, p)
if err != nil || rel == "." {
return nil
}
if e.IsDir() {
if skipDirs[e.Name()] {
return fs.SkipDir
}
return nil
}
if !e.Type().IsRegular() || skipNames[e.Name()] || strings.HasPrefix(e.Name(), ".bdrive-tmp-") {
return nil
}
info, err := e.Info()
if err != nil {
return nil
}
files[filepath.ToSlash(rel)] = FileInfo{
// Synthetic content identity for the ETag; changes when the
// file does, which is all revalidation needs.
Blob: fmt.Sprintf("dir-%d-%d", info.ModTime().UnixNano(), info.Size()),
Size: info.Size(),
Time: info.ModTime().UTC(),
}
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
// Open streams a file from disk. Paths are only ever snapshot map keys
// (produced by Files above), so they cannot escape Root.
func (d *DirSource) Open(_ context.Context, path string, _ FileInfo) (io.ReadCloser, error) {
return os.Open(filepath.Join(d.Root, filepath.FromSlash(path)))
}