diff --git a/management/internals/modules/reverseproxy/accesslogs/filter.go b/management/internals/modules/reverseproxy/accesslogs/filter.go index 1b07386d4..a1fa28312 100644 --- a/management/internals/modules/reverseproxy/accesslogs/filter.go +++ b/management/internals/modules/reverseproxy/accesslogs/filter.go @@ -18,9 +18,11 @@ const ( DefaultSortOrder = "desc" ) -// Valid sortable fields mapped to their database column names +// Valid sortable fields mapped to their database column names or expressions +// For multi-column sorts, columns are separated by comma (e.g., "host, path") var validSortFields = map[string]string{ "timestamp": "timestamp", + "url": "host, path", // Sort by host first, then path "host": "host", "path": "path", "method": "method", @@ -40,7 +42,7 @@ type AccessLogFilter struct { PageSize int // Sorting parameters - SortBy string // Field to sort by: timestamp, host, path, method, status_code, duration, source_ip, user_id, auth_method, reason + SortBy string // Field to sort by: timestamp, url, host, path, method, status_code, duration, source_ip, user_id, auth_method, reason SortOrder string // Sort order: asc or desc (default: desc) // Filtering parameters diff --git a/management/internals/modules/reverseproxy/accesslogs/filter_test.go b/management/internals/modules/reverseproxy/accesslogs/filter_test.go index 10024c248..ea1fce54b 100644 --- a/management/internals/modules/reverseproxy/accesslogs/filter_test.go +++ b/management/internals/modules/reverseproxy/accesslogs/filter_test.go @@ -381,6 +381,7 @@ func TestAccessLogFilter_ValidSortFields(t *testing.T) { expectedSortByVal string }{ {"timestamp", "timestamp", "timestamp", "timestamp"}, + {"url", "url", "host, path", "url"}, {"host", "host", "host", "host"}, {"path", "path", "path", "path"}, {"method", "method", "method", "method"}, diff --git a/management/server/store/sql_store.go b/management/server/store/sql_store.go index 4d9da888e..5ddc341d4 100644 --- a/management/server/store/sql_store.go +++ b/management/server/store/sql_store.go @@ -5082,9 +5082,17 @@ func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength Lockin query = s.applyAccessLogFilters(query, filter) - sortColumn := filter.GetSortColumn() - sortOrder := filter.GetSortOrder() - orderClause := sortColumn + " " + strings.ToUpper(sortOrder) + sortColumns := filter.GetSortColumn() + sortOrder := strings.ToUpper(filter.GetSortOrder()) + + var orderClauses []string + for _, col := range strings.Split(sortColumns, ",") { + col = strings.TrimSpace(col) + if col != "" { + orderClauses = append(orderClauses, col+" "+sortOrder) + } + } + orderClause := strings.Join(orderClauses, ", ") query = query. Order(orderClause). diff --git a/shared/management/http/api/openapi.yml b/shared/management/http/api/openapi.yml index d2a56bed0..b0ce1b5cc 100644 --- a/shared/management/http/api/openapi.yml +++ b/shared/management/http/api/openapi.yml @@ -7413,9 +7413,9 @@ paths: name: sort_by schema: type: string - enum: [timestamp, host, path, method, status_code, duration, source_ip, user_id, auth_method, reason] + enum: [timestamp, url, host, path, method, status_code, duration, source_ip, user_id, auth_method, reason] default: timestamp - description: Field to sort by + description: Field to sort by (url sorts by host then path) - in: query name: sort_order schema: