admin

package
v0.1.32 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2025 License: MIT Imports: 4 Imported by: 0

README

Admin API

The admin package provides administrative operations for the simple-content service that bypass normal owner_id/tenant_id restrictions. These operations are designed for operational, monitoring, and bulk processing use cases.

⚠️ Security Warning

IMPORTANT: Admin API endpoints expose unrestricted access to content across all tenants and owners. In production deployments:

  • Always enable authentication/authorization middleware
  • Restrict access to trusted administrators only
  • Log all admin operations for audit trails
  • Consider deploying admin API on a separate internal-only endpoint

Features

  • List All Contents: Paginated listing with flexible filtering
  • Count Contents: Efficient counting for monitoring and analytics
  • Get Statistics: Aggregated statistics with breakdowns by status, tenant, type, etc.
  • Flexible Filtering: Filter by tenant, owner, status, document type, date ranges
  • Pagination Support: Offset-based pagination with configurable limits

Usage

Programmatic Usage
import (
	"context"
	"github.com/tendant/simple-content/pkg/simplecontent/admin"
	"github.com/tendant/simple-content/pkg/simplecontent/repo/postgres"
)

// Create admin service
repo := postgres.NewRepository(db)
adminSvc := admin.New(repo)

// List all contents
resp, err := adminSvc.ListAllContents(ctx, admin.ListContentsRequest{
	Filters: admin.ContentFilters{
		Limit: &limit,
		Offset: &offset,
	},
})

// Count contents by tenant
tenantID := uuid.MustParse("...")
countResp, err := adminSvc.CountContents(ctx, admin.CountRequest{
	Filters: admin.ContentFilters{
		TenantID: &tenantID,
	},
})

// Get statistics
statsResp, err := adminSvc.GetStatistics(ctx, admin.StatisticsRequest{
	Filters: admin.ContentFilters{},
	Options: admin.DefaultStatisticsOptions(),
})
HTTP API Usage

Enable admin API in server configuration:

ENABLE_ADMIN_API=true
List All Contents
GET /api/v1/admin/contents?limit=100&offset=0
GET /api/v1/admin/contents?tenant_id=<uuid>
GET /api/v1/admin/contents?status=uploaded
GET /api/v1/admin/contents?derivation_type=thumbnail
GET /api/v1/admin/contents?include_deleted=true

Query Parameters:

  • tenant_id (uuid): Filter by tenant
  • owner_id (uuid): Filter by owner
  • status (string): Filter by status (created, uploaded, deleted)
  • derivation_type (string): Filter by derivation type
  • document_type (string): Filter by document type
  • limit (int): Maximum results (default: 100, max: 1000)
  • offset (int): Pagination offset (default: 0)
  • include_deleted (boolean): Include deleted content (default: false)

Response:

{
  "contents": [...],
  "total_count": null,
  "limit": 100,
  "offset": 0,
  "has_more": true
}
Count Contents
GET /api/v1/admin/contents/count
GET /api/v1/admin/contents/count?tenant_id=<uuid>
GET /api/v1/admin/contents/count?status=uploaded

Query Parameters: Same as list (excluding pagination)

Response:

{
  "count": 12345
}
Get Statistics
GET /api/v1/admin/contents/stats
GET /api/v1/admin/contents/stats?tenant_id=<uuid>
GET /api/v1/admin/contents/stats?include_tenant=false

Query Parameters:

  • Filtering: Same as list/count
  • Options:
    • include_status (boolean): Include status breakdown (default: true)
    • include_tenant (boolean): Include tenant breakdown (default: true)
    • include_derivation (boolean): Include derivation type breakdown (default: true)
    • include_document_type (boolean): Include document type breakdown (default: true)
    • include_time_range (boolean): Include time range (default: true)

Response:

{
  "statistics": {
    "total_count": 12345,
    "by_status": {
      "created": 1000,
      "uploaded": 10000,
      "deleted": 1345
    },
    "by_tenant": {
      "tenant-1-uuid": 8000,
      "tenant-2-uuid": 4345
    },
    "by_derivation_type": {
      "original": 10000,
      "thumbnail": 1500,
      "preview": 845
    },
    "by_document_type": {
      "application/pdf": 5000,
      "image/jpeg": 4000,
      "video/mp4": 3345
    },
    "oldest_content": "2024-01-01T00:00:00Z",
    "newest_content": "2024-12-31T23:59:59Z"
  },
  "computed_at": "2024-12-31T23:59:59Z"
}

