Refactor API structure and add comprehensive user management features

- Restructure API v1 with improved serializers organization
- Add user deletion requests and moderation queue system
- Implement bulk moderation operations and permissions
- Add user profile enhancements with display names and avatars
- Expand ride and park API endpoints with better filtering
- Add manufacturer API with detailed ride relationships
- Improve authentication flows and error handling
- Update frontend documentation and API specifications
This commit is contained in:
pacnpal
2025-08-29 16:03:51 -04:00
parent 7b9f64be72
commit bb7da85516
92 changed files with 19690 additions and 9076 deletions

View File

@@ -1,420 +1,372 @@
# ThrillWiki Frontend API Documentation
This document provides comprehensive documentation for frontend developers on how to integrate with the ThrillWiki API endpoints.
Last updated: 2025-08-29
## Base URL
```
http://localhost:8000/api/v1/
```
This document provides comprehensive documentation for all ThrillWiki API endpoints that the NextJS frontend should use.
## Authentication
Most endpoints are publicly accessible. Admin endpoints require authentication.
## Content Discovery Endpoints
All API requests require authentication via JWT tokens. Include the token in the Authorization header:
```typescript
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
```
## Base URL
All API endpoints are prefixed with `/api/v1/`
## Moderation System API
The moderation system provides comprehensive content moderation, user management, and administrative tools. All moderation endpoints require moderator-level permissions or above.
### Moderation Reports
#### List Reports
- **GET** `/api/v1/moderation/reports/`
- **Permissions**: Moderators and above can view all reports, regular users can only view their own reports
- **Query Parameters**:
- `status`: Filter by report status (PENDING, UNDER_REVIEW, RESOLVED, DISMISSED)
- `priority`: Filter by priority (LOW, MEDIUM, HIGH, URGENT)
- `report_type`: Filter by report type (SPAM, HARASSMENT, INAPPROPRIATE_CONTENT, etc.)
- `reported_by`: Filter by user ID who made the report
- `assigned_moderator`: Filter by assigned moderator ID
- `created_after`: Filter reports created after date (ISO format)
- `created_before`: Filter reports created before date (ISO format)
- `unassigned`: Boolean filter for unassigned reports
- `overdue`: Boolean filter for overdue reports based on SLA
- `search`: Search in reason and description fields
- `ordering`: Order by fields (created_at, updated_at, priority, status)
#### Create Report
- **POST** `/api/v1/moderation/reports/`
- **Permissions**: Any authenticated user
- **Body**: CreateModerationReportData
#### Get Report Details
- **GET** `/api/v1/moderation/reports/{id}/`
- **Permissions**: Moderators and above, or report creator
#### Update Report
- **PATCH** `/api/v1/moderation/reports/{id}/`
- **Permissions**: Moderators and above
- **Body**: Partial UpdateModerationReportData
#### Assign Report
- **POST** `/api/v1/moderation/reports/{id}/assign/`
- **Permissions**: Moderators and above
- **Body**: `{ "moderator_id": number }`
#### Resolve Report
- **POST** `/api/v1/moderation/reports/{id}/resolve/`
- **Permissions**: Moderators and above
- **Body**: `{ "resolution_action": string, "resolution_notes": string }`
#### Report Statistics
- **GET** `/api/v1/moderation/reports/stats/`
- **Permissions**: Moderators and above
- **Returns**: ModerationStatsData
### Moderation Queue
#### List Queue Items
- **GET** `/api/v1/moderation/queue/`
- **Permissions**: Moderators and above
- **Query Parameters**:
- `status`: Filter by status (PENDING, IN_PROGRESS, COMPLETED, CANCELLED)
- `priority`: Filter by priority (LOW, MEDIUM, HIGH, URGENT)
- `item_type`: Filter by item type (CONTENT_REVIEW, USER_REVIEW, BULK_ACTION, etc.)
- `assigned_to`: Filter by assigned moderator ID
- `unassigned`: Boolean filter for unassigned items
- `has_related_report`: Boolean filter for items with related reports
- `search`: Search in title and description fields
#### Get My Queue
- **GET** `/api/v1/moderation/queue/my_queue/`
- **Permissions**: Moderators and above
- **Returns**: Queue items assigned to current user
#### Assign Queue Item
- **POST** `/api/v1/moderation/queue/{id}/assign/`
- **Permissions**: Moderators and above
- **Body**: `{ "moderator_id": number }`
#### Unassign Queue Item
- **POST** `/api/v1/moderation/queue/{id}/unassign/`
- **Permissions**: Moderators and above
#### Complete Queue Item
- **POST** `/api/v1/moderation/queue/{id}/complete/`
- **Permissions**: Moderators and above
- **Body**: CompleteQueueItemData
### Moderation Actions
#### List Actions
- **GET** `/api/v1/moderation/actions/`
- **Permissions**: Moderators and above
- **Query Parameters**:
- `action_type`: Filter by action type (WARNING, USER_SUSPENSION, USER_BAN, etc.)
- `moderator`: Filter by moderator ID
- `target_user`: Filter by target user ID
- `is_active`: Boolean filter for active actions
- `expired`: Boolean filter for expired actions
- `expiring_soon`: Boolean filter for actions expiring within 24 hours
- `has_related_report`: Boolean filter for actions with related reports
#### Create Action
- **POST** `/api/v1/moderation/actions/`
- **Permissions**: Moderators and above (with role-based restrictions)
- **Body**: CreateModerationActionData
#### Get Active Actions
- **GET** `/api/v1/moderation/actions/active/`
- **Permissions**: Moderators and above
#### Get Expired Actions
- **GET** `/api/v1/moderation/actions/expired/`
- **Permissions**: Moderators and above
#### Deactivate Action
- **POST** `/api/v1/moderation/actions/{id}/deactivate/`
- **Permissions**: Moderators and above
### Bulk Operations
#### List Bulk Operations
- **GET** `/api/v1/moderation/bulk-operations/`
- **Permissions**: Admins and superusers only
- **Query Parameters**:
- `status`: Filter by status (PENDING, RUNNING, COMPLETED, FAILED, CANCELLED)
- `operation_type`: Filter by operation type
- `priority`: Filter by priority
- `created_by`: Filter by creator ID
- `can_cancel`: Boolean filter for cancellable operations
- `has_failures`: Boolean filter for operations with failures
- `in_progress`: Boolean filter for operations in progress
#### Create Bulk Operation
- **POST** `/api/v1/moderation/bulk-operations/`
- **Permissions**: Admins and superusers only
- **Body**: CreateBulkOperationData
#### Get Running Operations
- **GET** `/api/v1/moderation/bulk-operations/running/`
- **Permissions**: Admins and superusers only
#### Cancel Operation
- **POST** `/api/v1/moderation/bulk-operations/{id}/cancel/`
- **Permissions**: Admins and superusers only
#### Retry Operation
- **POST** `/api/v1/moderation/bulk-operations/{id}/retry/`
- **Permissions**: Admins and superusers only
#### Get Operation Logs
- **GET** `/api/v1/moderation/bulk-operations/{id}/logs/`
- **Permissions**: Admins and superusers only
### User Moderation
#### Get User Moderation Profile
- **GET** `/api/v1/moderation/users/{id}/`
- **Permissions**: Moderators and above
- **Returns**: UserModerationProfileData
#### Take Action Against User
- **POST** `/api/v1/moderation/users/{id}/moderate/`
- **Permissions**: Moderators and above
- **Body**: CreateModerationActionData
#### Search Users
- **GET** `/api/v1/moderation/users/search/`
- **Permissions**: Moderators and above
- **Query Parameters**:
- `query`: Search in username and email
- `role`: Filter by user role
- `has_restrictions`: Boolean filter for users with active restrictions
#### User Moderation Statistics
- **GET** `/api/v1/moderation/users/stats/`
- **Permissions**: Moderators and above
## Parks API
### Parks Listing
- **GET** `/api/v1/parks/`
- **Query Parameters**:
- `search`: Search in park names and descriptions
- `country`: Filter by country code
- `state`: Filter by state/province
- `city`: Filter by city
- `status`: Filter by operational status
- `park_type`: Filter by park type
- `has_rides`: Boolean filter for parks with rides
- `ordering`: Order by fields (name, opened_date, ride_count, etc.)
- `page`: Page number for pagination
- `page_size`: Number of results per page
### Park Details
- **GET** `/api/v1/parks/{slug}/`
- **Returns**: Complete park information including rides, photos, and statistics
### Park Rides
- **GET** `/api/v1/parks/{park_slug}/rides/`
- **Query Parameters**: Similar filtering options as global rides endpoint
### Park Photos
- **GET** `/api/v1/parks/{park_slug}/photos/`
- **Query Parameters**:
- `photo_type`: Filter by photo type (banner, card, gallery)
- `ordering`: Order by upload date, likes, etc.
## Rides API
### Rides Listing
- **GET** `/api/v1/rides/`
- **Query Parameters**:
- `search`: Search in ride names and descriptions
- `park`: Filter by park slug
- `manufacturer`: Filter by manufacturer slug
- `ride_type`: Filter by ride type
- `status`: Filter by operational status
- `opened_after`: Filter rides opened after date
- `opened_before`: Filter rides opened before date
- `height_min`: Minimum height requirement
- `height_max`: Maximum height requirement
- `has_photos`: Boolean filter for rides with photos
- `ordering`: Order by fields (name, opened_date, height, etc.)
### Ride Details
- **GET** `/api/v1/rides/{park_slug}/{ride_slug}/`
- **Returns**: Complete ride information including specifications, photos, and reviews
### Ride Photos
- **GET** `/api/v1/rides/{park_slug}/{ride_slug}/photos/`
### Ride Reviews
- **GET** `/api/v1/rides/{park_slug}/{ride_slug}/reviews/`
- **POST** `/api/v1/rides/{park_slug}/{ride_slug}/reviews/`
## Manufacturers API
### Manufacturers Listing
- **GET** `/api/v1/rides/manufacturers/`
- **Query Parameters**:
- `search`: Search in manufacturer names
- `country`: Filter by country
- `has_rides`: Boolean filter for manufacturers with rides
- `ordering`: Order by name, ride_count, etc.
### Manufacturer Details
- **GET** `/api/v1/rides/manufacturers/{slug}/`
### Manufacturer Rides
- **GET** `/api/v1/rides/manufacturers/{slug}/rides/`
## Authentication API
### Login
- **POST** `/api/v1/auth/login/`
- **Body**: `{ "username": string, "password": string }`
- **Returns**: JWT tokens and user data
### Signup
- **POST** `/api/v1/auth/signup/`
- **Body**: User registration data
### Logout
- **POST** `/api/v1/auth/logout/`
### Current User
- **GET** `/api/v1/auth/user/`
- **Returns**: Current user profile data
### Password Reset
- **POST** `/api/v1/auth/password/reset/`
- **Body**: `{ "email": string }`
### Password Change
- **POST** `/api/v1/auth/password/change/`
- **Body**: `{ "old_password": string, "new_password": string }`
## Statistics API
### Global Statistics
- **GET** `/api/v1/stats/`
- **Returns**: Global platform statistics
### Trending Content
Get trending parks and rides based on view counts, ratings, and recency.
**Endpoint:** `GET /trending/content/`
**Parameters:**
- `limit` (optional): Number of trending items to return (default: 20, max: 100)
- `timeframe` (optional): Timeframe for trending calculation - "day", "week", "month" (default: "week")
**Response Format:**
```json
{
"trending_rides": [
{
"id": 137,
"name": "Steel Vengeance",
"park": "Cedar Point",
"category": "ride",
"rating": 4.8,
"rank": 1,
"views": 15234,
"views_change": "+25%",
"slug": "steel-vengeance",
"date_opened": "2018-05-05",
"url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/",
"park_url": "https://thrillwiki.com/parks/cedar-point/",
"card_image": "https://media.thrillwiki.com/rides/steel-vengeance-card.jpg"
}
],
"trending_parks": [
{
"id": 1,
"name": "Cedar Point",
"park": "Cedar Point",
"category": "park",
"rating": 4.6,
"rank": 1,
"views": 45678,
"views_change": "+12%",
"slug": "cedar-point",
"date_opened": "1870-01-01",
"url": "https://thrillwiki.com/parks/cedar-point/",
"card_image": "https://media.thrillwiki.com/parks/cedar-point-card.jpg",
"city": "Sandusky",
"state": "Ohio",
"country": "USA",
"primary_company": "Cedar Fair"
}
],
"latest_reviews": []
}
```
### New Content
Get recently added parks and rides.
**Endpoint:** `GET /trending/new/`
**Parameters:**
- `limit` (optional): Number of new items to return (default: 20, max: 100)
- `days` (optional): Number of days to look back for new content (default: 30, max: 365)
**Response Format:**
```json
{
"recently_added": [
{
"id": 137,
"name": "Steel Vengeance",
"park": "Cedar Point",
"category": "ride",
"date_added": "2018-05-05",
"date_opened": "2018-05-05",
"slug": "steel-vengeance",
"url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/",
"park_url": "https://thrillwiki.com/parks/cedar-point/",
"card_image": "https://media.thrillwiki.com/rides/steel-vengeance-card.jpg"
},
{
"id": 42,
"name": "Dollywood",
"park": "Dollywood",
"category": "park",
"date_added": "2018-05-01",
"date_opened": "1986-05-03",
"slug": "dollywood",
"url": "https://thrillwiki.com/parks/dollywood/",
"card_image": "https://media.thrillwiki.com/parks/dollywood-card.jpg",
"city": "Pigeon Forge",
"state": "Tennessee",
"country": "USA",
"primary_company": "Dollywood Company"
}
],
"newly_opened": [
{
"id": 136,
"name": "Time Traveler",
"park": "Silver Dollar City",
"category": "ride",
"date_added": "2018-04-28",
"date_opened": "2018-04-28",
"slug": "time-traveler",
"url": "https://thrillwiki.com/parks/silver-dollar-city/rides/time-traveler/",
"park_url": "https://thrillwiki.com/parks/silver-dollar-city/",
"card_image": "https://media.thrillwiki.com/rides/time-traveler-card.jpg"
}
],
"upcoming": []
}
```
**Key Changes:**
- **REMOVED:** `location` field from all trending and new content responses
- **ADDED:** `park` field - shows the park name for both parks and rides
- **ADDED:** `date_opened` field - shows when the park/ride originally opened
### Trigger Content Calculation (Admin Only)
Manually trigger the calculation of trending and new content.
**Endpoint:** `POST /trending/calculate/`
**Authentication:** Admin access required
**Response Format:**
```json
{
"message": "Trending content calculation completed",
"trending_completed": true,
"new_content_completed": true,
"completion_time": "2025-08-28 16:41:42",
"trending_output": "Successfully calculated 50 trending items for all",
"new_content_output": "Successfully calculated 50 new items for all"
}
```
## Data Field Descriptions
### Common Fields
- `id`: Unique identifier for the item
- `name`: Display name of the park or ride
- `park`: Name of the park (for rides, this is the parent park; for parks, this is the park itself)
- `category`: Type of content ("park" or "ride")
- `slug`: URL-friendly identifier
- `date_opened`: ISO date string of when the park/ride originally opened (YYYY-MM-DD format)
- `url`: Frontend URL for direct navigation to the item's detail page
- `card_image`: URL to the card image for display in lists and grids (available for both parks and rides)
### Park-Specific Fields
- `city`: City where the park is located (shortened format)
- `state`: State/province where the park is located (shortened format)
- `country`: Country where the park is located (shortened format)
- `primary_company`: Name of the primary operating company for the park
### Ride-Specific Fields
- `park_url`: Frontend URL for the ride's parent park
### Trending-Specific Fields
- `rating`: Average user rating (0.0 to 10.0)
- `rank`: Position in trending list (1-based)
- `views`: Current view count
- `views_change`: Percentage change in views (e.g., "+25%")
### New Content-Specific Fields
- `date_added`: ISO date string of when the item was added to the database (YYYY-MM-DD format)
## Implementation Notes
### Content Categorization
The API automatically categorizes new content based on dates:
- **Recently Added**: Items added to the database in the last 30 days
- **Newly Opened**: Items that opened in the last year
- **Upcoming**: Future openings (currently empty, reserved for future use)
### Caching
- Trending content is cached for 24 hours
- New content is cached for 30 minutes
- Use the admin trigger endpoint to force cache refresh
### Error Handling
All endpoints return standard HTTP status codes:
- `200`: Success
- `400`: Bad request (invalid parameters)
- `403`: Forbidden (admin endpoints only)
- `500`: Internal server error
### Rate Limiting
No rate limiting is currently implemented, but it may be added in the future.
## Migration from Previous API Format
If you were previously using the API with `location` fields, update your frontend code:
**Before:**
```javascript
const ride = {
name: "Steel Vengeance",
location: "Cedar Point", // OLD FIELD
category: "ride"
};
```
**After:**
```javascript
const ride = {
name: "Steel Vengeance",
park: "Cedar Point", // NEW FIELD
category: "ride",
date_opened: "2018-05-05" // NEW FIELD
};
```
## Backend Architecture Changes
The trending system has been migrated from Celery-based async processing to Django management commands for better reliability and simpler deployment:
### Management Commands
- `python manage.py calculate_trending` - Calculate trending content
- `python manage.py calculate_new_content` - Calculate new content
### Direct Calculation
The API now uses direct calculation instead of async tasks, providing immediate results while maintaining performance through caching.
## URL Fields for Frontend Navigation
All API responses now include dynamically generated `url` fields that provide direct links to the frontend pages for each entity. These URLs are generated based on the configured `FRONTEND_DOMAIN` setting.
### URL Patterns
- **Parks**: `https://domain.com/parks/{park-slug}/`
- **Rides**: `https://domain.com/parks/{park-slug}/rides/{ride-slug}/`
- **Ride Models**: `https://domain.com/rides/manufacturers/{manufacturer-slug}/{model-slug}/`
- **Companies (Operators)**: `https://domain.com/parks/operators/{operator-slug}/`
- **Companies (Property Owners)**: `https://domain.com/parks/owners/{owner-slug}/`
- **Companies (Manufacturers)**: `https://domain.com/rides/manufacturers/{manufacturer-slug}/`
- **Companies (Designers)**: `https://domain.com/rides/designers/{designer-slug}/`
### Domain Separation Rules
**CRITICAL**: Company URLs follow strict domain separation:
- **Parks Domain**: OPERATOR and PROPERTY_OWNER roles generate URLs under `/parks/`
- **Rides Domain**: MANUFACTURER and DESIGNER roles generate URLs under `/rides/`
- Companies with multiple roles use their primary role (first in the roles array) for URL generation
- URLs are auto-generated when entities are saved and stored in the database
### Example Response with URL Fields
```json
{
"id": 1,
"name": "Steel Vengeance",
"slug": "steel-vengeance",
"park": {
"id": 1,
"name": "Cedar Point",
"slug": "cedar-point",
"url": "https://thrillwiki.com/parks/cedar-point/"
},
"url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/",
"manufacturer": {
"id": 1,
"name": "Rocky Mountain Construction",
"slug": "rocky-mountain-construction",
"url": "https://thrillwiki.com/rides/manufacturers/rocky-mountain-construction/"
}
}
```
## Example Usage
### Fetch Trending Content
```javascript
const response = await fetch('/api/v1/trending/content/?limit=10');
const data = await response.json();
// Display trending rides with clickable links
data.trending_rides.forEach(ride => {
console.log(`${ride.name} at ${ride.park} - opened ${ride.date_opened}`);
console.log(`Visit: ${ride.url}`);
});
```
### Fetch New Content
```javascript
const response = await fetch('/api/v1/trending/new/?limit=5&days=7');
const data = await response.json();
// Display newly opened attractions
data.newly_opened.forEach(item => {
console.log(`${item.name} at ${item.park} - opened ${item.date_opened}`);
});
```
### Admin: Trigger Calculation
```javascript
const response = await fetch('/api/v1/trending/calculate/', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_ADMIN_TOKEN',
'Content-Type': 'application/json'
}
});
const result = await response.json();
console.log(result.message);
## Reviews Endpoints
- **GET** `/api/v1/trending/`
- **Query Parameters**:
- `content_type`: Filter by content type (parks, rides, reviews)
- `time_period`: Time period for trending (24h, 7d, 30d)
### Latest Reviews
Get the latest reviews from both parks and rides across the platform.
- **GET** `/api/v1/reviews/latest/`
- **Query Parameters**:
- `limit`: Number of reviews to return
- `park`: Filter by park slug
- `ride`: Filter by ride slug
**Endpoint:** `GET /reviews/latest/`
## Error Handling
**Parameters:**
- `limit` (optional): Number of reviews to return (default: 20, max: 100)
All API endpoints return standardized error responses:
**Response Format:**
```json
{
"count": 15,
"results": [
{
"id": 42,
"type": "ride",
"title": "Amazing coaster experience!",
"content_snippet": "This ride was absolutely incredible. The airtime was perfect and the inversions were smooth...",
"rating": 9,
"created_at": "2025-08-28T21:30:00Z",
"user": {
"username": "coaster_fan_2024",
"display_name": "Coaster Fan",
"avatar_url": "https://media.thrillwiki.com/avatars/user123.jpg"
},
"subject_name": "Steel Vengeance",
"subject_slug": "steel-vengeance",
"subject_url": "/parks/cedar-point/rides/steel-vengeance/",
"park_name": "Cedar Point",
"park_slug": "cedar-point",
"park_url": "/parks/cedar-point/"
},
{
"id": 38,
"type": "park",
"title": "Great family park",
"content_snippet": "Had a wonderful time with the family. The park was clean, staff was friendly, and there were rides for all ages...",
"rating": 8,
"created_at": "2025-08-28T20:15:00Z",
"user": {
"username": "family_fun",
"display_name": "Family Fun",
"avatar_url": "/static/images/default-avatar.png"
},
"subject_name": "Dollywood",
"subject_slug": "dollywood",
"subject_url": "/parks/dollywood/",
"park_name": null,
"park_slug": null,
"park_url": null
}
]
```typescript
interface ApiError {
status: "error";
error: {
code: string;
message: string;
details?: any;
request_user?: string;
};
data: null;
}
```
**Field Descriptions:**
- `id`: Unique review identifier
- `type`: Review type - "park" or "ride"
- `title`: Review title/headline
- `content_snippet`: Truncated review content (max 150 characters with smart word breaking)
- `rating`: User rating from 1-10
- `created_at`: ISO timestamp when review was created
- `user`: User information object
- `username`: User's unique username
- `display_name`: User's display name (falls back to username if not set)
- `avatar_url`: URL to user's avatar image (uses default if not set)
- `subject_name`: Name of the reviewed item (park or ride)
- `subject_slug`: URL slug of the reviewed item
- `subject_url`: Frontend URL to the reviewed item's detail page
- `park_name`: For ride reviews, the name of the parent park (null for park reviews)
- `park_slug`: For ride reviews, the slug of the parent park (null for park reviews)
- `park_url`: For ride reviews, the URL to the parent park (null for park reviews)
Common error codes:
- `NOT_AUTHENTICATED`: User not logged in
- `PERMISSION_DENIED`: Insufficient permissions
- `NOT_FOUND`: Resource not found
- `VALIDATION_ERROR`: Invalid request data
- `RATE_LIMITED`: Too many requests
**Authentication:** None required (public endpoint)
## Pagination
**Example Usage:**
```javascript
// Fetch latest 10 reviews
const response = await fetch('/api/v1/reviews/latest/?limit=10');
const data = await response.json();
List endpoints use cursor-based pagination:
// Display reviews
data.results.forEach(review => {
console.log(`${review.user.display_name} rated ${review.subject_name}: ${review.rating}/10`);
console.log(`"${review.title}" - ${review.content_snippet}`);
if (review.type === 'ride') {
console.log(`Ride at ${review.park_name}`);
}
});
```typescript
interface PaginatedResponse<T> {
status: "success";
data: {
results: T[];
count: number;
next: string | null;
previous: string | null;
};
error: null;
}
```
**Error Responses:**
- `400 Bad Request`: Invalid limit parameter
- `500 Internal Server Error`: Database or server error
## Rate Limiting
**Notes:**
- Reviews are filtered to only show published reviews (`is_published=True`)
- Results are sorted by creation date (newest first)
- Content snippets are intelligently truncated at word boundaries
- Avatar URLs fall back to default avatar if user hasn't uploaded one
- The endpoint combines reviews from both parks and rides into a single chronological feed
API endpoints are rate limited based on user role:
- Anonymous users: 100 requests/hour
- Authenticated users: 1000 requests/hour
- Moderators: 5000 requests/hour
- Admins: 10000 requests/hour
## WebSocket Connections
Real-time updates are available for:
- Moderation queue updates
- New reports and actions
- Bulk operation progress
- Live statistics updates
Connect to: `ws://localhost:8000/ws/moderation/` (requires authentication)