837 lines
19 KiB
Markdown
837 lines
19 KiB
Markdown
# MemeMind Client-Server Integration Guide
|
|
|
|
## Overview
|
|
|
|
This document explains how the Cocos Creator client communicates with the MemeMind-Server backend and what extensions would be needed to support the full game flow including user authentication, progress tracking, and point/life management.
|
|
|
|
---
|
|
|
|
## Part 1: Current Integration (Read-Only)
|
|
|
|
### Current API Call: Get All Levels
|
|
|
|
**Client File**: `/Users/richard/Documents/code/cocosProject/mp-xieyingeng/assets/scripts/managers/LevelDataManager.ts`
|
|
|
|
**Current Implementation**:
|
|
```typescript
|
|
async initialize(): Promise<void> {
|
|
try {
|
|
// Initialize() is called by PageLoading during startup
|
|
const response = await HttpUtil.get<ApiResponse>(
|
|
'https://ilookai.cn/api/v1/wechat-game/levels'
|
|
);
|
|
|
|
if (response.success && response.data?.levels) {
|
|
this._apiData = response.data.levels;
|
|
this._levelDataCache.clear();
|
|
|
|
// Preload next level images asynchronously
|
|
this.preloadNextLevel(0);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load level data', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Server Endpoint**:
|
|
```
|
|
GET /api/v1/wechat-game/levels
|
|
Status: 200
|
|
Response: ApiResponseDto<LevelListResponseDto>
|
|
```
|
|
|
|
**Response Format**:
|
|
```typescript
|
|
{
|
|
success: true,
|
|
data: {
|
|
levels: [
|
|
{
|
|
level: 1, // Level number (1-indexed)
|
|
id: "level-001", // Unique ID
|
|
imageUrl: "https://...", // Level image URL
|
|
answer: "meme", // Correct answer
|
|
hint1: "image", // First hint (free)
|
|
hint2: "funny", // Second hint (costs 1 life)
|
|
hint3: null, // Third hint (costs 1 life)
|
|
sortOrder: 0, // Display order
|
|
createdAt: "2026-04-01T...",
|
|
updatedAt: "2026-04-05T..."
|
|
},
|
|
...
|
|
],
|
|
total: 50
|
|
},
|
|
message: null,
|
|
timestamp: "2026-04-05T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
### Data Flow
|
|
|
|
```
|
|
Client Startup
|
|
│
|
|
▼
|
|
PageLoading.ts: _startPreload()
|
|
│
|
|
├─ LevelDataManager.initialize()
|
|
│ │
|
|
│ └─> HttpUtil.get('/api/v1/wechat-game/levels')
|
|
│ │
|
|
│ ▼
|
|
│ MemeMind-Server
|
|
│ │
|
|
│ ├─> WechatGameController.getAllLevels()
|
|
│ ├─> WechatGameService.getAllLevels()
|
|
│ ├─> LevelRepository.findAllOrdered()
|
|
│ └─> MySQL: SELECT * FROM levels ORDER BY sort_order
|
|
│
|
|
│ Response returned
|
|
│ │
|
|
│ ▼
|
|
│ LevelDataManager._apiData = levels
|
|
│ Preload images
|
|
│
|
|
├─ Progress: 80% -> 100%
|
|
│
|
|
▼
|
|
PageHome displayed
|
|
│
|
|
▼
|
|
User clicks "Start Game"
|
|
│
|
|
▼
|
|
PageLevel loaded
|
|
│
|
|
├─> Reads from _apiData
|
|
├─> Displays level image, hints, input
|
|
└─> Ready for gameplay
|
|
```
|
|
|
|
---
|
|
|
|
## Part 2: Missing Features for Full Integration
|
|
|
|
### 1. User Authentication
|
|
|
|
**What's needed**:
|
|
- WeChat OpenID extraction via `wx.login()`
|
|
- Backend user registration/login endpoint
|
|
- JWT token generation and validation
|
|
- User context in requests
|
|
|
|
**Implementation Plan**:
|
|
|
|
**Server Addition**:
|
|
```typescript
|
|
// src/modules/users/users.module.ts
|
|
@Module({
|
|
controllers: [UsersController],
|
|
providers: [UsersService, UsersRepository],
|
|
imports: [TypeOrmModule.forFeature([User])],
|
|
})
|
|
export class UsersModule {}
|
|
|
|
// src/modules/users/entities/user.entity.ts
|
|
@Entity('users')
|
|
export class User {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@Column({ unique: true })
|
|
wxOpenId: string; // WeChat OpenID
|
|
|
|
@Column()
|
|
nickname: string;
|
|
|
|
@Column({ default: 10 })
|
|
currentLives: number;
|
|
|
|
@Column({ default: 0 })
|
|
currentLevelIndex: number;
|
|
|
|
@Column({ default: 0 })
|
|
totalPoints: number;
|
|
|
|
@CreateDateColumn()
|
|
createdAt: Date;
|
|
|
|
@UpdateDateColumn()
|
|
updatedAt: Date;
|
|
}
|
|
|
|
// Endpoint: POST /api/v1/users/login
|
|
@Post('login')
|
|
async login(@Body() dto: LoginDto): Promise<ApiResponseDto<LoginResponseDto>> {
|
|
// dto.code from wx.login()
|
|
// Exchange code for wxOpenId via WeChat API
|
|
// Create or fetch user
|
|
// Generate JWT token
|
|
// Return user + token
|
|
}
|
|
```
|
|
|
|
**Client Addition**:
|
|
```typescript
|
|
// In PageLoading.ts or main.ts
|
|
async function initializeUser() {
|
|
try {
|
|
const code = await wx.login();
|
|
const response = await HttpUtil.post('/api/v1/users/login', {
|
|
code: code.code
|
|
});
|
|
|
|
if (response.success) {
|
|
const { user, token } = response.data;
|
|
StorageManager.setToken(token); // New: Store JWT
|
|
StorageManager.setUserId(user.id); // New: Store user ID
|
|
return user;
|
|
}
|
|
} catch (error) {
|
|
console.error('Login failed', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Level Submission & Answer Validation
|
|
|
|
**What's needed**:
|
|
- POST endpoint for level submissions
|
|
- Answer validation (case-insensitive, trim whitespace)
|
|
- Update user progress
|
|
- Award points/lives
|
|
- Handle wrong answers
|
|
|
|
**Implementation Plan**:
|
|
|
|
**Server Addition**:
|
|
```typescript
|
|
// src/modules/levels/level-submission.module.ts
|
|
@Module({
|
|
controllers: [LevelSubmissionController],
|
|
providers: [LevelSubmissionService],
|
|
imports: [
|
|
TypeOrmModule.forFeature([UserProgress, Level]),
|
|
UsersModule,
|
|
WechatGameModule,
|
|
],
|
|
})
|
|
export class LevelSubmissionModule {}
|
|
|
|
// Endpoint: POST /api/v1/levels/:levelId/submit
|
|
@Post(':levelId/submit')
|
|
@UseGuards(JwtAuthGuard) // Require authentication
|
|
async submitAnswer(
|
|
@Param('levelId') levelId: string,
|
|
@Body() dto: SubmitAnswerDto,
|
|
@Req() request: any,
|
|
): Promise<ApiResponseDto<SubmitAnswerResponseDto>> {
|
|
// request.user.id from JWT
|
|
// Validate answer (case-insensitive, trim)
|
|
// If correct:
|
|
// - Award 1 life
|
|
// - Update currentLevelIndex
|
|
// - Record submission
|
|
// If wrong:
|
|
// - Record wrong attempt
|
|
// - Maybe deduct lives?
|
|
// Return result
|
|
}
|
|
|
|
// src/modules/levels/dto/submit-answer.dto.ts
|
|
export class SubmitAnswerDto {
|
|
@IsString()
|
|
@IsNotEmpty()
|
|
answer: string;
|
|
|
|
@IsNumber()
|
|
timeTaken: number; // Seconds to solve
|
|
|
|
@IsNumber()
|
|
hintsUsed: number; // How many hints revealed
|
|
}
|
|
|
|
export class SubmitAnswerResponseDto {
|
|
success: boolean; // Correct answer?
|
|
message: string; // "Correct!" or "Wrong!"
|
|
newLives: number; // Updated lives
|
|
newLevel: number; // Next level index
|
|
pointsEarned: number; // Points for this solve
|
|
totalPoints: number; // Total accumulated points
|
|
}
|
|
```
|
|
|
|
**Client Change**:
|
|
```typescript
|
|
// In PageLevel.ts: showSuccess() method
|
|
private async showSuccess(): void {
|
|
this.stopCountdown();
|
|
this.playSuccessSound();
|
|
|
|
// NEW: Submit to server
|
|
try {
|
|
const token = StorageManager.getToken();
|
|
const response = await HttpUtil.post(
|
|
`/api/v1/levels/${this._currentLevel.id}/submit`,
|
|
{
|
|
answer: this._userAnswer,
|
|
timeTaken: this._elapsedTime,
|
|
hintsUsed: this._hintsUsed
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${token}`
|
|
}
|
|
}
|
|
);
|
|
|
|
if (response.success) {
|
|
// Update local storage with new progress
|
|
StorageManager.setLives(response.data.newLives);
|
|
StorageManager.onLevelCompleted(response.data.newLevel - 1);
|
|
|
|
// Show points earned
|
|
this.showPointsNotification(response.data.pointsEarned);
|
|
}
|
|
} catch (error) {
|
|
console.error('Submission failed', error);
|
|
}
|
|
|
|
this._showPassModal();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. User Progress Tracking
|
|
|
|
**What's needed**:
|
|
- Endpoint to get user progress
|
|
- Endpoint to update progress
|
|
- Sync between client localStorage and server
|
|
- Handle offline mode
|
|
|
|
**Implementation Plan**:
|
|
|
|
**Server Addition**:
|
|
```typescript
|
|
// Endpoint: GET /api/v1/users/me/progress
|
|
@Get('me/progress')
|
|
@UseGuards(JwtAuthGuard)
|
|
async getProgress(
|
|
@Req() request: any,
|
|
): Promise<ApiResponseDto<UserProgressDto>> {
|
|
// request.user.id from JWT
|
|
// Return user progress
|
|
}
|
|
|
|
export class UserProgressDto {
|
|
id: string;
|
|
userId: string;
|
|
currentLevelIndex: number;
|
|
maxLevelUnlocked: number;
|
|
totalPoints: number;
|
|
currentLives: number;
|
|
completedLevels: {
|
|
levelId: string;
|
|
completedAt: Date;
|
|
timeTaken: number;
|
|
hintsUsed: number;
|
|
}[];
|
|
}
|
|
|
|
// Endpoint: POST /api/v1/users/me/progress/sync
|
|
@Post('me/progress/sync')
|
|
@UseGuards(JwtAuthGuard)
|
|
async syncProgress(
|
|
@Req() request: any,
|
|
@Body() dto: SyncProgressDto,
|
|
): Promise<ApiResponseDto<SyncProgressResponseDto>> {
|
|
// Merge client progress with server
|
|
// Handle conflicts (prefer latest)
|
|
// Return merged progress
|
|
}
|
|
```
|
|
|
|
**Client Update**:
|
|
```typescript
|
|
// In StorageManager.ts: Add sync methods
|
|
static async syncWithServer(): Promise<void> {
|
|
try {
|
|
const token = this.getToken();
|
|
const localProgress = this.getCurrentLevelIndex();
|
|
const localLives = this.getLives();
|
|
|
|
const response = await HttpUtil.post(
|
|
'/api/v1/users/me/progress/sync',
|
|
{
|
|
currentLevelIndex: localProgress,
|
|
currentLives: localLives,
|
|
},
|
|
{
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
}
|
|
);
|
|
|
|
if (response.success) {
|
|
// Update local with server's merged data
|
|
this.setLives(response.data.currentLives);
|
|
// Update progress for each completed level
|
|
response.data.completedLevels.forEach((level) => {
|
|
this.onLevelCompleted(level.levelIndex);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Sync failed', error);
|
|
// Continue with local data
|
|
}
|
|
}
|
|
|
|
// Call on app startup and periodically
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Hint Usage & Cost Management
|
|
|
|
**What's needed**:
|
|
- Track which hints have been used
|
|
- Deduct lives when using premium hints
|
|
- Prevent excessive hint usage
|
|
|
|
**Implementation Plan**:
|
|
|
|
**Server Addition**:
|
|
```typescript
|
|
// Track hint usage per level per user
|
|
@Entity('level_hints')
|
|
export class LevelHint {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id: string;
|
|
|
|
@ManyToOne(() => User)
|
|
user: User;
|
|
|
|
@Column()
|
|
levelId: string;
|
|
|
|
@Column()
|
|
hint1Revealed: boolean; // Always free
|
|
|
|
@Column()
|
|
hint2Revealed: boolean; // Costs 1 life
|
|
|
|
@Column()
|
|
hint3Revealed: boolean; // Costs 1 life
|
|
|
|
@CreateDateColumn()
|
|
createdAt: Date;
|
|
}
|
|
|
|
// Endpoint: POST /api/v1/levels/:levelId/reveal-hint
|
|
@Post(':levelId/reveal-hint')
|
|
@UseGuards(JwtAuthGuard)
|
|
async revealHint(
|
|
@Param('levelId') levelId: string,
|
|
@Body() dto: RevealHintDto, // { hintNumber: 2 }
|
|
@Req() request: any,
|
|
): Promise<ApiResponseDto<RevealHintResponseDto>> {
|
|
// Validate hint number
|
|
// Check if already revealed
|
|
// If hint 2 or 3: deduct 1 life
|
|
// Update hint tracking
|
|
// Return hint text
|
|
}
|
|
```
|
|
|
|
**Client Change**:
|
|
```typescript
|
|
// In PageLevel.ts: onUnlockClue() method
|
|
private async onUnlockClue(clueIndex: number): void {
|
|
const token = StorageManager.getToken();
|
|
|
|
// Hint index 1 is always free (hint1)
|
|
// Hints 2 and 3 cost 1 life each
|
|
if (clueIndex > 0 && !this._freeCluePassed) {
|
|
const currentLives = StorageManager.getLives();
|
|
if (currentLives <= 0) {
|
|
this._showToast('没有生命了!(No lives left!)');
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
const response = await HttpUtil.post(
|
|
`/api/v1/levels/${this._currentLevel.id}/reveal-hint`,
|
|
{ hintNumber: clueIndex + 1 },
|
|
{ headers: { Authorization: `Bearer ${token}` } }
|
|
);
|
|
|
|
if (response.success) {
|
|
const hint = response.data.hintText;
|
|
this._showHint(hint);
|
|
|
|
// Deduct life if premium hint
|
|
if (clueIndex > 0) {
|
|
StorageManager.consumeLife();
|
|
StorageManager.setLives(response.data.remainingLives);
|
|
this._updateLivesDisplay();
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to unlock hint', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Leaderboard & Statistics
|
|
|
|
**What's needed**:
|
|
- Track total points per user
|
|
- Track completion time
|
|
- Leaderboard endpoint
|
|
- User statistics endpoint
|
|
|
|
**Implementation Plan**:
|
|
|
|
**Server Addition**:
|
|
```typescript
|
|
// Endpoint: GET /api/v1/leaderboard?limit=100
|
|
@Get('leaderboard')
|
|
async getLeaderboard(
|
|
@Query('limit') limit: number = 100,
|
|
): Promise<ApiResponseDto<LeaderboardDto>> {
|
|
// Select top users by totalPoints
|
|
// Include rank, nickname, points, completedLevels
|
|
}
|
|
|
|
// Endpoint: GET /api/v1/users/me/statistics
|
|
@Get('me/statistics')
|
|
@UseGuards(JwtAuthGuard)
|
|
async getStatistics(
|
|
@Req() request: any,
|
|
): Promise<ApiResponseDto<StatisticsDto>> {
|
|
// Return user statistics
|
|
// Total levels completed
|
|
// Average time per level
|
|
// Total hints used
|
|
// Current streak, etc.
|
|
}
|
|
|
|
export class StatisticsDto {
|
|
totalLevelsCompleted: number;
|
|
currentLevelIndex: number;
|
|
totalPoints: number;
|
|
currentLives: number;
|
|
averageTimePerLevel: number; // Seconds
|
|
totalTimeSpent: number; // Seconds
|
|
totalHintsUsed: number;
|
|
perfectSolves: number; // Solved in first try
|
|
longestStreak: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Part 3: API Authentication Pattern
|
|
|
|
### JWT Guard Implementation
|
|
|
|
**Server**:
|
|
```typescript
|
|
// src/modules/auth/guards/jwt.guard.ts
|
|
@Injectable()
|
|
export class JwtAuthGuard implements CanActivate {
|
|
constructor(private readonly configService: ConfigService) {}
|
|
|
|
canActivate(context: ExecutionContext): boolean {
|
|
const request = context.switchToHttp().getRequest();
|
|
const token = this.extractTokenFromHeader(request);
|
|
|
|
if (!token) {
|
|
throw new UnauthorizedException('No token provided');
|
|
}
|
|
|
|
try {
|
|
const secret = this.configService.get('JWT_SECRET');
|
|
const decoded = verify(token, secret);
|
|
request.user = decoded; // Attach to request
|
|
return true;
|
|
} catch (error) {
|
|
throw new UnauthorizedException('Invalid token');
|
|
}
|
|
}
|
|
|
|
private extractTokenFromHeader(request): string | undefined {
|
|
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
|
return type === 'Bearer' ? token : undefined;
|
|
}
|
|
}
|
|
|
|
// Usage on endpoints
|
|
@UseGuards(JwtAuthGuard)
|
|
@Get('me/progress')
|
|
async getProgress(@Req() request: any) {
|
|
const userId = request.user.id; // From JWT payload
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**Client**:
|
|
```typescript
|
|
// In HttpUtil.ts: Add auth header
|
|
static async post<T>(
|
|
url: string,
|
|
data: any,
|
|
options?: RequestOptions
|
|
): Promise<ApiResponse<T>> {
|
|
const token = StorageManager.getToken();
|
|
|
|
const headers = {
|
|
...options?.headers,
|
|
'Content-Type': 'application/json',
|
|
};
|
|
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
// Make request with headers
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Part 4: Data Sync Strategy
|
|
|
|
### Scenario 1: Online Mode
|
|
|
|
```
|
|
User completes level
|
|
│
|
|
▼
|
|
[Client] Submits answer
|
|
│
|
|
├─ HTTP POST /api/v1/levels/:id/submit
|
|
│
|
|
▼
|
|
[Server] Validates and updates
|
|
│
|
|
├─ Check answer
|
|
├─ Award life/points
|
|
├─ Update progress
|
|
├─ Store submission
|
|
│
|
|
▼
|
|
[Response] Returned to client
|
|
│
|
|
├─ Update localStorage
|
|
├─ Show success modal
|
|
├─ Move to next level
|
|
│
|
|
▼
|
|
Progress synced
|
|
```
|
|
|
|
### Scenario 2: Offline Mode
|
|
|
|
```
|
|
User completes level (no connection)
|
|
│
|
|
├─ Submit fails (no network)
|
|
│
|
|
▼
|
|
[Client] Stores locally
|
|
│
|
|
├─ StorageManager.recordOfflineSubmission()
|
|
├─ Update lives/progress locally
|
|
├─ Show success modal (assume correct)
|
|
│
|
|
▼
|
|
When connection returns
|
|
│
|
|
├─ StorageManager.syncPendingSubmissions()
|
|
│
|
|
▼
|
|
[Server] Receives batch of submissions
|
|
│
|
|
├─ Validate all answers
|
|
├─ Apply corrections if needed
|
|
├─ Return merged state
|
|
│
|
|
▼
|
|
[Client] Reconciles state
|
|
│
|
|
├─ If conflicts: server wins
|
|
├─ Update localStorage
|
|
├─ Show notification of changes
|
|
│
|
|
▼
|
|
Progress synced
|
|
```
|
|
|
|
---
|
|
|
|
## Part 5: Implementation Roadmap
|
|
|
|
### Phase 1: Basic Auth (Week 1)
|
|
- [ ] User entity & table
|
|
- [ ] Login endpoint with wx.login() code exchange
|
|
- [ ] JWT token generation
|
|
- [ ] JwtAuthGuard for protected routes
|
|
- [ ] Client login integration
|
|
- [ ] Token storage in StorageManager
|
|
|
|
### Phase 2: Progress Tracking (Week 2)
|
|
- [ ] UserProgress entity & table
|
|
- [ ] GET /api/v1/users/me/progress
|
|
- [ ] POST /api/v1/levels/:id/submit
|
|
- [ ] Update client PageLevel.ts to submit answers
|
|
- [ ] Sync endpoint for merging progress
|
|
- [ ] Offline submission queue
|
|
|
|
### Phase 3: Hint System Integration (Week 3)
|
|
- [ ] LevelHint entity & tracking
|
|
- [ ] POST /api/v1/levels/:id/reveal-hint
|
|
- [ ] Life deduction logic
|
|
- [ ] Client hint unlock cost
|
|
|
|
### Phase 4: Leaderboard & Stats (Week 4)
|
|
- [ ] Statistics calculation
|
|
- [ ] Leaderboard endpoint
|
|
- [ ] Client leaderboard page
|
|
- [ ] Personal statistics page
|
|
|
|
### Phase 5: Polish & Optimization (Week 5)
|
|
- [ ] Caching layer (@nestjs/cache-manager)
|
|
- [ ] Rate limiting (@nestjs/throttler)
|
|
- [ ] Request logging middleware
|
|
- [ ] Performance monitoring
|
|
- [ ] Database indexing optimization
|
|
|
|
---
|
|
|
|
## Part 6: Environment Configuration
|
|
|
|
### Server .env.production
|
|
|
|
```bash
|
|
NODE_ENV=production
|
|
PORT=3000
|
|
|
|
# Database
|
|
DB_HOST=production-db-host
|
|
DB_PORT=3306
|
|
DB_USERNAME=prod_user
|
|
DB_PASSWORD=secure_password
|
|
DB_DATABASE=meme_mind_prod
|
|
|
|
# Authentication
|
|
JWT_SECRET=very-secure-secret-key-32-chars-min
|
|
JWT_EXPIRATION=7d
|
|
|
|
# WeChat
|
|
WECHAT_APPID=your_wechat_appid
|
|
WECHAT_SECRET=your_wechat_secret
|
|
|
|
# API
|
|
API_BASE_URL=https://ilookai.cn/api
|
|
CORS_ORIGIN=https://yourdomain.com
|
|
```
|
|
|
|
### Client Storage Keys
|
|
|
|
```typescript
|
|
// New keys needed:
|
|
- 'auth_token' → JWT token
|
|
- 'user_id' → Current user ID
|
|
- 'offline_submissions' → Queue of submissions to send
|
|
- 'last_sync' → Timestamp of last sync
|
|
```
|
|
|
|
---
|
|
|
|
## Part 7: Error Handling
|
|
|
|
### Common API Errors
|
|
|
|
```typescript
|
|
// 401 Unauthorized
|
|
{
|
|
success: false,
|
|
data: null,
|
|
message: 'Invalid token',
|
|
path: '/api/v1/users/me/progress'
|
|
}
|
|
|
|
// 404 Not Found
|
|
{
|
|
success: false,
|
|
data: null,
|
|
message: 'Level with id "xyz" not found',
|
|
path: '/api/v1/levels/xyz'
|
|
}
|
|
|
|
// 400 Bad Request (validation)
|
|
{
|
|
success: false,
|
|
data: null,
|
|
message: 'Validation failed: answer must be a string',
|
|
path: '/api/v1/levels/123/submit'
|
|
}
|
|
|
|
// 500 Server Error
|
|
{
|
|
success: false,
|
|
data: null,
|
|
message: 'Internal server error',
|
|
path: '/api/v1/levels'
|
|
}
|
|
```
|
|
|
|
### Client Handling
|
|
|
|
```typescript
|
|
async function handleApiError(error: unknown) {
|
|
if (axios.isAxiosError(error)) {
|
|
const status = error.response?.status;
|
|
const message = error.response?.data?.message;
|
|
|
|
if (status === 401) {
|
|
// Token expired - redirect to login
|
|
StorageManager.clearToken();
|
|
navigateTo('PageHome');
|
|
} else if (status === 404) {
|
|
showToast(`Not found: ${message}`);
|
|
} else if (status === 400) {
|
|
showToast(`Invalid input: ${message}`);
|
|
} else {
|
|
showToast(`Error: ${message || 'Unknown error'}`);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
The current integration only handles reading level data. To support the full game loop with:
|
|
- ✅ User authentication (JWT)
|
|
- ✅ Answer submission & validation
|
|
- ✅ Progress tracking & sync
|
|
- ✅ Hint system with cost management
|
|
- ✅ Leaderboard & statistics
|
|
|
|
You need to implement the endpoints, models, and client logic described in this guide. The roadmap suggests a 5-week implementation with phases for each feature.
|
|
|
|
---
|
|
|
|
*Generated: 2026-04-05 | For MemeMind Project*
|