persona-community-2/services/persona-api/internal/adapter/memory/media.go
2026-02-23 10:54:06 +00:00

136 lines
3.0 KiB
Go

package memory
import (
"context"
"sort"
"strings"
"sync"
"time"
"git.threesix.ai/jordan/persona-community-2/services/persona-api/internal/domain"
"git.threesix.ai/jordan/persona-community-2/services/persona-api/internal/port"
)
// Compile-time interface check.
var _ port.MediaRepository = (*MediaRepository)(nil)
// MediaRepository is an in-memory media metadata store for standalone development.
type MediaRepository struct {
mu sync.RWMutex
objects map[domain.MediaObjectID]*domain.MediaObject
byPath map[string]domain.MediaObjectID
}
// NewMediaRepository creates a new in-memory media repository.
func NewMediaRepository() *MediaRepository {
return &MediaRepository{
objects: make(map[domain.MediaObjectID]*domain.MediaObject),
byPath: make(map[string]domain.MediaObjectID),
}
}
func (r *MediaRepository) copyObject(obj *domain.MediaObject) *domain.MediaObject {
cp := *obj
return &cp
}
func (r *MediaRepository) Create(_ context.Context, obj *domain.MediaObject) error {
r.mu.Lock()
defer r.mu.Unlock()
r.objects[obj.ID] = r.copyObject(obj)
r.byPath[obj.Path] = obj.ID
return nil
}
func (r *MediaRepository) Get(_ context.Context, id domain.MediaObjectID) (*domain.MediaObject, error) {
r.mu.RLock()
defer r.mu.RUnlock()
obj, ok := r.objects[id]
if !ok || obj.DeletedAt != nil {
return nil, domain.ErrNotFound
}
return r.copyObject(obj), nil
}
func (r *MediaRepository) ListByUser(_ context.Context, userID domain.UserID, opts port.ListMediaOptions) ([]domain.MediaObject, int, error) {
r.mu.RLock()
defer r.mu.RUnlock()
var all []domain.MediaObject
for _, obj := range r.objects {
if obj.UserID != userID || obj.DeletedAt != nil {
continue
}
if opts.ContentTypePrefix != "" && !strings.HasPrefix(obj.ContentType, opts.ContentTypePrefix) {
continue
}
all = append(all, *r.copyObject(obj))
}
// Sort by created_at DESC
sort.Slice(all, func(i, j int) bool {
return all[i].CreatedAt.After(all[j].CreatedAt)
})
total := len(all)
// Apply pagination
limit := opts.Limit
if limit <= 0 {
limit = 50
}
offset := opts.Offset
if offset > len(all) {
offset = len(all)
}
end := offset + limit
if end > len(all) {
end = len(all)
}
return all[offset:end], total, nil
}
func (r *MediaRepository) SoftDelete(_ context.Context, id domain.MediaObjectID) error {
r.mu.Lock()
defer r.mu.Unlock()
obj, ok := r.objects[id]
if !ok {
return domain.ErrNotFound
}
now := time.Now()
obj.DeletedAt = &now
return nil
}
func (r *MediaRepository) HardDelete(_ context.Context, id domain.MediaObjectID) error {
r.mu.Lock()
defer r.mu.Unlock()
obj, ok := r.objects[id]
if !ok {
return domain.ErrNotFound
}
delete(r.byPath, obj.Path)
delete(r.objects, id)
return nil
}
func (r *MediaRepository) GetByPath(_ context.Context, path string) (*domain.MediaObject, error) {
r.mu.RLock()
defer r.mu.RUnlock()
id, ok := r.byPath[path]
if !ok {
return nil, domain.ErrNotFound
}
obj, ok := r.objects[id]
if !ok || obj.DeletedAt != nil {
return nil, domain.ErrNotFound
}
return r.copyObject(obj), nil
}