Files
MemeMind-Server/CLAUDE.md
2026-04-19 13:27:10 +08:00

11 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

MemeMind-Server is a NestJS backend for a WeChat mini-game called MemeMind. The server handles user authentication via WeChat login, manages game levels and progress, supports social sharing/challenges, and maintains user profiles. All API responses use a standardized format and are exposed under /api with Swagger docs at /api/docs.

Tech Stack

  • Framework: NestJS 11 with TypeScript 5.7
  • Database: MySQL (via TypeORM)
  • Authentication: JWT (7-day expiration)
  • Package Manager: pnpm (with pnpm-lock.yaml)
  • Testing: Jest (unit tests alongside source, e2e tests in test/)
  • Code Quality: ESLint + Prettier (single quotes, trailing commas)
  • Deployment: PM2 with rsync to remote server

Project Structure

src/
├── main.ts                      # App bootstrap (CORS, validation, Swagger)
├── app.module.ts                # Root module importing all features
├── config/
│   ├── config.module.ts         # Global config provider (env validation)
│   ├── env.validation.ts        # Environment variable schema validation
│   └── database.config.ts        # TypeORM configuration
├── database/
│   └── migrations/              # TypeORM migrations (not heavily used yet)
├── common/
│   ├── dto/api-response.dto.ts  # Unified response wrapper (success/error)
│   ├── filters/http-exception.filter.ts  # Global error handling
│   ├── guards/jwt-auth.guard.ts # JWT verification & payload extraction
│   └── decorators/current-user.decorator.ts  # Param decorator for @CurrentUser()
└── modules/
    ├── auth/                    # WeChat login, user creation, JWT issuance
    ├── user/                    # User profile and stamina management
    ├── level/                   # Game level progression tracking
    ├── share/                   # Social challenge/share features
    ├── game-config/             # Game configuration endpoints
    └── wechat-game/             # Shared game entities and repositories

docs/
├── api/                         # API documentation (Markdown, auto-synced with code)
├── api/README.md                # Index of all API modules
└── superpowers/                 # Legacy or undocumented features

Key Commands

Development

pnpm install                # Install dependencies
pnpm run start:dev          # Run with file watching (http://localhost:3000/api)
pnpm run start:debug        # Debug mode with Node inspector
pnpm run build              # Compile TypeScript to dist/
pnpm run start:prod         # Run compiled production build

Code Quality

pnpm run lint               # ESLint with auto-fix
pnpm run format             # Prettier format (src/ and test/)

Testing

pnpm run test               # All unit tests (*.spec.ts in src/)
pnpm run test:watch         # Watch mode
pnpm run test:cov           # Coverage report (outputs to coverage/)
pnpm run test:debug         # Debug unit tests
pnpm run test:e2e           # E2E tests (test/*.e2e-spec.ts)

Deployment

pnpm run deploy             # Build, rsync to server, restart PM2 cluster

Architecture Patterns

Module Structure

Each feature module follows NestJS conventions:

// module.ts: Declares imports (other modules, TypeORM entities, services)
@Module({
  imports: [TypeOrmModule.forFeature([Entity]), AuthModule],
  controllers: [FeatureController],
  providers: [FeatureService, CustomRepository],
  exports: [FeatureService],  // For cross-module injection
})
export class FeatureModule {}

// controller.ts: HTTP routing, DTO validation, Swagger decorators
@Controller('v1/feature')
export class FeatureController {
  @Post() async create(@Body() dto: CreateDto): Promise<ApiResponseDto<ResponseDto>>
  @UseGuards(JwtAuthGuard) // Applied per-endpoint
  @Get(':id') async getOne(@Param('id') id: string, @CurrentUser() user: JwtPayload)
}

// service.ts: Business logic, repository calls, external API calls
@Injectable()
export class FeatureService {
  constructor(private readonly repo: CustomRepository) {}
  async create(data: CreateDto): Promise<ResponseDto> { ... }
}

// repositories/: Custom data access logic (extend TypeORM repositories)
@Injectable()
export class CustomRepository extends Repository<Entity> {
  async customQuery(): Promise<Entity[]> { ... }
}

// dto/: Request/response schemas with class-validator decorators
export class CreateDto {
  @IsString() @MinLength(1) name: string;
}

Authentication & Authorization

  • No global guard: JWT validation is per-endpoint via @UseGuards(JwtAuthGuard)
  • User extraction: Use @CurrentUser() to inject JwtPayload { sub: userId, openid }
  • WeChat integration: AuthService.wxLogin(code) calls WeChat API, creates user on first login, issues JWT

Request/Response Contract

All endpoints return ApiResponseDto<T>:

{
  success: boolean,           // true on success, false on error
  data: T | null,             // Response payload or null
  message: string | null,     // Error message (null on success)
  timestamp: Date             // ISO 8601 timestamp
}

Errors are caught by HttpExceptionFilter, which formats exceptions as failed responses.

