filer/store/local/local.go
2023-08-25 02:26:44 +02:00

126 lines
2.4 KiB
Go

package local
import (
"bytes"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"codeberg.org/u1f320/filer/store"
"emperror.dev/errors"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
const (
StoreKey = "local"
ErrNotDirectory = errors.Sentinel("not a directory")
ErrAlreadyExists = errors.Sentinel("file already exists")
defaultPath = "uploads"
)
type localStore struct {
path string
}
var _ store.Store = (*localStore)(nil)
// New creates a new local store.
// It creates the directory if it doesn't exist,
func New(path string) (store.Store, error) {
if path == "" {
path = defaultPath
}
if !strings.HasPrefix(path, "/") {
wd, err := os.Getwd()
if err != nil {
return nil, errors.Wrap(err, "getting working directory")
}
path = filepath.Join(wd, path)
}
log.Info().Str("path", path).Msg("Using local storage")
fi, err := os.Stat(path)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, errors.Wrap(err, "calling Stat on path")
}
err = os.MkdirAll(path, 0o744)
if err != nil {
return nil, errors.Wrap(err, "creating uploads directory")
}
} else if !fi.IsDir() {
return nil, ErrNotDirectory
}
return &localStore{
path: path,
}, nil
}
func (l *localStore) WriteFile(id uuid.UUID, data io.Reader, contentType string) (err error) {
path := filepath.Join(l.path, id.String())
_, err = os.Stat(path)
if err == nil {
return ErrAlreadyExists
}
file, err := os.Create(path)
if err != nil {
return errors.Wrap(err, "creating file")
}
defer func() {
err = errors.Append(err, file.Close())
}()
_, err = io.Copy(file, data)
if err != nil {
return errors.Wrap(err, "writing data to file")
}
return nil
}
func (l *localStore) GetFile(id uuid.UUID) (r io.Reader, err error) {
path := filepath.Join(l.path, id.String())
file, err := os.Open(path)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil, store.ErrNotExist
}
}
defer func() {
err = errors.Append(err, file.Close())
}()
b := new(bytes.Buffer)
_, err = io.Copy(b, file)
if err != nil {
return nil, errors.Wrap(err, "copying file to buffer")
}
return b, nil
}
func (l *localStore) DeleteFile(id uuid.UUID) error {
path := filepath.Join(l.path, id.String())
err := os.Remove(path)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil // a file already not existing is not an error
}
return errors.Wrap(err, "removing file")
}
return nil
}