Files
Memoh/internal/storage/providers/localfs/provider.go
T
Acbox 5aeb2fd3fc fix(media): add local filesystem fallback and fix gallery lightbox matching
- Add localfs storage provider as fallback when containerfs is unreachable
- Wrap media service with fallback provider in both entry points
- Fix gallery lightbox src matching by comparing pathnames only
2026-04-03 00:01:49 +08:00

78 lines
1.9 KiB
Go

// Package localfs implements storage.Provider backed by the host filesystem.
// Files are stored under {root}/{routingKey}.
package localfs
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// Provider stores media assets on the host filesystem.
type Provider struct {
root string
}
// New creates a local filesystem storage provider rooted at dir.
func New(root string) *Provider {
return &Provider{root: root}
}
func (p *Provider) Put(_ context.Context, key string, reader io.Reader) error {
dest := p.resolve(key)
if err := os.MkdirAll(filepath.Dir(dest), 0o750); err != nil {
return fmt.Errorf("mkdir: %w", err)
}
f, err := os.Create(dest) //nolint:gosec // path is constructed from trusted storage key
if err != nil {
return fmt.Errorf("create: %w", err)
}
defer func() { _ = f.Close() }()
if _, err := io.Copy(f, reader); err != nil {
return fmt.Errorf("write: %w", err)
}
return nil
}
func (p *Provider) Open(_ context.Context, key string) (io.ReadCloser, error) {
return os.Open(p.resolve(key))
}
func (p *Provider) Delete(_ context.Context, key string) error {
return os.Remove(p.resolve(key))
}
func (p *Provider) AccessPath(key string) string {
return p.resolve(key)
}
// ListPrefix returns all keys sharing a common prefix (directory listing).
func (p *Provider) ListPrefix(_ context.Context, prefix string) ([]string, error) {
dir := filepath.Dir(p.resolve(prefix))
base := filepath.Base(prefix)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
var keys []string
for _, e := range entries {
if e.IsDir() {
continue
}
if strings.HasPrefix(e.Name(), base) {
rel, _ := filepath.Rel(p.root, filepath.Join(dir, e.Name()))
if rel != "" {
keys = append(keys, filepath.ToSlash(rel))
}
}
}
return keys, nil
}
func (p *Provider) resolve(key string) string {
return filepath.Join(p.root, filepath.FromSlash(key))
}