mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
add pagination for access logs
This commit is contained in:
@@ -6,5 +6,5 @@ import (
|
|||||||
|
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
SaveAccessLog(ctx context.Context, proxyLog *AccessLogEntry) error
|
SaveAccessLog(ctx context.Context, proxyLog *AccessLogEntry) error
|
||||||
GetAllAccessLogs(ctx context.Context, accountID, userID string) ([]*AccessLogEntry, error)
|
GetAllAccessLogs(ctx context.Context, accountID, userID string, filter AccessLogFilter) ([]*AccessLogEntry, int64, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ func (h *handler) getAccessLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err := h.manager.GetAllAccessLogs(r.Context(), userAuth.AccountId, userAuth.UserId)
|
// Parse pagination parameters from request
|
||||||
|
var filter accesslogs.AccessLogFilter
|
||||||
|
filter.ParseFromRequest(r)
|
||||||
|
|
||||||
|
logs, totalCount, err := h.manager.GetAllAccessLogs(r.Context(), userAuth.AccountId, userAuth.UserId, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
@@ -41,5 +45,22 @@ func (h *handler) getAccessLogs(w http.ResponseWriter, r *http.Request) {
|
|||||||
apiLogs = append(apiLogs, *log.ToAPIResponse())
|
apiLogs = append(apiLogs, *log.ToAPIResponse())
|
||||||
}
|
}
|
||||||
|
|
||||||
util.WriteJSONObject(r.Context(), w, apiLogs)
|
// Return paginated response
|
||||||
|
response := &api.ProxyAccessLogsResponse{
|
||||||
|
Data: apiLogs,
|
||||||
|
Page: filter.Page,
|
||||||
|
PageSize: filter.PageSize,
|
||||||
|
TotalRecords: int(totalCount),
|
||||||
|
TotalPages: getTotalPageCount(int(totalCount), filter.PageSize),
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTotalPageCount calculates the total number of pages
|
||||||
|
func getTotalPageCount(totalCount, pageSize int) int {
|
||||||
|
if pageSize <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (totalCount + pageSize - 1) / pageSize
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,20 +55,20 @@ func (m *managerImpl) SaveAccessLog(ctx context.Context, logEntry *accesslogs.Ac
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllAccessLogs retrieves all access logs for an account
|
// GetAllAccessLogs retrieves access logs for an account with pagination
|
||||||
func (m *managerImpl) GetAllAccessLogs(ctx context.Context, accountID, userID string) ([]*accesslogs.AccessLogEntry, error) {
|
func (m *managerImpl) GetAllAccessLogs(ctx context.Context, accountID, userID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error) {
|
||||||
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.NewPermissionValidationError(err)
|
return nil, 0, status.NewPermissionValidationError(err)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, status.NewPermissionDeniedError()
|
return nil, 0, status.NewPermissionDeniedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
logs, err := m.store.GetAccountAccessLogs(ctx, store.LockingStrengthNone, accountID)
|
logs, totalCount, err := m.store.GetAccountAccessLogs(ctx, store.LockingStrengthNone, accountID, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs, nil
|
return logs, totalCount, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5061,14 +5061,27 @@ func (s *SqlStore) CreateAccessLog(ctx context.Context, logEntry *accesslogs.Acc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountAccessLogs retrieves all access logs for a given account
|
// GetAccountAccessLogs retrieves access logs for a given account with pagination
|
||||||
func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*accesslogs.AccessLogEntry, error) {
|
func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error) {
|
||||||
var logs []*accesslogs.AccessLogEntry
|
var logs []*accesslogs.AccessLogEntry
|
||||||
|
var totalCount int64
|
||||||
|
|
||||||
|
// Count total records for pagination metadata
|
||||||
|
countQuery := s.db.WithContext(ctx).
|
||||||
|
Model(&accesslogs.AccessLogEntry{}).
|
||||||
|
Where(accountIDCondition, accountID)
|
||||||
|
|
||||||
|
if err := countQuery.Count(&totalCount).Error; err != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to count access logs: %v", err)
|
||||||
|
return nil, 0, status.Errorf(status.Internal, "failed to count access logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query with pagination
|
||||||
query := s.db.WithContext(ctx).
|
query := s.db.WithContext(ctx).
|
||||||
Where(accountIDCondition, accountID).
|
Where(accountIDCondition, accountID).
|
||||||
Order("timestamp DESC").
|
Order("timestamp DESC").
|
||||||
Limit(1000)
|
Limit(filter.GetLimit()).
|
||||||
|
Offset(filter.GetOffset())
|
||||||
|
|
||||||
if lockStrength != LockingStrengthNone {
|
if lockStrength != LockingStrengthNone {
|
||||||
query = query.Clauses(clause.Locking{Strength: string(lockStrength)})
|
query = query.Clauses(clause.Locking{Strength: string(lockStrength)})
|
||||||
@@ -5077,10 +5090,10 @@ func (s *SqlStore) GetAccountAccessLogs(ctx context.Context, lockStrength Lockin
|
|||||||
result := query.Find(&logs)
|
result := query.Find(&logs)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.WithContext(ctx).Errorf("failed to get access logs from store: %v", result.Error)
|
log.WithContext(ctx).Errorf("failed to get access logs from store: %v", result.Error)
|
||||||
return nil, status.Errorf(status.Internal, "failed to get access logs from store")
|
return nil, 0, status.Errorf(status.Internal, "failed to get access logs from store")
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs, nil
|
return logs, totalCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlStore) GetReverseProxyTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error) {
|
func (s *SqlStore) GetReverseProxyTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error) {
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ type Store interface {
|
|||||||
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
||||||
|
|
||||||
CreateAccessLog(ctx context.Context, log *accesslogs.AccessLogEntry) error
|
CreateAccessLog(ctx context.Context, log *accesslogs.AccessLogEntry) error
|
||||||
GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*accesslogs.AccessLogEntry, error)
|
GetAccountAccessLogs(ctx context.Context, lockStrength LockingStrength, accountID string, filter accesslogs.AccessLogFilter) ([]*accesslogs.AccessLogEntry, int64, error)
|
||||||
GetReverseProxyTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error)
|
GetReverseProxyTargetByTargetID(ctx context.Context, lockStrength LockingStrength, accountID string, targetID string) (*reverseproxy.Target, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2773,6 +2773,36 @@ components:
|
|||||||
- path
|
- path
|
||||||
- duration_ms
|
- duration_ms
|
||||||
- status_code
|
- status_code
|
||||||
|
ProxyAccessLogsResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
description: List of proxy access log entries
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/ProxyAccessLog"
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
description: Current page number
|
||||||
|
example: 1
|
||||||
|
page_size:
|
||||||
|
type: integer
|
||||||
|
description: Number of items per page
|
||||||
|
example: 50
|
||||||
|
total_records:
|
||||||
|
type: integer
|
||||||
|
description: Total number of log records available
|
||||||
|
example: 523
|
||||||
|
total_pages:
|
||||||
|
type: integer
|
||||||
|
description: Total number of pages available
|
||||||
|
example: 11
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
- page
|
||||||
|
- page_size
|
||||||
|
- total_records
|
||||||
|
- total_pages
|
||||||
IdentityProviderType:
|
IdentityProviderType:
|
||||||
type: string
|
type: string
|
||||||
description: Type of identity provider
|
description: Type of identity provider
|
||||||
@@ -6341,17 +6371,31 @@ paths:
|
|||||||
/api/events/proxy:
|
/api/events/proxy:
|
||||||
get:
|
get:
|
||||||
summary: List all Reverse Proxy Access Logs
|
summary: List all Reverse Proxy Access Logs
|
||||||
description: Returns a list of all reverse proxy access log entries
|
description: Returns a paginated list of all reverse proxy access log entries
|
||||||
tags: [ Events ]
|
tags: [ Events ]
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: page
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
minimum: 1
|
||||||
|
description: Page number for pagination (1-indexed)
|
||||||
|
- in: query
|
||||||
|
name: page_size
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 50
|
||||||
|
minimum: 1
|
||||||
|
maximum: 100
|
||||||
|
description: Number of items per page (max 100)
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: List of reverse proxy access logs
|
description: Paginated list of reverse proxy access logs
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
$ref: "#/components/schemas/ProxyAccessLogsResponse"
|
||||||
items:
|
|
||||||
$ref: "#/components/schemas/ProxyAccessLog"
|
|
||||||
'401':
|
'401':
|
||||||
"$ref": "#/components/responses/requires_authentication"
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
'403':
|
'403':
|
||||||
|
|||||||
@@ -1950,6 +1950,24 @@ type ProxyAccessLog struct {
|
|||||||
UserId *string `json:"user_id,omitempty"`
|
UserId *string `json:"user_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyAccessLogsResponse defines model for ProxyAccessLogsResponse.
|
||||||
|
type ProxyAccessLogsResponse struct {
|
||||||
|
// Data List of proxy access log entries
|
||||||
|
Data []ProxyAccessLog `json:"data"`
|
||||||
|
|
||||||
|
// Page Current page number
|
||||||
|
Page int `json:"page"`
|
||||||
|
|
||||||
|
// PageSize Number of items per page
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
|
||||||
|
// TotalPages Total number of pages available
|
||||||
|
TotalPages int `json:"total_pages"`
|
||||||
|
|
||||||
|
// TotalRecords Total number of log records available
|
||||||
|
TotalRecords int `json:"total_records"`
|
||||||
|
}
|
||||||
|
|
||||||
// ProxyCluster A proxy cluster represents a group of proxy nodes serving the same address
|
// ProxyCluster A proxy cluster represents a group of proxy nodes serving the same address
|
||||||
type ProxyCluster struct {
|
type ProxyCluster struct {
|
||||||
// Address Cluster address used for CNAME targets
|
// Address Cluster address used for CNAME targets
|
||||||
@@ -2655,6 +2673,15 @@ type GetApiEventsNetworkTrafficParamsConnectionType string
|
|||||||
// GetApiEventsNetworkTrafficParamsDirection defines parameters for GetApiEventsNetworkTraffic.
|
// GetApiEventsNetworkTrafficParamsDirection defines parameters for GetApiEventsNetworkTraffic.
|
||||||
type GetApiEventsNetworkTrafficParamsDirection string
|
type GetApiEventsNetworkTrafficParamsDirection string
|
||||||
|
|
||||||
|
// GetApiEventsProxyParams defines parameters for GetApiEventsProxy.
|
||||||
|
type GetApiEventsProxyParams struct {
|
||||||
|
// Page Page number for pagination (1-indexed)
|
||||||
|
Page *int `form:"page,omitempty" json:"page,omitempty"`
|
||||||
|
|
||||||
|
// PageSize Number of items per page (max 100)
|
||||||
|
PageSize *int `form:"page_size,omitempty" json:"page_size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// GetApiGroupsParams defines parameters for GetApiGroups.
|
// GetApiGroupsParams defines parameters for GetApiGroups.
|
||||||
type GetApiGroupsParams struct {
|
type GetApiGroupsParams struct {
|
||||||
// Name Filter groups by name (exact match)
|
// Name Filter groups by name (exact match)
|
||||||
|
|||||||
Reference in New Issue
Block a user