Use Cases

1. Monitoring Dashboard
// Get overall system statistics
stats, err := adminSvc.GetStatistics(ctx, admin.StatisticsRequest{
	Filters: admin.ContentFilters{},
	Options: admin.DefaultStatisticsOptions(),
})

// Display metrics:
// - Total content count
// - Status distribution (created/uploaded/failed)
// - Tenant usage distribution
// - Content type distribution
2. Tenant Analytics
// Get all content for a specific tenant
tenantID := uuid.MustParse("...")
contents, err := adminSvc.ListAllContents(ctx, admin.ListContentsRequest{
	Filters: admin.ContentFilters{
		TenantID: &tenantID,
	},
})

// Count content by status for reporting
count, err := adminSvc.CountContents(ctx, admin.CountRequest{
	Filters: admin.ContentFilters{
		TenantID: &tenantID,
		Status:   &uploadedStatus,
	},
})
3. Bulk Processing
// Process all uploaded content in batches
limit := 1000
offset := 0
status := "uploaded"

for {
	resp, err := adminSvc.ListAllContents(ctx, admin.ListContentsRequest{
		Filters: admin.ContentFilters{
			Status: &status,
			Limit:  &limit,
			Offset: &offset,
		},
	})

	// Process batch
	for _, content := range resp.Contents {
		processContent(content)
	}

	if !resp.HasMore {
		break
	}
	offset += limit
}
4. Data Cleanup
// Find and clean up old deleted content
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
deleted := "deleted"

resp, err := adminSvc.ListAllContents(ctx, admin.ListContentsRequest{
	Filters: admin.ContentFilters{
		Status:        &deleted,
		UpdatedBefore: &thirtyDaysAgo,
		IncludeDeleted: true,
	},
})

// Permanently delete old soft-deleted content
for _, content := range resp.Contents {
	hardDeleteContent(content.ID)
}

Filtering Options

ContentFilters
  • Identity Filters:

    • TenantID / TenantIDs: Filter by tenant(s)
    • OwnerID / OwnerIDs: Filter by owner(s)
  • Type Filters:

    • Status / Statuses: Filter by status (created, uploaded, deleted)
    • DerivationType / DerivationTypes: Filter by derivation type
    • DocumentType / DocumentTypes: Filter by document MIME type
  • Time Range Filters:

    • CreatedAfter / CreatedBefore: Filter by creation time
    • UpdatedAfter / UpdatedBefore: Filter by update time
  • Pagination:

    • Limit: Maximum results per request (default: 100, max: 1000)
    • Offset: Pagination offset
  • Sorting:

    • SortBy: Sort field (created_at, updated_at, name, status)
    • SortOrder: Sort order (ASC, DESC)
  • Special Flags:

    • IncludeDeleted: Include soft-deleted content

Functional Options

import "github.com/tendant/simple-content/pkg/simplecontent/admin"

// Using functional options for cleaner code
resp, err := adminSvc.ListAllContents(ctx, admin.ListContentsRequest{
	Filters: admin.ContentFilters{
		TenantID: admin.WithTenantID(tenantID),
		Status:   admin.WithStatus("uploaded"),
		Limit:    admin.WithPagination(100, 0),
	},
})

Performance Considerations

Indexes

For optimal performance, ensure the following database indexes exist:

-- For tenant-based queries
CREATE INDEX idx_content_tenant_id ON content.content(tenant_id);

-- For status queries
CREATE INDEX idx_content_status ON content.content(status);

-- For time-based queries
CREATE INDEX idx_content_created_at ON content.content(created_at);
CREATE INDEX idx_content_updated_at ON content.content(updated_at);

-- Composite index for common admin queries
CREATE INDEX idx_content_tenant_status ON content.content(tenant_id, status);
Query Optimization
  • Use COUNT queries before large listing operations to determine total size
  • Implement cursor-based pagination for very large datasets (future enhancement)
  • Limit result sizes: Default limit is 100, maximum is 1000
  • Filter early: Apply specific filters to reduce result sets
  • Use statistics API for aggregations instead of client-side computation

Security Best Practices

  1. Authentication: Always require authentication for admin endpoints
  2. Authorization: Implement role-based access control (RBAC)
  3. Audit Logging: Log all admin operations with user identity
  4. Rate Limiting: Prevent abuse with rate limits
  5. IP Whitelisting: Restrict admin API to internal networks
  6. Separate Deployment: Consider deploying admin API separately