Database

  • ORM: TypeORM with MySQL
  • Entities: Auto-loaded from **/*.entity.ts, sync mode in dev
  • Migrations: Located in src/database/migrations/ (minimal usage)
  • Connection: Configured via ConfigService (env vars: DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE)

Configuration

Environment variables are validated via env.validation.ts using class-validator:

NODE_ENV        # development|production|test (default: development)
PORT            # Server port (default: 3000)
DB_HOST         # MySQL host (default: localhost)
DB_PORT         # MySQL port (default: 3306)
DB_USERNAME     # MySQL user (default: meme_user)
DB_PASSWORD     # MySQL password (default: '')
DB_DATABASE     # MySQL database (default: meme_mind)
WX_APPID        # WeChat mini-game app ID
WX_SECRET       # WeChat mini-game secret
JWT_SECRET      # JWT signing secret

Config files loaded in order: .env.local.env.env.production. Use .env.local for local overrides (never committed).

Testing

  • Unit tests: Colocated with source as *.spec.ts (e.g., auth.service.spec.ts)
  • E2E tests: In test/ directory (e.g., test/app.e2e-spec.ts)
  • Framework: Jest with ts-jest compiler
  • Coverage: Run pnpm run test:cov to generate coverage report in coverage/
  • Individual tests: pnpm run test -- --testNamePattern="test name" or pnpm run test:debug

API Documentation

API docs are maintained in docs/api/ and must be updated whenever controller/DTO changes occur. The api-doc-maintainer skill automates this when editing src/modules/*/.

File mapping:

  • docs/api/auth-api.mdsrc/modules/auth/
  • docs/api/game-api.mdsrc/modules/wechat-game/ & src/modules/level/
  • docs/api/share-challenge-api.mdsrc/modules/share/

When to update docs:

  • New endpoint added
  • Request/response DTO fields changed
  • Error codes or business logic modified
  • Authentication requirements changed

Use the template in AGENTS.md (api-doc-maintainer skill) for consistent formatting.

Deployment

Local to production:

  1. Ensure .env.production has correct credentials (WX_APPID, WX_SECRET, JWT_SECRET, DB credentials)
  2. Run pnpm run deploy (triggers deploy.sh)
  3. Script: builds locally → rsyncs dist/, package.json, pnpm-lock.yaml to /var/www/MemeMind-Server/ on server → installs deps → restarts PM2 cluster (2 instances)

PM2 config (ecosystem.config.js): Cluster mode (2 instances), auto-restart, 1GB memory limit, logs to logs/*.log

Important: Before deploying, verify deploy.sh credentials (SERVER_IP, SERVER_USER, REMOTE_DIR, APP_NAME) match your target environment.

Code Style & Conventions

  • TypeScript: Strict mode enabled, no implicit any, explicit types on public APIs
  • Naming: Classes PascalCase, methods/variables camelCase, directories kebab-case
  • DTO files: Semantic names (e.g., wx-login.dto.ts, share-response.dto.ts)
  • Quotes: Single quotes (enforced by Prettier)
  • Indentation: 2 spaces
  • Commits: Use Conventional Commits (e.g., feat(auth):, fix(level):, docs(api):)

Key Integration Points

Auth Module

  • Controller: POST /api/v1/auth/wx-login
  • Flow: WeChat code → AuthService.wxLogin() → calls WeChat API (jscode2session) → creates/updates user → signs JWT
  • Exports: AuthService, UserRepository, UserLevelProgressRepository (used by other modules)

User Module

  • Depends on: AuthModule (for JWT guard, UserRepository)
  • Usage: Profile endpoints, stamina management

Level Module

  • Depends on: AuthModule, UserModule, WechatGameModule
  • Entities: Reused from WechatGameModule (Level, UserLevelProgress)

Share Module

  • Entities: ShareConfig, ShareParticipant, ShareLevelProgress (independent tables)
  • Depends on: WechatGameModule, AuthModule
  • Pattern: Multi-table repository pattern for complex queries

Common Development Tasks

  • Add a new API endpoint: Create DTO in modules/{feature}/dto/, add method to controller, implement in service, call pnpm run lint && pnpm run test to verify
  • Add a new entity: Create *.entity.ts in module folder, add to TypeOrmModule.forFeature in module, update relevant repository
  • Modify API response format: Update DTO/entity, regenerate Swagger docs, update docs/api/*.md manually if not auto-synced
  • Debug a failing test: Run pnpm run test:debug -- --testNamePattern="specific test" and use Node inspector
  • Run migrations: TypeORM in-dev synchronize mode auto-creates tables; for production, use TypeORM CLI or manual SQL

Troubleshooting

  • Port 3000 in use: lsof -i :3000 and kill -9 <PID>, or change PORT env var
  • Database connection fails: Verify DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD in .env.local match your MySQL setup
  • JWT verification fails: Check JWT_SECRET is consistent across app instances and .env files
  • Swagger not loading: Ensure app.listen() completes; check browser console for CORS errors
  • Tests hanging: Check for open database connections; run pnpm run test:debug to inspect
  • Prettier/ESLint conflicts: Run pnpm run format && pnpm run lint in sequence (format first, then lint)

Additional Resources