package storage import ( domain "RenewCMS/internal/domain/image" "errors" "io" "os" "path/filepath" "github.com/google/uuid" ) var contentTypeExtensions = map[string]string{ "image/png": ".png", "image/jpeg": ".jpeg", "image/webp": ".webp", "image/svg+xml": ".svg", } type LocalStorage struct { directory string } var _ domain.Storage = &LocalStorage{} func NewLocalStorage(directory string) *LocalStorage { return &LocalStorage{directory: directory} } func (s *LocalStorage) Put(contentType string, content io.Reader) (string, error) { data, err := io.ReadAll(content) if err != nil { return "", err } if err := os.MkdirAll(s.directory, 0755); err != nil { return "", err } storedName := uuid.NewString() + contentTypeExtensions[contentType] if err := os.WriteFile(filepath.Join(s.directory, storedName), data, 0666); err != nil { return "", err } return storedName, nil } func (s *LocalStorage) Open(storedName string) (domain.Blob, error) { if !isPlainName(storedName) { return domain.Blob{}, domain.ErrBlobNotFound } file, err := os.Open(filepath.Join(s.directory, storedName)) if err != nil { return domain.Blob{}, domain.ErrBlobNotFound } info, err := file.Stat() if err != nil || info.IsDir() { _ = file.Close() return domain.Blob{}, domain.ErrBlobNotFound } return domain.Blob{Content: file, Size: info.Size(), ModTime: info.ModTime()}, nil } func (s *LocalStorage) Delete(storedName string) error { if !isPlainName(storedName) { return nil } err := os.Remove(filepath.Join(s.directory, storedName)) if err != nil && !errors.Is(err, os.ErrNotExist) { return err } return nil } func isPlainName(name string) bool { return name != "" && name != "." && name != ".." && filepath.Base(name) == name && filepath.ToSlash(name) == name }