forked from baron/baron-sso
테넌트 목록 조회 cursor기반으로 재구성. 사용자 metadata 미사용 필드 제거
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"baron-sso-backend/internal/pagination"
|
||||
"baron-sso-backend/internal/repository"
|
||||
"baron-sso-backend/internal/service"
|
||||
"baron-sso-backend/internal/utils"
|
||||
@@ -121,9 +122,11 @@ type clientSummary struct {
|
||||
}
|
||||
|
||||
type clientListResponse struct {
|
||||
Items []clientSummary `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
Items []clientSummary `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
Cursor string `json:"cursor,omitempty"`
|
||||
NextCursor string `json:"nextCursor,omitempty"`
|
||||
}
|
||||
|
||||
type clientDetailResponse struct {
|
||||
@@ -186,7 +189,12 @@ type consentSummary struct {
|
||||
}
|
||||
|
||||
type consentListResponse struct {
|
||||
Items []consentSummary `json:"items"`
|
||||
Items []consentSummary `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
Limit int `json:"limit"`
|
||||
Offset int `json:"offset"`
|
||||
Cursor string `json:"cursor,omitempty"`
|
||||
NextCursor string `json:"nextCursor,omitempty"`
|
||||
}
|
||||
|
||||
type clientUpsertRequest struct {
|
||||
@@ -1097,6 +1105,30 @@ func (h *DevHandler) listVisibleClientSummaries(
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (h *DevHandler) listAllVisibleClientSummaries(c *fiber.Ctx, profile *domain.UserProfileResponse) ([]clientSummary, error) {
|
||||
const pageSize = 500
|
||||
items := make([]clientSummary, 0)
|
||||
for offset := 0; ; offset += pageSize {
|
||||
page, err := h.listVisibleClientSummaries(c, profile, pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, page...)
|
||||
if len(page) < pageSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func clientSummaryCursorKey(client clientSummary) (time.Time, string) {
|
||||
timestamp := time.Unix(0, 0).UTC()
|
||||
if client.CreatedAt != nil && !client.CreatedAt.IsZero() {
|
||||
timestamp = client.CreatedAt.UTC()
|
||||
}
|
||||
return timestamp, client.ID
|
||||
}
|
||||
|
||||
func extractAuthClaimsFromBearer(authHeader string) (string, string) {
|
||||
authHeader = strings.TrimSpace(authHeader)
|
||||
if !strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
|
||||
@@ -1213,6 +1245,7 @@ func (h *DevHandler) ListClients(c *fiber.Ctx) error {
|
||||
h.injectTenantContextFromHeader(c)
|
||||
limit := c.QueryInt("limit", 50)
|
||||
offset := c.QueryInt("offset", 0)
|
||||
cursorRaw := strings.TrimSpace(c.Query("cursor"))
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
@@ -1221,7 +1254,7 @@ func (h *DevHandler) ListClients(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
profile := h.getCurrentProfile(c)
|
||||
items, err := h.listVisibleClientSummaries(c, profile, limit, offset)
|
||||
allItems, err := h.listAllVisibleClientSummaries(c, profile)
|
||||
if err != nil {
|
||||
status := fiber.StatusInternalServerError
|
||||
errMsg := err.Error()
|
||||
@@ -1239,10 +1272,37 @@ func (h *DevHandler) ListClients(c *fiber.Ctx) error {
|
||||
return errorJSON(c, status, errMsg)
|
||||
}
|
||||
|
||||
var items []clientSummary
|
||||
nextCursor := ""
|
||||
if cursorRaw != "" {
|
||||
ordered := append([]clientSummary(nil), allItems...)
|
||||
pagination.SortByKeyDesc(ordered, clientSummaryCursorKey)
|
||||
items, nextCursor, err = pagination.PageByCursor(ordered, limit, cursorRaw, clientSummaryCursorKey)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid cursor")
|
||||
}
|
||||
offset = 0
|
||||
} else {
|
||||
if offset > len(allItems) {
|
||||
offset = len(allItems)
|
||||
}
|
||||
end := offset + limit
|
||||
if end > len(allItems) {
|
||||
end = len(allItems)
|
||||
}
|
||||
items = allItems[offset:end]
|
||||
if len(allItems) > end && len(items) > 0 {
|
||||
lastTimestamp, lastID := clientSummaryCursorKey(items[len(items)-1])
|
||||
nextCursor = pagination.Encode(lastTimestamp, lastID)
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(clientListResponse{
|
||||
Items: items,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
Items: items,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
Cursor: cursorRaw,
|
||||
NextCursor: nextCursor,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2126,9 +2186,13 @@ func (h *DevHandler) ListConsents(c *fiber.Ctx) error {
|
||||
subject := strings.TrimSpace(c.Query("subject"))
|
||||
limit := c.QueryInt("limit", 50)
|
||||
offset := c.QueryInt("offset", 0)
|
||||
cursorRaw := strings.TrimSpace(c.Query("cursor"))
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
// [Isolation] Get admin tenant ID from locals or header
|
||||
adminTenantID := ""
|
||||
@@ -2156,10 +2220,16 @@ func (h *DevHandler) ListConsents(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
queryLimit := limit
|
||||
queryOffset := offset
|
||||
if cursorRaw != "" || subject != "" || (statusFilter != "" && statusFilter != "all") {
|
||||
queryLimit = 10000
|
||||
queryOffset = 0
|
||||
}
|
||||
if adminTenantID != "" {
|
||||
consents, total, err = h.ConsentRepo.ListByTenant(c.Context(), clientID, adminTenantID, limit, offset)
|
||||
consents, total, err = h.ConsentRepo.ListByTenant(c.Context(), clientID, adminTenantID, queryLimit, queryOffset)
|
||||
} else {
|
||||
consents, total, err = h.ConsentRepo.List(c.Context(), clientID, limit, offset)
|
||||
consents, total, err = h.ConsentRepo.List(c.Context(), clientID, queryLimit, queryOffset)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -2216,12 +2286,47 @@ func (h *DevHandler) ListConsents(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"items": items,
|
||||
"total": total,
|
||||
pagination.SortByKeyDesc(items, consentSummaryCursorKey)
|
||||
nextCursor := ""
|
||||
if cursorRaw != "" {
|
||||
items, nextCursor, err = pagination.PageByCursor(items, limit, cursorRaw, consentSummaryCursorKey)
|
||||
if err != nil {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "invalid cursor")
|
||||
}
|
||||
offset = 0
|
||||
} else if queryLimit != limit {
|
||||
if offset > len(items) {
|
||||
offset = len(items)
|
||||
}
|
||||
end := offset + limit
|
||||
if end > len(items) {
|
||||
end = len(items)
|
||||
}
|
||||
pageItems := items[offset:end]
|
||||
if len(items) > end && len(pageItems) > 0 {
|
||||
lastTimestamp, lastID := consentSummaryCursorKey(pageItems[len(pageItems)-1])
|
||||
nextCursor = pagination.Encode(lastTimestamp, lastID)
|
||||
}
|
||||
items = pageItems
|
||||
} else if total > int64(offset+len(items)) && len(items) > 0 {
|
||||
lastTimestamp, lastID := consentSummaryCursorKey(items[len(items)-1])
|
||||
nextCursor = pagination.Encode(lastTimestamp, lastID)
|
||||
}
|
||||
|
||||
return c.JSON(consentListResponse{
|
||||
Items: items,
|
||||
Total: total,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
Cursor: cursorRaw,
|
||||
NextCursor: nextCursor,
|
||||
})
|
||||
}
|
||||
|
||||
func consentSummaryCursorKey(consent consentSummary) (time.Time, string) {
|
||||
return consent.CreatedAt, consent.ClientID + ":" + consent.Subject
|
||||
}
|
||||
|
||||
func (h *DevHandler) RevokeConsents(c *fiber.Ctx) error {
|
||||
tenantID := h.injectTenantContextFromHeader(c)
|
||||
subject := strings.TrimSpace(c.Query("subject"))
|
||||
|
||||
Reference in New Issue
Block a user