Example middleware:

func AdminAuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Verify admin JWT token
		token := extractToken(r)
		claims, err := verifyAdminToken(token)
		if err != nil {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// Log admin operation
		logAdminOperation(claims.UserID, r.Method, r.URL.Path)

		next.ServeHTTP(w, r)
	})
}

Example Application

See examples/admin-operations/main.go for a complete working example demonstrating:

  • Creating sample data
  • Listing all contents
  • Filtering by tenant and status
  • Counting contents
  • Retrieving statistics
  • Paginated listing

Run the example:

go run ./examples/admin-operations/main.go

Future Enhancements

  • Cursor-based pagination for better performance with large datasets
  • Bulk update operations
  • Export to CSV/JSON
  • Scheduled reports
  • Webhook notifications for admin events
  • Advanced filtering with SQL-like expressions

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AdminService

type AdminService interface {
	// ListAllContents returns a paginated list of contents with optional filtering.
	// Unlike the regular ListContent operation, this does not require owner_id or tenant_id.
	ListAllContents(ctx context.Context, req ListContentsRequest) (*ListContentsResponse, error)

	// CountContents returns the count of contents matching the given filters.
	// This is useful for pagination and monitoring purposes.
	CountContents(ctx context.Context, req CountRequest) (*CountResponse, error)

	// GetStatistics returns aggregated statistics about contents.
	// This provides breakdown by status, tenant, derivation type, etc.
	GetStatistics(ctx context.Context, req StatisticsRequest) (*StatisticsResponse, error)
}

AdminService defines the interface for administrative content operations. These operations bypass normal owner_id/tenant_id restrictions and are intended for operational, monitoring, and bulk processing use cases.

IMPORTANT: Endpoints using this service should be protected with appropriate authentication and authorization middleware to ensure only authorized administrators can access these operations.

func New

New creates a new AdminService instance that uses the provided repository.

type ContentFilters

type ContentFilters struct {
	// Identity filters
	TenantID  *uuid.UUID  `json:"tenant_id,omitempty"`
	TenantIDs []uuid.UUID `json:"tenant_ids,omitempty"`
	OwnerID   *uuid.UUID  `json:"owner_id,omitempty"`
	OwnerIDs  []uuid.UUID `json:"owner_ids,omitempty"`

	// Status filters
	Status   *string  `json:"status,omitempty"`
	Statuses []string `json:"statuses,omitempty"`

	// Type filters
	DerivationType  *string  `json:"derivation_type,omitempty"`
	DerivationTypes []string `json:"derivation_types,omitempty"`
	DocumentType    *string  `json:"document_type,omitempty"`
	DocumentTypes   []string `json:"document_types,omitempty"`

	// Time range filters
	CreatedAfter  *time.Time `json:"created_after,omitempty"`
	CreatedBefore *time.Time `json:"created_before,omitempty"`
	UpdatedAfter  *time.Time `json:"updated_after,omitempty"`
	UpdatedBefore *time.Time `json:"updated_before,omitempty"`

	// Pagination
	Limit  *int `json:"limit,omitempty"`
	Offset *int `json:"offset,omitempty"`

	// Sorting
	SortBy    *string `json:"sort_by,omitempty"`    // created_at, updated_at, name
	SortOrder *string `json:"sort_order,omitempty"` // asc, desc

	// Special flags
	IncludeDeleted bool `json:"include_deleted,omitempty"`
}

ContentFilters defines flexible filtering options for admin operations

type ContentStatistics

type ContentStatistics struct {
	TotalCount       int64            `json:"total_count"`
	ByStatus         map[string]int64 `json:"by_status,omitempty"`
	ByTenant         map[string]int64 `json:"by_tenant,omitempty"`
	ByDerivationType map[string]int64 `json:"by_derivation_type,omitempty"`
	ByDocumentType   map[string]int64 `json:"by_document_type,omitempty"`
	OldestContent    *time.Time       `json:"oldest_content,omitempty"`
	NewestContent    *time.Time       `json:"newest_content,omitempty"`
}

ContentStatistics provides aggregated statistics about content

type CountRequest

type CountRequest struct {
	Filters ContentFilters `json:"filters"`
}

CountRequest contains parameters for counting contents

type CountResponse

type CountResponse struct {
	Count int64 `json:"count"`
}

