feat(media): pagination

This commit is contained in:
Florian Sylvain committed 2026-10-13 19:54:59 +02:00
1 parent 04dc151ed7
commit 63c1e7076e
65 files changed
+2960 -365

No files matched your search

+11 -6
View File
@@ -26,8 +26,8 @@ const MaxImageSize int64 = 10485760
const (
MinFilenameRunes = 1
MaxFilenameRunes = 100
DefaultListLimit = 24
MaxListLimit = 100
DefaultListPage = 1
DefaultListSize = 25
)
var allowedContentTypes = map[string]bool{
@@ -128,12 +128,12 @@ type ListQuery struct {
Filter Filter
Variants VariantsFilter
Sort Sort
Limit int
Cursor string
Page int
Size int
}
func NewListQuery() ListQuery {
return ListQuery{Filter: FilterAll, Sort: SortNewest, Limit: DefaultListLimit}
return ListQuery{Filter: FilterAll, Sort: SortNewest, Page: DefaultListPage, Size: DefaultListSize}
}
func (q ListQuery) Validate() error {
@@ -152,7 +152,12 @@ func (q ListQuery) Validate() error {
default:
return ErrInvalidQuery
}
if q.Limit < 1 || q.Limit > MaxListLimit {
if q.Page < 1 {
return ErrInvalidQuery
}
switch q.Size {
case 5, 25, 50, 100:
default:
return ErrInvalidQuery
}
if q.Hash != "" && !sha256Pattern.MatchString(q.Hash) {
+9 -37
View File
@@ -4,13 +4,9 @@ import (
"RenewCMS/internal/domain/article"
"RenewCMS/internal/domain/image"
"RenewCMS/internal/domain/user"
"encoding/base64"
"strconv"
"strings"
)
const cursorPrefix = "offset:"
type MediaItem struct {
Image image.Image
UploadedBy string
@@ -28,9 +24,11 @@ type MediaCounts struct {
}
type MediaPage struct {
Items []MediaItem
NextCursor string
Counts MediaCounts
Items []MediaItem
Total int
Page int
Size int
Counts MediaCounts
}
type ListMediaUseCase struct {
@@ -48,10 +46,6 @@ func (l *ListMediaUseCase) List(query image.ListQuery) (MediaPage, error) {
if err := query.Validate(); err != nil {
return MediaPage{}, err
}
offset, err := decodeCursor(query.Cursor)
if err != nil {
return MediaPage{}, err
}
usage, err := l.usageCounter()
if err != nil {
@@ -83,8 +77,9 @@ func (l *ListMediaUseCase) List(query image.ListQuery) (MediaPage, error) {
}
}
pageEnd := min(offset+query.Limit, len(matching))
pageStart := min(offset, pageEnd)
offset := (query.Page - 1) * query.Size
pageStart := min(offset, len(matching))
pageEnd := min(pageStart+query.Size, len(matching))
names := newUploaderNames(l.userRepository)
items := []MediaItem{}
for _, img := range matching[pageStart:pageEnd] {
@@ -103,11 +98,7 @@ func (l *ListMediaUseCase) List(query image.ListQuery) (MediaPage, error) {
})
}
page := MediaPage{Items: items, Counts: counts}
if pageEnd < len(matching) {
page.NextCursor = encodeCursor(pageEnd)
}
return page, nil
return MediaPage{Items: items, Total: len(matching), Page: query.Page, Size: query.Size, Counts: counts}, nil
}
func (l *ListMediaUseCase) Describe(img image.Image) (MediaItem, error) {
@@ -217,22 +208,3 @@ func matchesFilter(filter image.Filter, usedIn int, duplicate bool) bool {
return true
}
}
func encodeCursor(offset int) string {
return base64.RawURLEncoding.EncodeToString([]byte(cursorPrefix + strconv.Itoa(offset)))
}
func decodeCursor(cursor string) (int, error) {
if cursor == "" {
return 0, nil
}
raw, err := base64.RawURLEncoding.DecodeString(cursor)
if err != nil || !strings.HasPrefix(string(raw), cursorPrefix) {
return 0, image.ErrInvalidQuery
}
offset, err := strconv.Atoi(strings.TrimPrefix(string(raw), cursorPrefix))
if err != nil || offset < 0 {
return 0, image.ErrInvalidQuery
}
return offset, nil
}