mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
5aeb2fd3fc
- 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
82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
// Package fallback implements storage.Provider that tries a primary provider
|
|
// first (e.g. containerfs) and falls back to a secondary (e.g. localfs) on
|
|
// failure. Reads check both providers so assets stored by either are reachable.
|
|
package fallback
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
"github.com/memohai/memoh/internal/storage"
|
|
)
|
|
|
|
// Provider delegates to primary and falls back to secondary on write errors.
|
|
type Provider struct {
|
|
primary storage.Provider
|
|
secondary storage.Provider
|
|
}
|
|
|
|
// New creates a fallback provider.
|
|
func New(primary, secondary storage.Provider) *Provider {
|
|
return &Provider{primary: primary, secondary: secondary}
|
|
}
|
|
|
|
func (p *Provider) Put(ctx context.Context, key string, reader io.Reader) error {
|
|
err := p.primary.Put(ctx, key, reader)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if seeker, ok := reader.(io.Seeker); ok {
|
|
if _, seekErr := seeker.Seek(0, io.SeekStart); seekErr != nil {
|
|
return err
|
|
}
|
|
}
|
|
return p.secondary.Put(ctx, key, reader)
|
|
}
|
|
|
|
func (p *Provider) Open(ctx context.Context, key string) (io.ReadCloser, error) {
|
|
rc, err := p.primary.Open(ctx, key)
|
|
if err == nil {
|
|
return rc, nil
|
|
}
|
|
return p.secondary.Open(ctx, key)
|
|
}
|
|
|
|
func (p *Provider) Delete(ctx context.Context, key string) error {
|
|
err := p.primary.Delete(ctx, key)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return p.secondary.Delete(ctx, key)
|
|
}
|
|
|
|
func (p *Provider) AccessPath(key string) string {
|
|
return p.primary.AccessPath(key)
|
|
}
|
|
|
|
// ListPrefix delegates to both providers and deduplicates.
|
|
func (p *Provider) ListPrefix(ctx context.Context, prefix string) ([]string, error) {
|
|
keys, _ := tryListPrefix(ctx, p.primary, prefix)
|
|
secondaryKeys, _ := tryListPrefix(ctx, p.secondary, prefix)
|
|
seen := make(map[string]struct{}, len(keys))
|
|
for _, k := range keys {
|
|
seen[k] = struct{}{}
|
|
}
|
|
for _, k := range secondaryKeys {
|
|
if _, ok := seen[k]; !ok {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
if len(keys) == 0 {
|
|
return nil, nil
|
|
}
|
|
return keys, nil
|
|
}
|
|
|
|
func tryListPrefix(ctx context.Context, p storage.Provider, prefix string) ([]string, error) {
|
|
if lister, ok := p.(storage.PrefixLister); ok {
|
|
return lister.ListPrefix(ctx, prefix)
|
|
}
|
|
return nil, nil
|
|
}
|