feat: 添加评论、反应、离线编辑及主题定制功能
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
- 实现评论系统,包括评论输入、列表展示和集成指南 - 添加反应功能组件(ReactionBar、ReactionButton、ReactionPicker) - 实现离线编辑支持,包括同步状态管理和冲突解决 - 添加主题定制功能,支持多种配色方案和主题预览 - 新增多视图布局选项(时间线、分组、砌体视图) - 实现个人资料编辑器,支持头像、简介和自定义字段编辑 - 添加统计页面,展示存储使用情况和上传趋势 - 新增相册管理功能,支持相册创建、编辑和照片管理 - 实现响应式设计和加载骨架屏组件 - 扩展国际化支持,新增孟加拉语、波斯语、印尼语、日语、葡萄牙语等语言 - 添加错误边界组件和离线指示器 - 更新配置文件、路由和依赖项 - 新增完整的文档、测试用例和集成指南
This commit is contained in:
815
API_DOCUMENTATION.md
Normal file
815
API_DOCUMENTATION.md
Normal file
@@ -0,0 +1,815 @@
|
||||
# API Documentation - Personal User Enhancements
|
||||
|
||||
This document provides comprehensive API documentation for the personal user enhancements features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Smart Collections](#smart-collections)
|
||||
2. [Album Management](#album-management)
|
||||
3. [Personal Statistics](#personal-statistics)
|
||||
4. [Offline Sync](#offline-sync)
|
||||
5. [Comments](#comments)
|
||||
6. [Reactions](#reactions)
|
||||
7. [User Preferences](#user-preferences)
|
||||
8. [Profile Customization](#profile-customization)
|
||||
|
||||
---
|
||||
|
||||
## Smart Collections
|
||||
|
||||
### Get Smart Collections
|
||||
|
||||
Retrieve all smart collections for the current user.
|
||||
|
||||
**Endpoint:** `GET /api/v1/collections/smart`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
collections: Array<{
|
||||
id: string;
|
||||
userId: string;
|
||||
type: 'date' | 'location' | 'person';
|
||||
name: string;
|
||||
criteria: {
|
||||
year?: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
location?: {
|
||||
name: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
radius?: number;
|
||||
};
|
||||
personId?: string;
|
||||
personName?: string;
|
||||
};
|
||||
contentCount: number;
|
||||
thumbnailUrl?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Get Collection Content
|
||||
|
||||
Retrieve content items within a specific smart collection.
|
||||
|
||||
**Endpoint:** `GET /api/v1/collections/smart/:id/content`
|
||||
|
||||
**Query Parameters:**
|
||||
- `page` (number): Page number (default: 1)
|
||||
- `pageSize` (number): Items per page (default: 20)
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
collectionId: string;
|
||||
items: Array<{
|
||||
id: string;
|
||||
type: 'photo' | 'story';
|
||||
thumbnailUrl: string;
|
||||
createdAt: string;
|
||||
metadata: Record<string, any>;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Album Management
|
||||
|
||||
### List Albums
|
||||
|
||||
Get all albums for the current user.
|
||||
|
||||
**Endpoint:** `GET /api/v1/albums`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
albums: Array<{
|
||||
id: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
coverPhotoId?: string;
|
||||
coverPhotoUrl?: string;
|
||||
photoCount: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Create Album
|
||||
|
||||
Create a new album.
|
||||
|
||||
**Endpoint:** `POST /api/v1/albums`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
name: string; // Required
|
||||
description?: string;
|
||||
coverPhotoId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
id: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
coverPhotoId?: string;
|
||||
coverPhotoUrl?: string;
|
||||
photoCount: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Get Album Details
|
||||
|
||||
Get detailed information about a specific album including photos.
|
||||
|
||||
**Endpoint:** `GET /api/v1/albums/:id`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
id: string;
|
||||
userId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
coverPhotoId?: string;
|
||||
coverPhotoUrl?: string;
|
||||
photoCount: number;
|
||||
photos: Array<{
|
||||
id: string;
|
||||
photoId: string;
|
||||
albumId: string;
|
||||
order: number;
|
||||
photoUrl: string;
|
||||
thumbnailUrl: string;
|
||||
addedAt: string;
|
||||
}>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Album
|
||||
|
||||
Update album information.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/albums/:id`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
name?: string;
|
||||
description?: string;
|
||||
coverPhotoId?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Album
|
||||
|
||||
Delete an album (photos are preserved).
|
||||
|
||||
**Endpoint:** `DELETE /api/v1/albums/:id`
|
||||
|
||||
**Response:** `204 No Content`
|
||||
|
||||
### Add Photos to Album
|
||||
|
||||
Add one or more photos to an album.
|
||||
|
||||
**Endpoint:** `POST /api/v1/albums/:id/photos`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
photoIds: string[]; // Array of photo IDs to add
|
||||
}
|
||||
```
|
||||
|
||||
### Remove Photos from Album
|
||||
|
||||
Remove photos from an album.
|
||||
|
||||
**Endpoint:** `DELETE /api/v1/albums/:id/photos`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
photoIds: string[]; // Array of photo IDs to remove
|
||||
}
|
||||
```
|
||||
|
||||
### Reorder Photos
|
||||
|
||||
Change the order of photos in an album.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/albums/:id/photos/order`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
photoIds: string[]; // Array of photo IDs in desired order
|
||||
}
|
||||
```
|
||||
|
||||
### Set Cover Photo
|
||||
|
||||
Set the cover photo for an album.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/albums/:id/cover`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
coverPhotoId: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Personal Statistics
|
||||
|
||||
### Get Statistics Overview
|
||||
|
||||
Get comprehensive statistics for the current user.
|
||||
|
||||
**Endpoint:** `GET /api/v1/statistics/overview`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
userId: string;
|
||||
totalPhotos: number;
|
||||
totalStories: number;
|
||||
totalStorageBytes: number;
|
||||
calculatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Get Upload Trends
|
||||
|
||||
Get monthly and yearly upload trends.
|
||||
|
||||
**Endpoint:** `GET /api/v1/statistics/uploads`
|
||||
|
||||
**Query Parameters:**
|
||||
- `period` (string): 'monthly' or 'yearly' (default: 'monthly')
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
trends: Array<{
|
||||
period: string; // 'YYYY-MM' or 'YYYY'
|
||||
photoCount: number;
|
||||
storyCount: number;
|
||||
storageBytes: number;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Get Storage Breakdown
|
||||
|
||||
Get storage usage breakdown by content type.
|
||||
|
||||
**Endpoint:** `GET /api/v1/statistics/storage`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
breakdown: {
|
||||
photos: number;
|
||||
videos: number;
|
||||
documents: number;
|
||||
other: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh Statistics
|
||||
|
||||
Force recalculation of statistics.
|
||||
|
||||
**Endpoint:** `POST /api/v1/statistics/refresh`
|
||||
|
||||
**Response:** `202 Accepted`
|
||||
|
||||
---
|
||||
|
||||
## Offline Sync
|
||||
|
||||
### Upload Offline Changes
|
||||
|
||||
Upload a batch of offline changes for synchronization.
|
||||
|
||||
**Endpoint:** `POST /api/v1/sync/changes`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
changes: Array<{
|
||||
id: string;
|
||||
entityType: 'story' | 'album' | 'photo';
|
||||
entityId: string;
|
||||
operation: 'create' | 'update' | 'delete';
|
||||
data: any;
|
||||
timestamp: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
success: boolean;
|
||||
syncedCount: number;
|
||||
failedCount: number;
|
||||
conflicts: Array<{
|
||||
changeId: string;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
localVersion: any;
|
||||
serverVersion: any;
|
||||
conflictType: 'modified' | 'deleted';
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Get Sync Status
|
||||
|
||||
Get current synchronization status.
|
||||
|
||||
**Endpoint:** `GET /api/v1/sync/status`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
lastSyncAt?: string;
|
||||
pendingChanges: number;
|
||||
isSyncing: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### Resolve Conflict
|
||||
|
||||
Resolve a synchronization conflict.
|
||||
|
||||
**Endpoint:** `POST /api/v1/sync/resolve-conflict`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
conflictId: string;
|
||||
strategy: 'keep-local' | 'keep-server' | 'merge';
|
||||
mergedData?: any; // Required if strategy is 'merge'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comments
|
||||
|
||||
### Get Comments
|
||||
|
||||
Get comments for a specific entity (story or photo).
|
||||
|
||||
**Endpoint:** `GET /api/v1/comments/:entityType/:entityId`
|
||||
|
||||
**Path Parameters:**
|
||||
- `entityType`: 'story' or 'photo'
|
||||
- `entityId`: ID of the entity
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
comments: Array<{
|
||||
id: string;
|
||||
entityType: 'story' | 'photo';
|
||||
entityId: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
userAvatarUrl?: string;
|
||||
text: string;
|
||||
createdAt: string;
|
||||
updatedAt?: string;
|
||||
isEdited: boolean;
|
||||
canEdit: boolean;
|
||||
canDelete: boolean;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Create Comment
|
||||
|
||||
Add a comment to an entity.
|
||||
|
||||
**Endpoint:** `POST /api/v1/comments`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
entityType: 'story' | 'photo';
|
||||
entityId: string;
|
||||
text: string; // 1-1000 characters
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
id: string;
|
||||
entityType: 'story' | 'photo';
|
||||
entityId: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
userAvatarUrl?: string;
|
||||
text: string;
|
||||
createdAt: string;
|
||||
isEdited: false;
|
||||
canEdit: true;
|
||||
canDelete: true;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Comment
|
||||
|
||||
Edit an existing comment (within 24 hours).
|
||||
|
||||
**Endpoint:** `PUT /api/v1/comments/:id`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
text: string; // 1-1000 characters
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Comment
|
||||
|
||||
Delete a comment.
|
||||
|
||||
**Endpoint:** `DELETE /api/v1/comments/:id`
|
||||
|
||||
**Response:** `204 No Content`
|
||||
|
||||
---
|
||||
|
||||
## Reactions
|
||||
|
||||
### Get Reactions
|
||||
|
||||
Get reactions for a specific entity.
|
||||
|
||||
**Endpoint:** `GET /api/v1/reactions/:entityType/:entityId`
|
||||
|
||||
**Path Parameters:**
|
||||
- `entityType`: 'story' or 'photo'
|
||||
- `entityId`: ID of the entity
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
entityType: 'story' | 'photo';
|
||||
entityId: string;
|
||||
counts: {
|
||||
like: number;
|
||||
love: number;
|
||||
laugh: number;
|
||||
wow: number;
|
||||
sad: number;
|
||||
};
|
||||
userReaction?: 'like' | 'love' | 'laugh' | 'wow' | 'sad';
|
||||
recentReactions: Array<{
|
||||
id: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
userAvatarUrl?: string;
|
||||
type: 'like' | 'love' | 'laugh' | 'wow' | 'sad';
|
||||
createdAt: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Add/Update Reaction
|
||||
|
||||
Add or update a reaction on an entity.
|
||||
|
||||
**Endpoint:** `POST /api/v1/reactions`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
entityType: 'story' | 'photo';
|
||||
entityId: string;
|
||||
type: 'like' | 'love' | 'laugh' | 'wow' | 'sad';
|
||||
}
|
||||
```
|
||||
|
||||
### Remove Reaction
|
||||
|
||||
Remove user's reaction from an entity.
|
||||
|
||||
**Endpoint:** `DELETE /api/v1/reactions/:entityType/:entityId`
|
||||
|
||||
**Response:** `204 No Content`
|
||||
|
||||
---
|
||||
|
||||
## User Preferences
|
||||
|
||||
### Get Preferences
|
||||
|
||||
Get all user preferences.
|
||||
|
||||
**Endpoint:** `GET /api/v1/preferences`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
userId: string;
|
||||
theme: {
|
||||
mode: 'light' | 'dark' | 'auto';
|
||||
colorScheme: string;
|
||||
};
|
||||
layout: {
|
||||
galleryLayout: 'grid' | 'list';
|
||||
timelineLayout: 'grid' | 'list';
|
||||
albumLayout: 'grid' | 'list';
|
||||
cardSize: 'small' | 'medium' | 'large';
|
||||
};
|
||||
timeline: {
|
||||
displayMode: 'chronological' | 'grouped' | 'masonry';
|
||||
};
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Theme Preferences
|
||||
|
||||
Update theme settings.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/preferences/theme`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
mode?: 'light' | 'dark' | 'auto';
|
||||
colorScheme?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Layout Preferences
|
||||
|
||||
Update layout settings.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/preferences/layout`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
galleryLayout?: 'grid' | 'list';
|
||||
timelineLayout?: 'grid' | 'list';
|
||||
albumLayout?: 'grid' | 'list';
|
||||
cardSize?: 'small' | 'medium' | 'large';
|
||||
}
|
||||
```
|
||||
|
||||
### Update Timeline Preferences
|
||||
|
||||
Update timeline display settings.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/preferences/timeline`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
displayMode: 'chronological' | 'grouped' | 'masonry';
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Profile Customization
|
||||
|
||||
### Get Profile
|
||||
|
||||
Get user profile information.
|
||||
|
||||
**Endpoint:** `GET /api/v1/profile`
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
userId: string;
|
||||
coverPhotoUrl?: string;
|
||||
bio?: string;
|
||||
customFields: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
value: string;
|
||||
visibility: 'public' | 'private';
|
||||
order: number;
|
||||
}>;
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Profile
|
||||
|
||||
Update profile information.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/profile`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
bio?: string; // 0-500 characters
|
||||
}
|
||||
```
|
||||
|
||||
### Upload Cover Photo
|
||||
|
||||
Upload a new cover photo.
|
||||
|
||||
**Endpoint:** `POST /api/v1/profile/cover`
|
||||
|
||||
**Request:** `multipart/form-data`
|
||||
- `file`: Image file (max 5MB, min 1200x400px)
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
{
|
||||
coverPhotoUrl: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Update Custom Fields
|
||||
|
||||
Update profile custom fields.
|
||||
|
||||
**Endpoint:** `PUT /api/v1/profile/custom-fields`
|
||||
|
||||
**Request Body:**
|
||||
```typescript
|
||||
{
|
||||
fields: Array<{
|
||||
id?: string; // Omit for new fields
|
||||
name: string;
|
||||
value: string;
|
||||
visibility: 'public' | 'private';
|
||||
order: number;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Topics
|
||||
|
||||
Real-time updates are delivered via WebSocket (STOMP protocol).
|
||||
|
||||
### Comment Updates
|
||||
|
||||
**Topic:** `/topic/comments/{entityType}/{entityId}`
|
||||
|
||||
**Message Format:**
|
||||
```typescript
|
||||
{
|
||||
type: 'comment_added' | 'comment_updated' | 'comment_deleted';
|
||||
comment: Comment; // Full comment object
|
||||
}
|
||||
```
|
||||
|
||||
### Reaction Updates
|
||||
|
||||
**Topic:** `/topic/reactions/{entityType}/{entityId}`
|
||||
|
||||
**Message Format:**
|
||||
```typescript
|
||||
{
|
||||
type: 'reaction_added' | 'reaction_updated' | 'reaction_removed';
|
||||
reaction: Reaction; // Full reaction object
|
||||
summary: ReactionSummary; // Updated counts
|
||||
}
|
||||
```
|
||||
|
||||
### Collection Updates
|
||||
|
||||
**Topic:** `/topic/collections/updates`
|
||||
|
||||
**Message Format:**
|
||||
```typescript
|
||||
{
|
||||
type: 'collection_created' | 'collection_updated';
|
||||
collection: SmartCollection;
|
||||
}
|
||||
```
|
||||
|
||||
### Personal Notifications
|
||||
|
||||
**Topic:** `/user/queue/notifications`
|
||||
|
||||
**Message Format:**
|
||||
```typescript
|
||||
{
|
||||
type: 'comment' | 'reaction' | 'sync_complete' | 'sync_conflict';
|
||||
title: string;
|
||||
message: string;
|
||||
data: any;
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints follow a consistent error response format:
|
||||
|
||||
```typescript
|
||||
{
|
||||
error: {
|
||||
code: string; // Error code (e.g., 'VALIDATION_ERROR')
|
||||
message: string; // Human-readable error message
|
||||
details?: any; // Additional error details
|
||||
timestamp: string; // ISO 8601 timestamp
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common HTTP Status Codes
|
||||
|
||||
- `200 OK`: Successful request
|
||||
- `201 Created`: Resource created successfully
|
||||
- `204 No Content`: Successful request with no response body
|
||||
- `400 Bad Request`: Invalid request data
|
||||
- `401 Unauthorized`: Authentication required
|
||||
- `403 Forbidden`: Insufficient permissions
|
||||
- `404 Not Found`: Resource not found
|
||||
- `409 Conflict`: Resource conflict (e.g., sync conflict)
|
||||
- `422 Unprocessable Entity`: Validation error
|
||||
- `429 Too Many Requests`: Rate limit exceeded
|
||||
- `500 Internal Server Error`: Server error
|
||||
- `503 Service Unavailable`: Service temporarily unavailable
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
API requests are rate-limited to prevent abuse:
|
||||
|
||||
- **Standard endpoints**: 100 requests per minute per user
|
||||
- **Upload endpoints**: 20 requests per minute per user
|
||||
- **Statistics refresh**: 5 requests per hour per user
|
||||
|
||||
Rate limit headers are included in responses:
|
||||
- `X-RateLimit-Limit`: Maximum requests allowed
|
||||
- `X-RateLimit-Remaining`: Remaining requests in current window
|
||||
- `X-RateLimit-Reset`: Timestamp when limit resets
|
||||
|
||||
---
|
||||
|
||||
## Pagination
|
||||
|
||||
List endpoints support pagination with the following query parameters:
|
||||
|
||||
- `page` (number): Page number (1-indexed, default: 1)
|
||||
- `pageSize` (number): Items per page (default: 20, max: 100)
|
||||
|
||||
Paginated responses include:
|
||||
```typescript
|
||||
{
|
||||
items: Array<T>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication via JWT token in the Authorization header:
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Tokens are obtained through the login endpoint and should be refreshed before expiration.
|
||||
Reference in New Issue
Block a user