CountResponse contains the count result

type ListContentsOption

type ListContentsOption func(*ContentFilters)

ListContentsOption provides functional options for listing contents

func WithCreatedAfter

func WithCreatedAfter(t time.Time) ListContentsOption

WithCreatedAfter filters by created after time

func WithCreatedBefore

func WithCreatedBefore(t time.Time) ListContentsOption

WithCreatedBefore filters by created before time

func WithDerivationType

func WithDerivationType(derivationType string) ListContentsOption

WithDerivationType filters by derivation type

func WithDerivationTypes

func WithDerivationTypes(derivationTypes ...string) ListContentsOption

WithDerivationTypes filters by multiple derivation types

func WithDocumentType

func WithDocumentType(documentType string) ListContentsOption

WithDocumentType filters by document type

func WithDocumentTypes

func WithDocumentTypes(documentTypes ...string) ListContentsOption

WithDocumentTypes filters by multiple document types

func WithIncludeDeleted

func WithIncludeDeleted() ListContentsOption

WithIncludeDeleted includes deleted contents in results

func WithLimit

func WithLimit(limit int) ListContentsOption

WithLimit sets the pagination limit

func WithOffset

func WithOffset(offset int) ListContentsOption

WithOffset sets the pagination offset

func WithOwnerID

func WithOwnerID(ownerID uuid.UUID) ListContentsOption

WithOwnerID filters by owner ID

func WithOwnerIDs

func WithOwnerIDs(ownerIDs ...uuid.UUID) ListContentsOption

WithOwnerIDs filters by multiple owner IDs

func WithPagination

func WithPagination(limit, offset int) ListContentsOption

WithPagination sets both limit and offset

func WithSortBy

func WithSortBy(sortBy string) ListContentsOption

WithSortBy sets the sort field

func WithSortOrder

func WithSortOrder(sortOrder string) ListContentsOption

WithSortOrder sets the sort order

func WithStatus

func WithStatus(status string) ListContentsOption

WithStatus filters by status

func WithStatuses

func WithStatuses(statuses ...string) ListContentsOption

WithStatuses filters by multiple statuses

func WithTenantID

func WithTenantID(tenantID uuid.UUID) ListContentsOption

WithTenantID filters by tenant ID

func WithTenantIDs

func WithTenantIDs(tenantIDs ...uuid.UUID) ListContentsOption

WithTenantIDs filters by multiple tenant IDs

func WithUpdatedAfter

func WithUpdatedAfter(t time.Time) ListContentsOption

WithUpdatedAfter filters by updated after time

func WithUpdatedBefore

func WithUpdatedBefore(t time.Time) ListContentsOption

WithUpdatedBefore filters by updated before time

type ListContentsRequest

type ListContentsRequest struct {
	Filters ContentFilters `json:"filters"`
}

ListContentsRequest contains parameters for admin content listing

type ListContentsResponse

type ListContentsResponse struct {
	Contents   []*simplecontent.Content `json:"contents"`
	TotalCount *int64                   `json:"total_count,omitempty"` // Optional, set if count was requested
	Limit      int                      `json:"limit"`
	Offset     int                      `json:"offset"`
	HasMore    bool                     `json:"has_more"`
}

ListContentsResponse contains the paginated list of contents

type StatisticsOptions

type StatisticsOptions struct {
	IncludeStatusBreakdown       bool `json:"include_status_breakdown"`
	IncludeTenantBreakdown       bool `json:"include_tenant_breakdown"`
	IncludeDerivationBreakdown   bool `json:"include_derivation_breakdown"`
	IncludeDocumentTypeBreakdown bool `json:"include_document_type_breakdown"`
	IncludeTimeRange             bool `json:"include_time_range"`
}

StatisticsOptions defines what statistics to compute

func DefaultStatisticsOptions

func DefaultStatisticsOptions() StatisticsOptions

DefaultStatisticsOptions returns statistics options with all breakdowns enabled

type StatisticsRequest

type StatisticsRequest struct {
	Filters ContentFilters    `json:"filters"`
	Options StatisticsOptions `json:"options"`
}

StatisticsRequest contains parameters for retrieving content statistics

type StatisticsResponse

type StatisticsResponse struct {
	Statistics ContentStatistics `json:"statistics"`
	ComputedAt time.Time         `json:"computed_at"`
}

StatisticsResponse contains the statistics result

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL