init
This commit is contained in:
commit
49b24e5773
22 changed files with 1499 additions and 0 deletions
126
store/local/local.go
Normal file
126
store/local/local.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
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
|
||||
}
|
19
store/store.go
Normal file
19
store/store.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrNotExist = errors.Sentinel("file does not exist")
|
||||
ErrInvalidStoreKey = errors.Sentinel("invalid $STORAGE key")
|
||||
)
|
||||
|
||||
type Store interface {
|
||||
WriteFile(id uuid.UUID, data io.Reader, contentType string) error
|
||||
GetFile(id uuid.UUID) (io.Reader, error)
|
||||
DeleteFile(id uuid.UUID) error
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue