Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPageLimit = 50
|
||||
maxPageLimit = 200
|
||||
maxTreePageLimit = 2000
|
||||
)
|
||||
|
||||
// QuerySearch returns the list/search text from query params.
|
||||
// Accepts both `q` (canonical) and `search` (UI/legacy alias).
|
||||
func QuerySearch(r *http.Request) string {
|
||||
q := strings.TrimSpace(r.URL.Query().Get("q"))
|
||||
if q != "" {
|
||||
return q
|
||||
}
|
||||
return strings.TrimSpace(r.URL.Query().Get("search"))
|
||||
}
|
||||
|
||||
// QueryTruthy reports whether a query param is an explicit truthy flag
|
||||
// (1/true/yes/on). Empty or unrecognized values are false.
|
||||
func QueryTruthy(r *http.Request, key string) bool {
|
||||
v := strings.ToLower(strings.TrimSpace(r.URL.Query().Get(key)))
|
||||
switch v {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// QueryDetailed is true when the client opts into full product list fields
|
||||
// (JSONB attributes, descriptions, quality scoring inputs) via detailed=1.
|
||||
func QueryDetailed(r *http.Request) bool {
|
||||
return QueryTruthy(r, "detailed")
|
||||
}
|
||||
|
||||
// ParseLimitOffset reads limit/offset query params with safe defaults and caps.
|
||||
// Oversized limits are clamped to maxPageLimit.
|
||||
func ParseLimitOffset(r *http.Request) (limit, offset int) {
|
||||
return ParseLimitOffsetMax(r, maxPageLimit)
|
||||
}
|
||||
|
||||
// ParseLimitOffsetMax allows a higher per-endpoint cap and clamps to max
|
||||
// (used for category tree loads).
|
||||
func ParseLimitOffsetMax(r *http.Request, max int) (limit, offset int) {
|
||||
if max <= 0 {
|
||||
max = maxPageLimit
|
||||
}
|
||||
limit, _ = strconv.Atoi(r.URL.Query().Get("limit"))
|
||||
offset, _ = strconv.Atoi(r.URL.Query().Get("offset"))
|
||||
if limit <= 0 {
|
||||
limit = defaultPageLimit
|
||||
}
|
||||
if limit > max {
|
||||
limit = max
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
return limit, offset
|
||||
}
|
||||
|
||||
// ParsePageLimitOffset supports legacy page/limit and v2 limit/offset.
|
||||
// When page is set, offset = (page-1)*limit with legacy defaults (limit=25, max 100).
|
||||
// When only offset/limit are set (no page), uses ParseLimitOffset defaults (limit=50, max 200).
|
||||
func ParsePageLimitOffset(r *http.Request) (page, limit, offset int) {
|
||||
pageRaw := strings.TrimSpace(r.URL.Query().Get("page"))
|
||||
if pageRaw == "" {
|
||||
limit, offset = ParseLimitOffset(r)
|
||||
page = 1
|
||||
if limit > 0 {
|
||||
page = offset/limit + 1
|
||||
}
|
||||
return page, limit, offset
|
||||
}
|
||||
page, _ = strconv.Atoi(pageRaw)
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
limit, _ = strconv.Atoi(r.URL.Query().Get("limit"))
|
||||
if limit <= 0 {
|
||||
limit = 25
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
offset = (page - 1) * limit
|
||||
return page, limit, offset
|
||||
}
|
||||
|
||||
// pageSlice returns a bounded page of items and the original total length.
|
||||
func pageSlice[T any](items []T, limit, offset int) (page []T, total int) {
|
||||
total = len(items)
|
||||
if offset >= total {
|
||||
return []T{}, total
|
||||
}
|
||||
end := offset + limit
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
return items[offset:end], total
|
||||
}
|
||||
Reference in New Issue
Block a user