about summary refs log tree commit diff stats
path: root/internal/storage/files
diff options
context:
space:
mode:
authorAlan Pearce2025-01-27 21:42:41 +0100
committerAlan Pearce2025-01-28 01:28:32 +0100
commit127a675fc7cd9cdb65e4b4caac21e0f259102ee8 (patch)
tree9f2e269187702a4ea26bd776547d99e3832c60a6 /internal/storage/files
parent04809ffd7971032818238db14feb6d3c95470e3b (diff)
downloadwebsite-127a675fc7cd9cdb65e4b4caac21e0f259102ee8.tar.lz
website-127a675fc7cd9cdb65e4b4caac21e0f259102ee8.tar.zst
website-127a675fc7cd9cdb65e4b4caac21e0f259102ee8.zip
serve files from Storage implementation
Diffstat (limited to 'internal/storage/files')
-rw-r--r--internal/storage/files/file.go102
-rw-r--r--internal/storage/files/reader.go84
-rw-r--r--internal/storage/files/writer.go6
3 files changed, 117 insertions, 75 deletions
diff --git a/internal/storage/files/file.go b/internal/storage/files/file.go
new file mode 100644
index 0000000..a71811c
--- /dev/null
+++ b/internal/storage/files/file.go
@@ -0,0 +1,102 @@
+package files
+
+import (
+	"fmt"
+	"hash/fnv"
+	"io"
+	"mime"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"gitlab.com/tozd/go/errors"
+	"go.alanpearce.eu/website/internal/storage"
+)
+
+type File struct {
+	storage.File
+}
+
+var encodings = map[string]string{
+	"br":   ".br",
+	"gzip": ".gz",
+}
+
+func (r *Reader) OpenFile(path string, filename string) (*File, error) {
+	f, err := os.Open(filename)
+	if err != nil {
+		return nil, errors.WithMessage(err, "could not open file for reading")
+	}
+	stat, err := f.Stat()
+	if err != nil {
+		return nil, errors.WithMessage(err, "could not stat file")
+	}
+
+	etag, err := etag(f)
+	if err != nil {
+		return nil, errors.WithMessage(err, "could not calculate etag")
+	}
+
+	file := &File{
+		File: storage.File{
+			Path:         path,
+			ContentType:  mime.TypeByExtension(filepath.Ext(filename)),
+			LastModified: stat.ModTime(),
+			Etag:         etag,
+			Encodings: map[string]io.ReadSeekCloser{
+				"identity": f,
+			},
+		},
+	}
+
+	for enc, suffix := range encodings {
+		_, err := os.Stat(filename + suffix)
+		if err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				continue
+			}
+
+			return nil, errors.WithMessagef(err, "could not stat file %s", filename+suffix)
+		}
+		file.Encodings[enc], err = os.Open(filename + suffix)
+		if err != nil {
+			return nil, errors.WithMessagef(err, "could not read file %s", filename+suffix)
+		}
+	}
+
+	return file, nil
+}
+
+func etag(f io.Reader) (string, error) {
+	hash := fnv.New64a()
+	if _, err := io.Copy(hash, f); err != nil {
+		return "", errors.WithMessage(err, "could not hash file")
+	}
+
+	return fmt.Sprintf(`W/"%x"`, hash.Sum(nil)), nil
+}
+
+func (f *File) Close() error {
+	var errs []error
+	for _, enc := range f.Encodings {
+		if err := enc.Close(); err != nil {
+			errs = append(errs, err)
+		}
+	}
+
+	return errors.Join(errs...)
+}
+
+func pathNameToFileName(pathname string) string {
+	if strings.HasSuffix(pathname, "/") {
+		pathname = pathname + "index.html"
+	}
+
+	return pathname
+}
+
+func fileNameToPathName(filename string) string {
+	pathname, _ := strings.CutSuffix(filename, "index.html")
+
+	return pathname
+}
diff --git a/internal/storage/files/reader.go b/internal/storage/files/reader.go
index 0bad9ef..425436b 100644
--- a/internal/storage/files/reader.go
+++ b/internal/storage/files/reader.go
@@ -1,12 +1,7 @@
 package files
 
 import (
-	"fmt"
-	"hash/fnv"
-	"io"
 	"io/fs"
-	"mime"
-	"os"
 	"path/filepath"
 	"strings"
 
@@ -15,21 +10,6 @@ import (
 	"gitlab.com/tozd/go/errors"
 )
 
-type File struct {
-	ContentType  string
-	Etag         string
-	Alternatives map[string]string
-}
-
-func (f *File) AvailableEncodings() []string {
-	encs := []string{}
-	for enc := range f.Alternatives {
-		encs = append(encs, enc)
-	}
-
-	return encs
-}
-
 type Reader struct {
 	root  string
 	log   *log.Logger
@@ -49,49 +29,13 @@ func NewReader(path string, log *log.Logger) (*Reader, error) {
 	return r, nil
 }
 
-func hashFile(filename string) (string, error) {
-	f, err := os.Open(filename)
+func (r *Reader) registerFile(urlpath string, filepath string) error {
+	file, err := r.OpenFile(urlpath, filepath)
 	if err != nil {
-		return "", errors.WithMessagef(err, "could not open file %s for hashing", filename)
-	}
-	defer f.Close()
-	hash := fnv.New64a()
-	if _, err := io.Copy(hash, f); err != nil {
-		return "", errors.WithMessagef(err, "could not hash file %s", filename)
+		return errors.WithMessagef(err, "could not register file %s", filepath)
 	}
 
-	return fmt.Sprintf(`W/"%x"`, hash.Sum(nil)), nil
-}
-
-var encodings = map[string]string{
-	"br":   ".br",
-	"gzip": ".gz",
-}
-
-func (r *Reader) registerFile(urlpath string, fp string) error {
-	hash, err := hashFile(fp)
-	if err != nil {
-		return err
-	}
-	f := File{
-		ContentType: mime.TypeByExtension(filepath.Ext(fp)),
-		Etag:        hash,
-		Alternatives: map[string]string{
-			"identity": fp,
-		},
-	}
-	for enc, suffix := range encodings {
-		_, err := os.Stat(fp + suffix)
-		if err != nil {
-			if errors.Is(err, os.ErrNotExist) {
-				continue
-			}
-
-			return err
-		}
-		f.Alternatives[enc] = fp + suffix
-	}
-	r.files[urlpath] = &f
+	r.files[urlpath] = file
 
 	return nil
 }
@@ -101,22 +45,22 @@ func (r *Reader) registerContentFiles() error {
 		if err != nil {
 			return errors.WithMessagef(err, "failed to access path %s", filePath)
 		}
+		if f.IsDir() {
+			return nil
+		}
+
 		relPath, err := filepath.Rel(r.root, filePath)
 		if err != nil {
 			return errors.WithMessagef(err, "failed to make path relative, path: %s", filePath)
 		}
-		urlPath, _ := strings.CutSuffix("/"+relPath, "index.html")
-		if !f.IsDir() {
-			switch filepath.Ext(relPath) {
-			case ".br", ".gz":
-				return nil
-			}
-			r.log.Debug("registering file", "urlpath", urlPath)
-
-			return r.registerFile(urlPath, filePath)
+		urlPath := fileNameToPathName("/" + relPath)
+
+		switch filepath.Ext(relPath) {
+		case ".br", ".gz":
+			return nil
 		}
 
-		return nil
+		return r.registerFile(urlPath, filePath)
 	})
 	if err != nil {
 		return errors.WithMessage(err, "could not walk directory")
diff --git a/internal/storage/files/writer.go b/internal/storage/files/writer.go
index cd227c0..ce498e7 100644
--- a/internal/storage/files/writer.go
+++ b/internal/storage/files/writer.go
@@ -6,7 +6,6 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"go.alanpearce.eu/x/log"
 
@@ -177,8 +176,5 @@ func (f *Files) Mkdirp(dir string) error {
 }
 
 func (f *Files) join(filename string) string {
-	if strings.HasSuffix(filename, "/") {
-		filename = filename + "index.html"
-	}
-	return filepath.Join(f.outputDirectory, filename)
+	return filepath.Join(f.outputDirectory, pathNameToFileName(filename))
 }