all repos — homestead @ 65039b065ba634b9c4b4c7f4b42ebccdbfd40ce0

Code for my website

shared/storage/files/writer.go (view raw)

package files

import (
	"compress/gzip"
	"io"
	"os"
	"path/filepath"

	"alin.ovh/x/log"
	"github.com/Southclaws/fault"
	"github.com/Southclaws/fault/fmsg"
	"github.com/andybalholm/brotli"
	"github.com/klauspost/compress/zstd"

	"alin.ovh/homestead/domain/content"
	"alin.ovh/homestead/shared/file"
	"alin.ovh/homestead/shared/storage"
)

type Files struct {
	outputDirectory string
	options         *Options
	log             *log.Logger
}

type Options struct {
	Compress bool
}

const (
	gzipLevel   = 6
	brotliLevel = 9
)

func NewWriter(outputDirectory string, logger *log.Logger, opts *Options) (*Files, error) {
	return &Files{
		outputDirectory: outputDirectory,
		options:         opts,
		log:             logger,
	}, nil
}

func (f *Files) OpenRead(filename string) (io.ReadCloser, error) {
	file, err := os.Open(f.join(filename))
	if err != nil {
		return nil, fault.Wrap(err)
	}

	return file, nil
}

func (f *Files) OpenWrite(filename string) (io.WriteCloser, error) {
	return openFileWrite(f.join(filename))
}

func (f *Files) WritePost(post *content.Post, wt io.WriterTo) error {
	fd, err := f.write(post.URL, wt)
	if err != nil {
		return err
	}

	if err := fd.Close(); err != nil {
		return fault.Wrap(err)
	}

	if mf, isMultifile := fd.(*file.MultiFile); isMultifile {
		err = mf.Chtimes(post.Date)
	} else {
		err = fault.Wrap(os.Chtimes(fd.Name(), post.Date, post.Date))
	}
	if err != nil {
		return fault.Wrap(err, fmsg.With("could not set file times"))
	}

	return nil
}

func (f *Files) Write(pathname string, _ string, wt io.WriterTo) error {
	fd, err := f.write(pathname, wt)
	if err != nil {
		return err
	}

	if err := fd.Close(); err != nil {
		return fault.Wrap(err)
	}

	return nil
}

func (f *Files) NewFileFromPost(post *content.Post) *storage.File {
	return &storage.File{
		Path:      post.URL,
		FSPath:    pathNameToFileName(post.URL),
		Encodings: map[string]*os.File{},
	}
}

func (f *Files) CopyFile(file *storage.File, src *os.File) error {
	dst, err := f.write(file.Path, src)
	if err != nil {
		return err
	}

	if err := dst.Close(); err != nil {
		return fault.Wrap(err)
	}

	return nil
}

func (f *Files) WriteFile(file *storage.File, wt io.WriterTo) error {
	return f.Write(file.Path, file.Title, wt)
}

func (f *Files) OpenFileAndVariants(filename string) (file.FileLike, error) {
	if f.options.Compress {
		return multiOpenFile(f.join(filename))
	}

	return openFileWrite(f.join(filename))
}

func (f *Files) Mkdirp(dir string) error {
	err := os.MkdirAll(f.join(dir), 0o750)
	if err != nil {
		return fault.Wrap(err, fmsg.With("could not create directory"))
	}

	return nil
}

func (f *Files) write(
	pathname string,
	writer io.WriterTo,
) (file.FileLike, error) {
	filename := pathNameToFileName(pathname)
	err := f.Mkdirp(filepath.Dir(filename))
	if err != nil {
		return nil, fault.Wrap(err, fmsg.With("could not create directory"))
	}

	fd, err := f.OpenFileAndVariants(filename)
	if err != nil {
		return nil, fault.Wrap(err, fmsg.With("could not open output file"))
	}

	if _, err := writer.WriteTo(fd); err != nil {
		return nil, fault.Wrap(err, fmsg.With("could not write output file"))
	}

	return fd, nil
}

func openFileWrite(filename string) (*os.File, error) {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
	if err != nil {
		return nil, fault.Wrap(err, fmsg.With("could not open output file"))
	}

	return f, nil
}

func openFileGz(filename string) (*file.CompressWriter, error) {
	filenameGz := filename + ".gz"
	f, err := openFileWrite(filenameGz)
	if err != nil {
		return nil, err
	}
	var w io.WriteCloser
	var baseErr error
	w, baseErr = gzip.NewWriterLevel(f, gzipLevel)
	if baseErr != nil {
		return nil, fault.Wrap(baseErr)
	}

	return file.NewCompressWriter(f, w), err
}

func openFileBrotli(filename string) (*file.CompressWriter, error) {
	filenameBrotli := filename + ".br"
	f, err := openFileWrite(filenameBrotli)
	if err != nil {
		return nil, err
	}

	return file.NewCompressWriter(f, brotli.NewWriterLevel(f, brotliLevel)), nil
}

func openFileZstd(filename string) (*file.CompressWriter, error) {
	f, err := openFileWrite(filename + ".zstd")
	if err != nil {
		return nil, err
	}

	var w io.WriteCloser
	var baseErr error
	w, baseErr = zstd.NewWriter(f)
	if baseErr != nil {
		return nil, fault.Wrap(baseErr)
	}

	return file.NewCompressWriter(f, w), nil
}

func multiOpenFile(filename string) (*file.MultiFile, error) {
	r, err := openFileWrite(filename)
	if err != nil {
		return nil, err
	}
	gz, err := openFileGz(filename)
	if err != nil {
		return nil, err
	}
	br, err := openFileBrotli(filename)
	if err != nil {
		return nil, err
	}
	zst, err := openFileZstd(filename)
	if err != nil {
		return nil, err
	}

	return file.NewMultiFile(r, gz, br, zst), nil
}

func (f *Files) join(filename string) string {
	return filepath.Join(f.outputDirectory, pathNameToFileName(filename))
}