87 lines
1.8 KiB
Go
87 lines
1.8 KiB
Go
package paginate
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
DefaultLimit = 20
|
|
MaxLimit = 100
|
|
)
|
|
|
|
// PageInfo holds pagination metadata returned alongside the data.
|
|
type PageInfo struct {
|
|
Total int `json:"total"`
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
HasNext bool `json:"has_next"`
|
|
HasPrev bool `json:"has_prev"`
|
|
}
|
|
|
|
// Page is the generic paginated response envelope.
|
|
type Page[T any] struct {
|
|
Data []T `json:"data"`
|
|
Pagination PageInfo `json:"pagination"`
|
|
}
|
|
|
|
// NewPage constructs a Page from a slice already fetched from the DB
|
|
// (which owns the limit/offset), the total record count, and the Params
|
|
// that were passed to the query.
|
|
func NewPage[T any](data []T, total int, p Params) Page[T] {
|
|
if data == nil {
|
|
data = []T{}
|
|
}
|
|
return Page[T]{
|
|
Data: data,
|
|
Pagination: PageInfo{
|
|
Total: total,
|
|
Limit: int(p.Limit),
|
|
Offset: int(p.Offset),
|
|
HasNext: int(p.Offset)+int(p.Limit) < total,
|
|
HasPrev: p.Offset > 0,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Params holds parsed limit/offset values
|
|
type Params struct {
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
// ParseParams reads "limit" and "offset" from the request query string and
|
|
// returns safe, clamped values. Invalid or missing values fall back to defaults.
|
|
//
|
|
// GET /bots?limit=10&offset=30
|
|
func ParseParams(r *http.Request) Params {
|
|
limit := parseIntParam(r, "limit", DefaultLimit)
|
|
offset := parseIntParam(r, "offset", 0)
|
|
|
|
if limit <= 0 {
|
|
limit = DefaultLimit
|
|
}
|
|
if limit > MaxLimit {
|
|
limit = MaxLimit
|
|
}
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
return Params{
|
|
Limit: int32(limit),
|
|
Offset: int32(offset),
|
|
}
|
|
}
|
|
|
|
func parseIntParam(r *http.Request, key string, fallback int) int {
|
|
raw := r.URL.Query().Get(key)
|
|
if raw == "" {
|
|
return fallback
|
|
}
|
|
v, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return v
|
|
}
|