1440 lines
32 KiB
Markdown
1440 lines
32 KiB
Markdown
# SerpentRace Backend API Documentation for Frontend Developers
|
|
## Complete API Reference with All Endpoints
|
|
|
|
## Table of Contents
|
|
1. [Test User Credentials](#test-user-credentials)
|
|
2. [Data Structures & Entities](#data-structures--entities)
|
|
3. [Base URL & Service Info](#base-url--service-info)
|
|
4. [Authentication Endpoints](#authentication-endpoints)
|
|
5. [User Management](#user-management)
|
|
6. [Deck Management](#deck-management)
|
|
7. [Organization Management](#organization-management)
|
|
8. [Chat System](#chat-system)
|
|
9. [Contact Management](#contact-management)
|
|
10. [Import/Export Functionality](#importexport-functionality)
|
|
11. [Admin Endpoints](#admin-endpoints)
|
|
12. [Error Handling](#error-handling)
|
|
13. [WebSocket Real-Time Communication](#websocket-real-time-communication)
|
|
|
|
---
|
|
|
|
## Test User Credentials
|
|
|
|
For development and testing, use these pre-configured user accounts:
|
|
|
|
### Regular User (Verified)
|
|
- **Username:** `john_doe`
|
|
- **Password:** `password123`
|
|
- **Email:** `john.doe@email.com`
|
|
- **Type:** Regular user (state: 1 - VERIFIED_REGULAR)
|
|
- **Organization:** None
|
|
|
|
### Premium User (Organization Member)
|
|
- **Username:** `jane_premium`
|
|
- **Password:** `password123`
|
|
- **Email:** `jane.smith@email.com`
|
|
- **Type:** Premium user (state: 2 - VERIFIED_PREMIUM)
|
|
- **Organization:** Tech Solutions Inc
|
|
|
|
### Teacher (Premium Organization Member)
|
|
- **Username:** `teacher_bob`
|
|
- **Password:** `password123`
|
|
- **Email:** `bob.teacher@eduinst.edu`
|
|
- **Type:** Premium user (state: 2 - VERIFIED_PREMIUM)
|
|
- **Organization:** Educational Institute
|
|
|
|
### Admin User
|
|
- **Username:** `admin_user`
|
|
- **Password:** `password123`
|
|
- **Email:** `admin@serpentrace.com`
|
|
- **Type:** Admin (state: 5 - ADMIN)
|
|
- **Organization:** None
|
|
|
|
### Unverified User
|
|
- **Username:** `new_user`
|
|
- **Password:** `password123`
|
|
- **Email:** `newuser@email.com`
|
|
- **Type:** Unverified (state: 0 - REGISTERED_NOT_VERIFIED)
|
|
- **Organization:** None
|
|
|
|
---
|
|
|
|
## Data Structures & Entities
|
|
|
|
### User DTOs
|
|
```typescript
|
|
interface ShortUserDto {
|
|
id: string; // UUID
|
|
username: string; // Username
|
|
state: number; // UserState enum
|
|
authLevel: 0 | 1; // 0 = regular, 1 = admin
|
|
}
|
|
|
|
interface DetailUserDto {
|
|
id: string; // UUID
|
|
orgid: string | null; // Organization ID (if member)
|
|
username: string; // Unique username
|
|
email: string; // Email address
|
|
fname: string; // First name
|
|
lname: string; // Last name
|
|
code: string | null; // Verification code
|
|
type: string; // 'personal' | 'premium' | 'admin'
|
|
phone: string | null; // Phone number
|
|
state: number; // UserState enum value
|
|
}
|
|
|
|
enum UserState {
|
|
REGISTERED_NOT_VERIFIED = 0, // Email not verified
|
|
VERIFIED_REGULAR = 1, // Regular verified user
|
|
VERIFIED_PREMIUM = 2, // Premium verified user
|
|
SOFT_DELETE = 3, // Soft deleted
|
|
DEACTIVATED = 4, // Account deactivated
|
|
ADMIN = 5 // Admin user
|
|
}
|
|
```
|
|
|
|
### Deck DTOs
|
|
```typescript
|
|
interface ShortDeckDto {
|
|
id: string; // UUID
|
|
name: string; // Deck name
|
|
type: number; // DeckType enum value
|
|
playedNumber: number; // Times played
|
|
ctype: number; // DeckVisibility enum value
|
|
}
|
|
|
|
interface DetailDeckDto {
|
|
id: string; // UUID
|
|
name: string; // Deck name
|
|
type: number; // DeckType enum value
|
|
userid: string; // Owner's user ID
|
|
creationdate: Date; // Creation timestamp
|
|
cards: Card[]; // Array of cards
|
|
playedNumber: number; // Times played
|
|
ctype: number; // DeckVisibility enum value
|
|
}
|
|
|
|
interface Card {
|
|
text: string; // Question/prompt text
|
|
type?: number; // CardType enum (optional)
|
|
answer?: string | null; // Answer (varies by type)
|
|
}
|
|
|
|
enum DeckType {
|
|
LUCK = 0, // Luck-based cards
|
|
JOKER = 1, // Joker/wild cards
|
|
QUESTION = 2 // Question-based cards
|
|
}
|
|
|
|
enum DeckVisibility {
|
|
PUBLIC = 0, // Public to all
|
|
PRIVATE = 1, // Private to owner
|
|
ORGANIZATION = 2 // Shared within organization
|
|
}
|
|
|
|
enum DeckState {
|
|
ACTIVE = 0, // Active deck
|
|
SOFT_DELETE = 1 // Soft deleted
|
|
}
|
|
|
|
enum CardType {
|
|
QUIZ = 0, // Multiple choice question
|
|
SENTENCE_PAIRING = 1, // Sentence completion
|
|
OWN_ANSWER = 2, // Custom answer
|
|
TRUE_FALSE = 3, // True/False question
|
|
CLOSER = 4 // Closer to answer
|
|
}
|
|
```
|
|
|
|
### Organization DTOs
|
|
```typescript
|
|
interface ShortOrganizationDto {
|
|
id: string; // UUID
|
|
name: string; // Organization name
|
|
contactfname: string; // Contact first name
|
|
contactlname: string; // Contact last name
|
|
contactemail: string; // Contact email
|
|
state: number; // OrganizationState enum
|
|
regdate: Date; // Registration date
|
|
maxOrganizationalDecks: number | null; // Max org decks allowed
|
|
}
|
|
|
|
enum OrganizationState {
|
|
REGISTERED = 0, // Just registered
|
|
ACTIVE = 1, // Active organization
|
|
SOFT_DELETE = 2 // Soft deleted
|
|
}
|
|
```
|
|
|
|
### Chat DTOs
|
|
```typescript
|
|
interface ShortChatDto {
|
|
id: string; // UUID
|
|
userCount: number; // Number of participants
|
|
state: number; // ChatState enum value
|
|
}
|
|
|
|
interface DetailChatDto {
|
|
id: string; // UUID
|
|
users: string[]; // Participant user IDs
|
|
messages: Message[]; // Message history
|
|
updateDate: Date; // Last update
|
|
state: number; // ChatState enum value
|
|
}
|
|
|
|
interface Message {
|
|
id: string; // Message UUID
|
|
date: Date; // Message timestamp
|
|
userid: string; // Sender user ID
|
|
text: string; // Message content
|
|
}
|
|
|
|
enum ChatType {
|
|
DIRECT = 'direct', // Direct message
|
|
GROUP = 'group', // Group chat
|
|
GAME = 'game' // Game-specific chat
|
|
}
|
|
|
|
enum ChatState {
|
|
ACTIVE = 0, // Active chat
|
|
ARCHIVE = 1, // Archived chat
|
|
SOFT_DELETE = 2 // Soft deleted
|
|
}
|
|
```
|
|
|
|
### Contact DTOs
|
|
```typescript
|
|
interface ContactDto {
|
|
id: string; // UUID
|
|
name: string; // Contact name
|
|
email: string; // Contact email
|
|
userid: string | null; // User ID if logged in
|
|
type: number; // ContactType enum
|
|
txt: string; // Message content
|
|
state: number; // ContactState enum
|
|
createDate: Date; // Creation date
|
|
updateDate: Date; // Last update
|
|
adminResponse: string | null; // Admin response
|
|
responseDate: Date | null; // Response date
|
|
respondedBy: string | null; // Responding admin ID
|
|
}
|
|
|
|
enum ContactType {
|
|
BUG = 0, // Bug report
|
|
PROBLEM = 1, // Problem report
|
|
QUESTION = 2, // General question
|
|
SALES = 3, // Sales inquiry
|
|
OTHER = 4 // Other type
|
|
}
|
|
|
|
enum ContactState {
|
|
ACTIVE = 0, // Active/unresolved
|
|
RESOLVED = 1, // Resolved
|
|
SOFT_DELETE = 2 // Soft deleted
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Base URL & Service Info
|
|
|
|
**Base URL:** `http://localhost:3000` (development)
|
|
|
|
### Service Information
|
|
**Endpoint:** `GET /`
|
|
|
|
**Authentication:** None required
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
service: "SerpentRace Backend API";
|
|
status: "running";
|
|
version: "1.0.0";
|
|
endpoints: {
|
|
swagger: "/api-docs";
|
|
users: "/api/users";
|
|
organizations: "/api/organizations";
|
|
decks: "/api/decks";
|
|
chats: "/api/chats";
|
|
contacts: "/api/contacts";
|
|
admin: "/api/admin";
|
|
deckImportExport: "/api/deck-import-export";
|
|
health: "/health";
|
|
};
|
|
websocket: {
|
|
enabled: true;
|
|
events: string[]; // WebSocket event names
|
|
};
|
|
}
|
|
```
|
|
|
|
### Health Check
|
|
**Endpoint:** `GET /health`
|
|
|
|
**Authentication:** None required
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
status: "healthy" | "unhealthy";
|
|
timestamp: string; // ISO timestamp
|
|
service: "SerpentRace Backend API";
|
|
version: "1.0.0";
|
|
environment: string; // "development" | "production"
|
|
database: {
|
|
connected: boolean; // Database connection status
|
|
type: string; // Database type
|
|
};
|
|
websocket: {
|
|
enabled: boolean; // WebSocket status
|
|
};
|
|
uptime: number; // Process uptime in seconds
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Authentication Endpoints
|
|
|
|
### User Login
|
|
**Endpoint:** `POST /api/users/login`
|
|
|
|
**Authentication:** None required
|
|
|
|
**Validation Rules:**
|
|
- `username`: 3-50 characters
|
|
- `password`: 6-100 characters
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
username: string; // Username or email
|
|
password: string; // Password
|
|
}
|
|
```
|
|
|
|
**Response Data (Success):**
|
|
```typescript
|
|
{
|
|
token: string; // JWT token (also set as cookie)
|
|
user: ShortUserDto; // User information
|
|
organizationName?: string; // Organization name (if member)
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
- `400`: Validation error
|
|
- `401`: Invalid credentials, unverified email, or account restrictions
|
|
- `500`: Internal server error
|
|
|
|
### User Registration
|
|
**Endpoint:** `POST /api/users/create`
|
|
|
|
**Authentication:** None required
|
|
|
|
**Validation Rules:**
|
|
- `username`: 3-50 characters, unique
|
|
- `email`: valid email format, unique
|
|
- `password`: 6-100 characters
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
username: string; // Unique username
|
|
email: string; // Valid email
|
|
password: string; // Password
|
|
fname?: string; // First name (optional)
|
|
lname?: string; // Last name (optional)
|
|
phone?: string; // Phone number (optional)
|
|
type?: string; // User type (optional)
|
|
}
|
|
```
|
|
|
|
**Response Data (Success):**
|
|
```typescript
|
|
{
|
|
id: string; // User UUID
|
|
username: string; // Username
|
|
email: string; // Email
|
|
regdate: Date; // Registration date
|
|
}
|
|
```
|
|
|
|
**Error Responses:**
|
|
- `400`: Validation error
|
|
- `409`: Username or email already exists
|
|
- `500`: Internal server error
|
|
|
|
---
|
|
|
|
## User Management
|
|
|
|
### Get User Profile
|
|
**Endpoint:** `GET /api/users/profile`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Response Data:** `DetailUserDto`
|
|
|
|
### Update User Profile
|
|
**Endpoint:** `PATCH /api/users/profile`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Request Data:** Partial `DetailUserDto` fields to update
|
|
|
|
**Response Data:** Updated `DetailUserDto`
|
|
|
|
**Error Responses:**
|
|
- `400`: Validation error
|
|
- `404`: User not found
|
|
- `409`: Email already exists
|
|
- `500`: Internal server error
|
|
|
|
---
|
|
|
|
## Deck Management
|
|
|
|
### Get Decks (Paginated) - RECOMMENDED
|
|
**Endpoint:** `GET /api/decks/page/{from}/{to}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `from`: Start index (0-based, ≥ 0)
|
|
- `to`: End index (inclusive, ≥ from)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
decks: ShortDeckDto[]; // Array of deck summaries
|
|
totalCount: number; // Total available decks
|
|
}
|
|
```
|
|
|
|
### Create Deck
|
|
**Endpoint:** `POST /api/decks`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
name: string; // Deck name (required)
|
|
type?: number; // DeckType enum (optional)
|
|
cards?: Card[]; // Initial cards (optional)
|
|
ctype?: number; // DeckVisibility enum (optional)
|
|
}
|
|
```
|
|
|
|
**Response Data:** `ShortDeckDto`
|
|
|
|
### Search Decks
|
|
**Endpoint:** `GET /api/decks/search`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Query Parameters:**
|
|
- `query`: Search query (required, non-empty)
|
|
- `limit`: Results limit (1-100, default: 20)
|
|
- `offset`: Results offset (≥ 0, default: 0)
|
|
|
|
**Response Data:** Array of matching `ShortDeckDto`
|
|
|
|
### Get Deck by ID
|
|
**Endpoint:** `GET /api/decks/{id}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `id`: Deck UUID
|
|
|
|
**Response Data:** `DetailDeckDto`
|
|
|
|
### Update Deck
|
|
**Endpoint:** `PUT /api/decks/{id}`
|
|
|
|
**Authentication:** Required (owner only)
|
|
|
|
**URL Parameters:**
|
|
- `id`: Deck UUID
|
|
|
|
**Request Data:** Partial `DetailDeckDto` fields to update
|
|
|
|
**Response Data:** Updated `ShortDeckDto`
|
|
|
|
### Delete Deck (Soft Delete)
|
|
**Endpoint:** `DELETE /api/decks/{id}`
|
|
|
|
**Authentication:** Required (owner only)
|
|
|
|
**URL Parameters:**
|
|
- `id`: Deck UUID
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean; // Deletion success status
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Organization Management
|
|
|
|
### Get Organizations (Paginated) - RECOMMENDED
|
|
**Endpoint:** `GET /api/organizations/page/{from}/{to}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `from`: Start index (0-based, ≥ 0)
|
|
- `to`: End index (inclusive, ≥ from)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
organizations: ShortOrganizationDto[];
|
|
totalCount: number;
|
|
}
|
|
```
|
|
|
|
### Search Organizations
|
|
**Endpoint:** `GET /api/organizations/search`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Query Parameters:**
|
|
- `query`: Search query (required, non-empty)
|
|
- `limit`: Results limit (1-100, default: 20)
|
|
- `offset`: Results offset (≥ 0, default: 0)
|
|
|
|
**Response Data:** Array of matching organizations
|
|
|
|
### Get Organization Login URL
|
|
**Endpoint:** `GET /api/organizations/{orgId}/login-url`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `orgId`: Organization UUID
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
loginUrl: string; // Organization login URL
|
|
organizationName: string; // Organization name
|
|
}
|
|
```
|
|
|
|
### Process Organization Auth Callback
|
|
**Endpoint:** `POST /api/organizations/auth-callback`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
organizationId: string; // Organization UUID
|
|
status: "ok" | "not_ok"; // Authentication status
|
|
authToken?: string; // Authentication token (optional)
|
|
}
|
|
```
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean; // Processing success
|
|
message: string; // Result message
|
|
updatedFields?: string[]; // Fields that were updated
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Chat System
|
|
|
|
### Get User Chats
|
|
**Endpoint:** `GET /api/chats/user-chats`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Query Parameters:**
|
|
- `includeArchived`: boolean (default: false)
|
|
|
|
**Response Data:** Array of `ShortChatDto`
|
|
|
|
### Get Chat History
|
|
**Endpoint:** `GET /api/chats/history/{chatId}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `chatId`: Chat UUID (validated)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
id: string; // Chat UUID
|
|
users: string[]; // Participants
|
|
messages: Message[]; // Message history
|
|
isArchived: boolean; // Archive status
|
|
state: number; // Chat state
|
|
}
|
|
```
|
|
|
|
### Create Chat
|
|
**Endpoint:** `POST /api/chats/create`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Validation Rules:**
|
|
- `type`: must be 'direct' or 'group'
|
|
- `userIds`: non-empty array
|
|
- `name`: required for groups
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
type: 'direct' | 'group'; // Chat type
|
|
userIds: string[]; // Participant user IDs
|
|
name?: string; // Group name (required for groups)
|
|
}
|
|
```
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
id: string; // Chat UUID
|
|
type: ChatType; // Chat type
|
|
name: string | null; // Chat name
|
|
users: string[]; // Participants
|
|
messages: Message[]; // Empty initially
|
|
}
|
|
```
|
|
|
|
### Send Message (REST - for testing)
|
|
**Endpoint:** `POST /api/chats/message`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Validation Rules:**
|
|
- `chatId`: valid UUID
|
|
- `message`: 1-2000 characters
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
chatId: string; // Chat UUID
|
|
message: string; // Message content
|
|
}
|
|
```
|
|
|
|
**Response Data:** `Message` object
|
|
|
|
### Archive Chat
|
|
**Endpoint:** `POST /api/chats/archive/{chatId}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `chatId`: Chat UUID (validated)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
}
|
|
```
|
|
|
|
### Restore Chat from Archive
|
|
**Endpoint:** `POST /api/chats/restore/{chatId}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `chatId`: Chat UUID (validated)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
}
|
|
```
|
|
|
|
### Get Archived Game Chats
|
|
**Endpoint:** `GET /api/chats/archived/game/{gameId}`
|
|
|
|
**Authentication:** Required
|
|
|
|
**URL Parameters:**
|
|
- `gameId`: Game UUID (validated)
|
|
|
|
**Response Data:** Array of archived chat objects
|
|
|
|
---
|
|
|
|
## Contact Management
|
|
|
|
### Create Contact
|
|
**Endpoint:** `POST /api/contact`
|
|
|
|
**Authentication:** Optional
|
|
|
|
**Validation Rules:**
|
|
- `name`, `email`, `type`, `txt`: required fields
|
|
- `type`: must be 0-4 (ContactType enum)
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
name: string; // Contact name (required)
|
|
email: string; // Contact email (required)
|
|
type: ContactType; // Contact type (0-4)
|
|
txt: string; // Message content (required)
|
|
}
|
|
```
|
|
|
|
**Response Data:** `ContactDto`
|
|
|
|
**Error Responses:**
|
|
- `400`: Missing fields or invalid contact type
|
|
- `500`: Internal server error
|
|
|
|
---
|
|
|
|
## Import/Export Functionality
|
|
|
|
### Export Deck
|
|
**Endpoint:** `GET /api/deck-import-export/export/{deckId}`
|
|
|
|
**Authentication:** Required (deck owner only)
|
|
|
|
**URL Parameters:**
|
|
- `deckId`: Deck UUID
|
|
|
|
**Response:** Binary .spr file download
|
|
|
|
**Headers:**
|
|
- `Content-Type`: `application/octet-stream`
|
|
- `Content-Disposition`: `attachment; filename="deckname.spr"`
|
|
|
|
### Import Deck
|
|
**Endpoint:** `POST /api/deck-import-export/import`
|
|
|
|
**Authentication:** Required
|
|
|
|
**Request:** Multipart form data
|
|
- `file`: .spr or JSON file (max 10MB)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
deckId: string; // Created deck ID
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Admin Endpoints
|
|
|
|
All admin endpoints require authentication with admin role (UserState.ADMIN = 5).
|
|
|
|
### User Management (Admin)
|
|
|
|
#### Get Users (Paginated) - RECOMMENDED
|
|
**Endpoint:** `GET /api/admin/users/page/{from}/{to}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**URL Parameters:**
|
|
- `from`: Start index (0-based)
|
|
- `to`: End index (inclusive, max page size: 100)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
users: DetailUserDto[]; // Array of detailed user objects
|
|
pagination: {
|
|
from: number; // Start index
|
|
to: number; // End index
|
|
returned: number; // Actual returned count
|
|
totalCount: number; // Total user count
|
|
includeDeleted: boolean; // Include deleted flag
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Get User by ID (Admin)
|
|
**Endpoint:** `GET /api/admin/users/{userId}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** `DetailUserDto` or null
|
|
|
|
#### Search Users (Admin)
|
|
**Endpoint:** `GET /api/admin/users/search/{searchTerm}`
|
|
|
|
**URL Parameters:**
|
|
- `searchTerm`: Search term (2-100 characters)
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Array of matching users
|
|
|
|
#### Update User (Admin)
|
|
**Endpoint:** `PATCH /api/admin/users/{userId}`
|
|
|
|
**Request Data:** Partial user fields to update
|
|
|
|
**Response Data:** Updated `DetailUserDto`
|
|
|
|
#### Deactivate User (Admin)
|
|
**Endpoint:** `POST /api/admin/users/{userId}/deactivate`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
message: string;
|
|
user: DetailUserDto;
|
|
}
|
|
```
|
|
|
|
#### Delete User (Admin)
|
|
**Endpoint:** `DELETE /api/admin/users/{userId}`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
message: string;
|
|
}
|
|
```
|
|
|
|
### Deck Management (Admin)
|
|
|
|
#### Get Decks (Paginated, Admin)
|
|
**Endpoint:** `GET /api/admin/decks/page/{from}/{to}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Same as regular deck pagination but unrestricted
|
|
|
|
#### Get Deck by ID (Admin)
|
|
**Endpoint:** `GET /api/admin/decks/{id}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** `DetailDeckDto`
|
|
|
|
#### Search Decks (Admin)
|
|
**Endpoint:** `GET /api/admin/decks/search/{searchTerm}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Array of matching decks
|
|
|
|
#### Hard Delete Deck (Admin)
|
|
**Endpoint:** `DELETE /api/admin/decks/{id}/hard`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
}
|
|
```
|
|
|
|
### Organization Management (Admin)
|
|
|
|
#### Create Organization (Admin)
|
|
**Endpoint:** `POST /api/admin/organizations`
|
|
|
|
**Request Data:** Organization creation data
|
|
|
|
**Response Data:** Created organization object
|
|
|
|
#### Update Organization (Admin)
|
|
**Endpoint:** `PATCH /api/admin/organizations/{id}`
|
|
|
|
**Request Data:** Partial organization fields to update
|
|
|
|
**Response Data:** Updated organization object
|
|
|
|
#### Get Organizations (Paginated, Admin)
|
|
**Endpoint:** `GET /api/admin/organizations/page/{from}/{to}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Organization pagination with unrestricted access
|
|
|
|
#### Get Organization by ID (Admin)
|
|
**Endpoint:** `GET /api/admin/organizations/{id}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Organization object
|
|
|
|
#### Search Organizations (Admin)
|
|
**Endpoint:** `GET /api/admin/organizations/search/{searchTerm}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Array of matching organizations
|
|
|
|
#### Soft Delete Organization (Admin)
|
|
**Endpoint:** `DELETE /api/admin/organizations/{id}`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
}
|
|
```
|
|
|
|
#### Hard Delete Organization (Admin)
|
|
**Endpoint:** `DELETE /api/admin/organizations/{id}/hard`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
}
|
|
```
|
|
|
|
### Chat Management (Admin)
|
|
|
|
#### Get Chats (Paginated, Admin)
|
|
**Endpoint:** `GET /api/admin/chats/page/{from}/{to}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
chats: DetailChatDto[];
|
|
pagination: {
|
|
from: number;
|
|
to: number;
|
|
returned: number;
|
|
totalCount: number;
|
|
includeDeleted: boolean;
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Get Chat by ID (Admin)
|
|
**Endpoint:** `GET /api/admin/chats/{id}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** `DetailChatDto`
|
|
|
|
### Contact Management (Admin)
|
|
|
|
#### Get Contacts (Paginated, Admin)
|
|
**Endpoint:** `GET /api/admin/contacts/page/{from}/{to}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Contact pagination with full access
|
|
|
|
#### Get Contact by ID (Admin)
|
|
**Endpoint:** `GET /api/admin/contacts/{id}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** `ContactDto`
|
|
|
|
#### Search Contacts (Admin)
|
|
**Endpoint:** `GET /api/admin/contacts/search/{searchTerm}`
|
|
|
|
**Query Parameters:**
|
|
- `includeDeleted`: boolean (default: false)
|
|
|
|
**Response Data:** Array of matching contacts
|
|
|
|
#### Respond to Contact (Admin)
|
|
**Endpoint:** `PUT /api/admin/contacts/{id}/respond`
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
adminResponse: string; // Admin response (required)
|
|
sendEmail?: boolean; // Send email to contact (optional)
|
|
language?: string; // Response language (optional)
|
|
}
|
|
```
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
contact: ContactDto;
|
|
emailSent: boolean;
|
|
emailError: string | null;
|
|
}
|
|
```
|
|
|
|
#### Resend Contact Email (Admin)
|
|
**Endpoint:** `POST /api/admin/contacts/{id}/resend-email`
|
|
|
|
**Request Data:**
|
|
```typescript
|
|
{
|
|
language?: string; // Email language (optional)
|
|
}
|
|
```
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
}
|
|
```
|
|
|
|
#### Soft Delete Contact (Admin)
|
|
**Endpoint:** `DELETE /api/admin/contacts/{id}`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
}
|
|
```
|
|
|
|
#### Hard Delete Contact (Admin)
|
|
**Endpoint:** `DELETE /api/admin/contacts/{id}/hard`
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
}
|
|
```
|
|
|
|
### Import/Export (Admin)
|
|
|
|
#### Import Deck from JSON (Admin)
|
|
**Endpoint:** `POST /api/admin/decks/import`
|
|
|
|
**Request:** Multipart form data
|
|
- `file`: JSON file (max 10MB)
|
|
|
|
**Response Data:**
|
|
```typescript
|
|
{
|
|
success: boolean;
|
|
message: string;
|
|
deckId: string;
|
|
}
|
|
```
|
|
|
|
#### Export Deck as JSON (Admin)
|
|
**Endpoint:** `GET /api/admin/decks/{deckId}/export`
|
|
|
|
**Response:** JSON file download
|
|
|
|
**Headers:**
|
|
- `Content-Type`: `application/json`
|
|
- `Content-Disposition`: `attachment; filename="deckname.json"`
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### Standard Error Response Format
|
|
```typescript
|
|
{
|
|
error: string; // Error message
|
|
details?: string; // Additional details (development only)
|
|
timestamp?: string; // Error timestamp
|
|
}
|
|
```
|
|
|
|
### HTTP Status Codes
|
|
- `200` - Success
|
|
- `201` - Created
|
|
- `204` - No Content
|
|
- `400` - Bad Request (validation error)
|
|
- `401` - Unauthorized (authentication required)
|
|
- `403` - Forbidden (insufficient permissions)
|
|
- `404` - Not Found
|
|
- `409` - Conflict (duplicate data)
|
|
- `500` - Internal Server Error
|
|
- `503` - Service Unavailable
|
|
|
|
### Common Error Scenarios
|
|
|
|
**Authentication Errors:**
|
|
```typescript
|
|
// Missing or invalid token
|
|
{ error: "Authentication required" }
|
|
|
|
// Account state restrictions
|
|
{ error: "Please verify your email address" }
|
|
{ error: "Account has been deactivated" }
|
|
|
|
// Admin access required
|
|
{ error: "Admin access required" }
|
|
```
|
|
|
|
**Validation Errors:**
|
|
```typescript
|
|
// Missing required fields
|
|
{ error: "Missing required fields: username, password" }
|
|
|
|
// Invalid field length
|
|
{ error: "Username must be between 3 and 50 characters" }
|
|
|
|
// Invalid parameters
|
|
{ error: "Invalid page parameters. \"from\" and \"to\" must be valid numbers with to >= from >= 0" }
|
|
|
|
// Invalid file type
|
|
{ error: "Only JSON and .spr files are allowed" }
|
|
```
|
|
|
|
**Business Logic Errors:**
|
|
```typescript
|
|
// Ownership restrictions
|
|
{ error: "Access denied - you can only export your own decks" }
|
|
|
|
// Feature restrictions
|
|
{ error: "Premium subscription required to create groups" }
|
|
|
|
// Duplicate data
|
|
{ error: "Deck with this name already exists" }
|
|
{ error: "Username or email already exists" }
|
|
```
|
|
|
|
---
|
|
|
|
## WebSocket Real-Time Communication
|
|
|
|
### Connection & Authentication
|
|
|
|
Connect to WebSocket server with JWT authentication:
|
|
|
|
```typescript
|
|
import io from 'socket.io-client';
|
|
|
|
// Option 1: JWT token in auth
|
|
const socket = io('http://localhost:3000', {
|
|
auth: {
|
|
token: 'your-jwt-token'
|
|
}
|
|
});
|
|
|
|
// Option 2: Cookie authentication
|
|
const socket = io('http://localhost:3000', {
|
|
withCredentials: true
|
|
});
|
|
```
|
|
|
|
### Connection Events
|
|
```typescript
|
|
// Connection successful
|
|
socket.on('connect', () => {
|
|
console.log('Connected to WebSocket server');
|
|
});
|
|
|
|
// Authentication failed
|
|
socket.on('connect_error', (error) => {
|
|
console.error('Connection failed:', error.message);
|
|
});
|
|
|
|
// Disconnected
|
|
socket.on('disconnect', (reason) => {
|
|
console.log('Disconnected:', reason);
|
|
});
|
|
|
|
// General errors
|
|
socket.on('error', (error: { message: string }) => {
|
|
console.error('WebSocket error:', error.message);
|
|
});
|
|
```
|
|
|
|
### Chat Management Events
|
|
|
|
**Initial Chat List:**
|
|
```typescript
|
|
// Automatically sent on connection
|
|
socket.on('chats:list', (chats: Array<{
|
|
id: string;
|
|
type: ChatType;
|
|
name: string | null;
|
|
users: string[];
|
|
lastActivity: Date | null;
|
|
isArchived: boolean;
|
|
}>) => {
|
|
// Update chat list in UI
|
|
});
|
|
```
|
|
|
|
**Join/Leave Chat:**
|
|
```typescript
|
|
// Join a chat room
|
|
socket.emit('chat:join', { chatId: 'chat-uuid' });
|
|
|
|
// Confirmation of joining
|
|
socket.on('chat:joined', (data: {
|
|
chatId: string;
|
|
messages: Message[];
|
|
users: string[];
|
|
}) => {
|
|
// Load chat messages
|
|
});
|
|
|
|
// Leave a chat room
|
|
socket.emit('chat:leave', { chatId: 'chat-uuid' });
|
|
|
|
// Confirmation of leaving
|
|
socket.on('chat:left', (data: { chatId: string }) => {
|
|
// Update UI
|
|
});
|
|
```
|
|
|
|
### Real-time Messaging
|
|
|
|
**Send/Receive Messages:**
|
|
```typescript
|
|
// Send message
|
|
socket.emit('message:send', {
|
|
chatId: 'chat-uuid',
|
|
message: 'Hello everyone!'
|
|
});
|
|
|
|
// Receive message
|
|
socket.on('message:received', (data: {
|
|
chatId: string;
|
|
message: Message;
|
|
senderInfo?: {
|
|
username: string;
|
|
fname: string;
|
|
lname: string;
|
|
};
|
|
}) => {
|
|
// Add message to chat UI
|
|
});
|
|
|
|
// Message sent confirmation
|
|
socket.on('message:sent', (data: {
|
|
chatId: string;
|
|
messageId: string;
|
|
timestamp: Date;
|
|
}) => {
|
|
// Update UI
|
|
});
|
|
```
|
|
|
|
**Rate Limiting:** 100 messages per user per minute
|
|
|
|
### Chat Creation Events
|
|
|
|
**Create Group Chat (Premium Only):**
|
|
```typescript
|
|
// Create group
|
|
socket.emit('group:create', {
|
|
name: 'Study Group',
|
|
userIds: ['user-uuid-1', 'user-uuid-2']
|
|
});
|
|
|
|
// Group created
|
|
socket.on('group:created', (data: {
|
|
chat: {
|
|
id: string;
|
|
type: 'group';
|
|
name: string;
|
|
users: string[];
|
|
createdBy: string;
|
|
};
|
|
}) => {
|
|
// Add to chat list
|
|
});
|
|
|
|
// Creation failed
|
|
socket.on('group:creation:failed', (data: {
|
|
error: string;
|
|
}) => {
|
|
// Show error
|
|
});
|
|
```
|
|
|
|
**Create Direct Chat:**
|
|
```typescript
|
|
// Create or get direct chat
|
|
socket.emit('chat:direct', {
|
|
targetUserId: 'user-uuid'
|
|
});
|
|
|
|
// Chat created
|
|
socket.on('chat:direct:created', (data: {
|
|
chat: {
|
|
id: string;
|
|
type: 'direct';
|
|
users: string[];
|
|
};
|
|
}) => {
|
|
// Add to list
|
|
});
|
|
|
|
// Chat already exists
|
|
socket.on('chat:direct:exists', (data: {
|
|
chatId: string;
|
|
}) => {
|
|
// Navigate to existing chat
|
|
});
|
|
```
|
|
|
|
**Create Game Chat:**
|
|
```typescript
|
|
// Create game chat
|
|
socket.emit('game:chat:create', {
|
|
gameId: 'game-uuid',
|
|
gameName: 'Quiz Game #123',
|
|
playerIds: ['player-uuid-1', 'player-uuid-2']
|
|
});
|
|
|
|
// Game chat created
|
|
socket.on('game:chat:created', (data: {
|
|
chat: {
|
|
id: string;
|
|
type: 'game';
|
|
name: string;
|
|
gameId: string;
|
|
users: string[];
|
|
};
|
|
}) => {
|
|
// Show game chat
|
|
});
|
|
```
|
|
|
|
### Chat History Management
|
|
|
|
**Get Chat History:**
|
|
```typescript
|
|
// Request full history
|
|
socket.emit('chat:history', { chatId: 'chat-uuid' });
|
|
|
|
// Receive active chat history
|
|
socket.on('chat:history', (data: {
|
|
chatId: string;
|
|
messages: Message[];
|
|
users: string[];
|
|
type: ChatType;
|
|
name: string | null;
|
|
}) => {
|
|
// Display full history
|
|
});
|
|
|
|
// Receive archived chat history
|
|
socket.on('chat:history:archived', (data: {
|
|
chatId: string;
|
|
messages: Message[];
|
|
isGameChat: boolean;
|
|
archiveDate: Date;
|
|
}) => {
|
|
// Display as read-only
|
|
});
|
|
```
|
|
|
|
### Message Retention Rules
|
|
- **All chats**: Messages older than 2 weeks are deleted
|
|
- **Direct & Game chats**: Max 10 messages per user (FIFO)
|
|
- **Group chats**: Time limit only (no per-user limit)
|
|
- **Archive**: Inactive chats (30 minutes) are archived
|
|
- **Cleanup**: Archived messages cleaned after 4 weeks
|
|
|
|
### Complete Implementation Example
|
|
|
|
```typescript
|
|
// hooks/useWebSocket.ts
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import io, { Socket } from 'socket.io-client';
|
|
|
|
export const useWebSocket = (token: string | null) => {
|
|
const socketRef = useRef<Socket | null>(null);
|
|
const [isConnected, setIsConnected] = useState(false);
|
|
const [chats, setChats] = useState<any[]>([]);
|
|
|
|
useEffect(() => {
|
|
if (!token) return;
|
|
|
|
socketRef.current = io('http://localhost:3000', {
|
|
auth: { token },
|
|
withCredentials: true
|
|
});
|
|
|
|
const socket = socketRef.current;
|
|
|
|
socket.on('connect', () => setIsConnected(true));
|
|
socket.on('connect_error', () => setIsConnected(false));
|
|
socket.on('disconnect', () => setIsConnected(false));
|
|
|
|
socket.on('chats:list', (chatList) => {
|
|
setChats(chatList);
|
|
});
|
|
|
|
socket.on('message:received', (data) => {
|
|
setChats(prev => prev.map(chat =>
|
|
chat.id === data.chatId
|
|
? { ...chat, messages: [...chat.messages, data.message] }
|
|
: chat
|
|
));
|
|
});
|
|
|
|
return () => {
|
|
socket.disconnect();
|
|
};
|
|
}, [token]);
|
|
|
|
const sendMessage = (chatId: string, message: string) => {
|
|
socketRef.current?.emit('message:send', { chatId, message });
|
|
};
|
|
|
|
const joinChat = (chatId: string) => {
|
|
socketRef.current?.emit('chat:join', { chatId });
|
|
};
|
|
|
|
const createDirectChat = (targetUserId: string) => {
|
|
socketRef.current?.emit('chat:direct', { targetUserId });
|
|
};
|
|
|
|
const createGroup = (name: string, userIds: string[]) => {
|
|
socketRef.current?.emit('group:create', { name, userIds });
|
|
};
|
|
|
|
return {
|
|
socket: socketRef.current,
|
|
isConnected,
|
|
chats,
|
|
sendMessage,
|
|
joinChat,
|
|
createDirectChat,
|
|
createGroup
|
|
};
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
This documentation provides a complete reference for all 50+ endpoints available in the SerpentRace backend API, with accurate data structures, validation rules, and implementation examples derived directly from the TypeScript source code.
|