54 lines
1.7 KiB
Go
54 lines
1.7 KiB
Go
|
|
package handlers
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"net/http"
|
|||
|
|
"strconv"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
|
|||
|
|
"building-mgmt/internal/repository"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Пагинация списков: одинаковые параметры limit/offset/sort/order
|
|||
|
|
// у всех GET-списков, поэтому разбор вынесен отдельно.
|
|||
|
|
// Словари сортировок приходят из repository — того же источника,
|
|||
|
|
// что и ORDER BY в SQL: валидация и запрос не могут разъехаться.
|
|||
|
|
|
|||
|
|
// parsePage разбирает параметры страницы. Возвращает ok=false после
|
|||
|
|
// ответа 400 — вызывающий хендлер обязан сразу вернуться.
|
|||
|
|
func parsePage(c *gin.Context, sorts map[string]repository.SortSpec) (repository.Page, bool) {
|
|||
|
|
page := repository.Page{
|
|||
|
|
Limit: repository.DefaultLimit,
|
|||
|
|
Sort: c.Query("sort"),
|
|||
|
|
Order: c.Query("order"),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if v := c.Query("limit"); v != "" {
|
|||
|
|
n, err := strconv.Atoi(v)
|
|||
|
|
if err != nil || n < 1 || n > repository.MaxLimit {
|
|||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid limit"})
|
|||
|
|
return page, false
|
|||
|
|
}
|
|||
|
|
page.Limit = n
|
|||
|
|
}
|
|||
|
|
if v := c.Query("offset"); v != "" {
|
|||
|
|
n, err := strconv.Atoi(v)
|
|||
|
|
if err != nil || n < 0 {
|
|||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid offset"})
|
|||
|
|
return page, false
|
|||
|
|
}
|
|||
|
|
page.Offset = n
|
|||
|
|
}
|
|||
|
|
if page.Order != "" && page.Order != "asc" && page.Order != "desc" {
|
|||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid order"})
|
|||
|
|
return page, false
|
|||
|
|
}
|
|||
|
|
if page.Sort != "" {
|
|||
|
|
if _, ok := sorts[page.Sort]; !ok {
|
|||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid sort"})
|
|||
|
|
return page, false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return page, true
|
|||
|
|
}
|