docs: map existing codebase
This commit is contained in:
319
.planning/codebase/ARCHITECTURE.md
Normal file
319
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** NestJS modular backend + React feature-based frontend with real-time WebSocket synchronization
|
||||
|
||||
**Key Characteristics:**
|
||||
- Modular NestJS architecture with feature-based modules (Auth, Characters, Campaigns, Equipment, Battle)
|
||||
- Feature-first React organization with shared components and hooks
|
||||
- WebSocket Gateway real-time sync for character and battle state
|
||||
- Role-based access control (ADMIN, GM, PLAYER) enforced globally
|
||||
- Prisma ORM for PostgreSQL data persistence
|
||||
- JWT-based stateless authentication
|
||||
|
||||
## Layers
|
||||
|
||||
**Backend: Controller Layer**
|
||||
- Purpose: HTTP request handling and routing
|
||||
- Location: `server/src/modules/*/[feature].controller.ts`
|
||||
- Contains: REST endpoints with decorators (@Post, @Get, @Put, @Patch, @Delete)
|
||||
- Depends on: Services, DTOs, Guards, Decorators
|
||||
- Used by: HTTP clients (React frontend)
|
||||
- Examples:
|
||||
- `server/src/modules/characters/characters.controller.ts` - Character CRUD
|
||||
- `server/src/modules/campaigns/campaigns.controller.ts` - Campaign management
|
||||
- `server/src/modules/equipment/equipment.controller.ts` - Equipment search/browse
|
||||
- `server/src/modules/auth/auth.controller.ts` - Login/Register
|
||||
|
||||
**Backend: Service Layer**
|
||||
- Purpose: Business logic, data processing, validation
|
||||
- Location: `server/src/modules/*/[feature].service.ts`
|
||||
- Contains: Methods for creating, updating, deleting entities; complex calculations
|
||||
- Depends on: PrismaService, other Services, external APIs (Claude)
|
||||
- Used by: Controllers, Gateways, other Services
|
||||
- Examples:
|
||||
- `server/src/modules/characters/characters.service.ts` - Character operations, access checks
|
||||
- `server/src/modules/characters/alchemy.service.ts` - Alchemy system logic (formulas, prepared items, vials)
|
||||
- `server/src/modules/characters/pathbuilder-import.service.ts` - Pathbuilder JSON parsing
|
||||
- `server/src/modules/equipment/equipment.service.ts` - Equipment search with filters
|
||||
- `server/src/modules/battle/battle.service.ts` - Battle session and token management
|
||||
|
||||
**Backend: Gateway Layer (WebSocket)**
|
||||
- Purpose: Real-time bidirectional communication for live updates
|
||||
- Location: `server/src/modules/*/[feature].gateway.ts`
|
||||
- Contains: Socket.io event handlers, authentication, room management
|
||||
- Depends on: JwtService, PrismaService, Services
|
||||
- Used by: React WebSocket hooks
|
||||
- Examples:
|
||||
- `server/src/modules/characters/characters.gateway.ts` - Character HP, conditions, items, alchemy real-time sync
|
||||
- `server/src/modules/battle/battle.gateway.ts` - Battle token movement, HP, initiative real-time sync
|
||||
|
||||
**Backend: Persistence Layer**
|
||||
- Purpose: Database abstraction and ORM
|
||||
- Location: `server/src/prisma/prisma.service.ts`
|
||||
- Contains: Prisma client wrapper, query interface
|
||||
- Depends on: PostgreSQL database
|
||||
- Used by: All Services
|
||||
- Data Models defined in: `server/prisma/schema.prisma`
|
||||
|
||||
**Frontend: Feature Modules**
|
||||
- Purpose: Isolated feature domains with components, hooks, types
|
||||
- Location: `client/src/features/[feature]/`
|
||||
- Contains: Components, hooks, index.ts barrel exports
|
||||
- Examples:
|
||||
- `client/src/features/auth/` - Login/Register pages, useAuthStore hook
|
||||
- `client/src/features/characters/` - Character sheet page, modals, utilities
|
||||
- `client/src/features/campaigns/` - Campaign list/detail pages
|
||||
- `client/src/features/battle/` - Battle canvas, tokens
|
||||
- `client/src/features/library/` - Battle maps, NPC templates (combatants)
|
||||
|
||||
**Frontend: Shared Layer**
|
||||
- Purpose: Reusable components, hooks, utilities, types across features
|
||||
- Location: `client/src/shared/`
|
||||
- Contains:
|
||||
- Components: `ui/` (shadcn/ui), `layout.tsx`, `protected-route.tsx`
|
||||
- Hooks: `use-character-socket.ts`, `use-battle-socket.ts`
|
||||
- Lib: `api.ts` (API client), `utils.ts` (helpers)
|
||||
- Types: `index.ts` (all TypeScript interfaces)
|
||||
- Used by: All features
|
||||
|
||||
**Frontend: Router**
|
||||
- Purpose: Navigation and route protection
|
||||
- Location: `client/src/App.tsx`
|
||||
- Contains: React Router v6 routes, protected route wrapper
|
||||
- Depends on: React Router, Feature components
|
||||
- Entry point: `client/src/main.tsx`
|
||||
|
||||
**Frontend: State Management**
|
||||
- Purpose: Client-side state persistence
|
||||
- Location: `client/src/features/auth/hooks/use-auth-store.ts`
|
||||
- Contains: Zustand store for authentication state
|
||||
- Used by: Auth-related components, ProtectedRoute
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Character Update (Real-Time WebSocket Sync)
|
||||
|
||||
1. **User Action** (React Component)
|
||||
- User damages character in `client/src/features/characters/components/hp-control.tsx`
|
||||
- Calls `api.updateCharacterHp()` → PATCH `/campaigns/:id/characters/:id/hp`
|
||||
|
||||
2. **Controller** (`server/src/modules/characters/characters.controller.ts`)
|
||||
- Receives HTTP request
|
||||
- Validates JWT token via `JwtAuthGuard`
|
||||
- Calls `CharactersService.updateHp()`
|
||||
|
||||
3. **Service** (`server/src/modules/characters/characters.service.ts`)
|
||||
- Checks campaign access (GM or campaign member)
|
||||
- Checks character ownership (owner or GM can edit)
|
||||
- Updates character HP in Prisma
|
||||
- Emits WebSocket event via `CharactersGateway.broadcast()`
|
||||
|
||||
4. **Gateway Broadcast** (`server/src/modules/characters/characters.gateway.ts`)
|
||||
- Broadcasts `character_update` event to all clients in character room
|
||||
- Update type: `'hp'`
|
||||
- Payload: `{ hpCurrent, hpTemp, hpMax }`
|
||||
|
||||
5. **Client Socket Hook** (`client/src/shared/hooks/use-character-socket.ts`)
|
||||
- Listens for `character_update` event
|
||||
- Routes to appropriate callback: `onHpUpdate?.(data)`
|
||||
- Component state updates via React state
|
||||
|
||||
6. **Component Re-render**
|
||||
- Character sheet displays new HP values
|
||||
- All other connected clients see update in real-time
|
||||
|
||||
### Equipment Database Search
|
||||
|
||||
1. **User Search** (React Component)
|
||||
- User searches equipment in `client/src/features/characters/components/add-item-modal.tsx`
|
||||
- Calls `api.searchEquipment({ query, category, filters, page, limit })`
|
||||
|
||||
2. **Controller** (`server/src/modules/equipment/equipment.controller.ts`)
|
||||
- Receives GET `/equipment` with query params
|
||||
- Calls `EquipmentService.search()`
|
||||
|
||||
3. **Service** (`server/src/modules/equipment/equipment.service.ts`)
|
||||
- Builds Prisma where clause from filters
|
||||
- Queries Equipment table (5,482 items)
|
||||
- Returns paginated results with pagination metadata
|
||||
- Includes category list for UI dropdown
|
||||
|
||||
4. **Component Display**
|
||||
- Results displayed with pagination controls
|
||||
- User can view equipment details, add to inventory
|
||||
- New item created via `api.addCharacterItem()`
|
||||
|
||||
### Character Creation (Pathbuilder Import)
|
||||
|
||||
1. **User Upload** (React Component)
|
||||
- User uploads Pathbuilder JSON in `client/src/features/characters/components/import-character-modal.tsx`
|
||||
- Calls `api.importCharacterFromPathbuilder(pathbuilderJson)`
|
||||
|
||||
2. **Controller** (`server/src/modules/characters/characters.controller.ts`)
|
||||
- POST `/campaigns/:id/characters/import`
|
||||
- Calls `PathbuilderImportService.importCharacter()`
|
||||
|
||||
3. **Pathbuilder Import Service** (`server/src/modules/characters/pathbuilder-import.service.ts`)
|
||||
- Parses Pathbuilder JSON structure
|
||||
- Extracts abilities, skills, feats, spells
|
||||
- Creates Character with all related entities (CharacterAbility, CharacterSkill, etc.)
|
||||
- Stores raw pathbuilderData for future reference
|
||||
|
||||
4. **Database Persistence**
|
||||
- Character created with all nested relations
|
||||
- Each skill, feat, spell stored as separate records
|
||||
- Character ready for real-time sync
|
||||
|
||||
### Battle Session Synchronization
|
||||
|
||||
1. **Token Movement** (React Component)
|
||||
- User drags token on battle canvas
|
||||
- Calls `api.moveBattleToken(positionX, positionY)`
|
||||
|
||||
2. **Battle Gateway** (`server/src/modules/battle/battle.gateway.ts`)
|
||||
- Receives movement update
|
||||
- Broadcasts `battle_update` event with type `'token_moved'`
|
||||
- All clients in session room receive update
|
||||
|
||||
3. **Client Display**
|
||||
- All participants see token at new position
|
||||
- No UI lag — updates are instant
|
||||
|
||||
### Alchemy System State
|
||||
|
||||
1. **Prepare Items** (React Component)
|
||||
- User selects formulas to prepare daily in `client/src/features/characters/components/alchemy-tab.tsx`
|
||||
- Calls `api.dailyPreparation(items)`
|
||||
|
||||
2. **Alchemy Service** (`server/src/modules/characters/alchemy.service.ts`)
|
||||
- Validates character has formulas
|
||||
- Creates CharacterPreparedItem records
|
||||
- Updates CharacterAlchemyState (versatileVialsCurrent, advancedAlchemyMax)
|
||||
- Emits WebSocket `alchemy_prepared` and `alchemy_state` updates
|
||||
|
||||
3. **Real-Time Sync**
|
||||
- UI updates showing prepared items and vial tracker
|
||||
- Ready for combat use
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
**Prisma ORM Service:**
|
||||
- Purpose: Database abstraction, query builder
|
||||
- Location: `server/src/prisma/prisma.service.ts`
|
||||
- Pattern: Singleton service injected into all modules
|
||||
- Used for: All CRUD operations, complex queries with relations
|
||||
|
||||
**JWT Strategy & Guards:**
|
||||
- Purpose: Authentication and authorization
|
||||
- Location:
|
||||
- `server/src/modules/auth/strategies/jwt.strategy.ts` - JWT validation
|
||||
- `server/src/modules/auth/guards/jwt-auth.guard.ts` - Global auth enforcement
|
||||
- `server/src/modules/auth/guards/roles.guard.ts` - Role-based access control
|
||||
- Pattern: NestJS guards executed globally on every request
|
||||
- Metadata: `@Roles()` and `@Public()` decorators control per-endpoint behavior
|
||||
|
||||
**API Client Service:**
|
||||
- Purpose: HTTP communication abstraction
|
||||
- Location: `client/src/shared/lib/api.ts`
|
||||
- Pattern: Singleton class with axios instance
|
||||
- Features: Token management, auto-retry, auth interceptors, 401 handling
|
||||
|
||||
**WebSocket Socket Manager:**
|
||||
- Purpose: Prevent duplicate connections, manage subscriptions
|
||||
- Location: `client/src/shared/hooks/use-character-socket.ts`
|
||||
- Pattern: Global socket singleton with ref counting
|
||||
- Features: Auto-reconnect, polling fallback, room subscription
|
||||
|
||||
**Character/Battle Update Types:**
|
||||
- Purpose: Type-safe event dispatch
|
||||
- Location:
|
||||
- `server/src/modules/characters/characters.gateway.ts` (CharacterUpdatePayload)
|
||||
- `server/src/modules/battle/battle.gateway.ts` (BattleUpdatePayload)
|
||||
- Pattern: Union types for event kind discrimination
|
||||
|
||||
## Entry Points
|
||||
|
||||
**Server Entry:**
|
||||
- Location: `server/src/main.ts`
|
||||
- Triggers: `npm run start:dev` or deployed container startup
|
||||
- Responsibilities:
|
||||
- Create NestJS app from AppModule
|
||||
- Enable global pipes (ValidationPipe)
|
||||
- Configure CORS from env
|
||||
- Setup Swagger API docs at `/api/docs`
|
||||
- Listen on PORT (default 5000)
|
||||
|
||||
**Client Entry:**
|
||||
- Location: `client/src/main.tsx`
|
||||
- Triggers: Browser page load or `npm run dev`
|
||||
- Responsibilities:
|
||||
- Render React app into #root DOM element
|
||||
- Wrap with StrictMode
|
||||
|
||||
**Router:**
|
||||
- Location: `client/src/App.tsx`
|
||||
- Pattern: React Router v6 with protected routes
|
||||
- Flow:
|
||||
1. QueryClientProvider (React Query setup)
|
||||
2. BrowserRouter (React Router)
|
||||
3. AppContent checks auth state
|
||||
4. Public routes: /login, /register
|
||||
5. Protected routes: / (campaigns), /campaigns/:id, /campaigns/:id/characters/:characterId, /campaigns/:id/battle, /campaigns/:id/library
|
||||
6. ProtectedRoute wrapper ensures authentication
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Strategy:** Structured error responses with HTTP status codes
|
||||
|
||||
**Backend Patterns:**
|
||||
- `NotFoundException` - 404 when entity not found
|
||||
- `ForbiddenException` - 403 when user lacks permission
|
||||
- `BadRequestException` - 400 for invalid input
|
||||
- `UnauthorizedException` - 401 for auth failures
|
||||
- Global error filter could be added for consistent formatting
|
||||
- Service methods validate access before querying: `checkCampaignAccess()`, `checkCharacterAccess()`
|
||||
|
||||
**Frontend Patterns:**
|
||||
- API client `response.interceptors` catches 401 → redirects to /login
|
||||
- Components wrapped in error boundaries (future enhancement)
|
||||
- Failed requests return rejected promises to component
|
||||
|
||||
**WebSocket Patterns:**
|
||||
- Token verification on connection → disconnect if invalid
|
||||
- No structured error responses; silent failures with console logging
|
||||
- Clients auto-reconnect via socket.io configuration
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**Logging:**
|
||||
- Backend: NestJS Logger class used in services/gateways
|
||||
- Frontend: Console.log for development (socket.io events log connection state)
|
||||
|
||||
**Validation:**
|
||||
- Backend: Global ValidationPipe with DTOs (class-validator)
|
||||
- Frontend: Form validation in components (manual checks in modals)
|
||||
- Prisma schema enforces constraints (NOT NULL, unique, enums)
|
||||
|
||||
**Authentication:**
|
||||
- Backend: JwtAuthGuard applied globally in app.module.ts
|
||||
- Endpoints opt-out via @Public() decorator
|
||||
- WebSocket: Token verified in gateway.handleConnection()
|
||||
- Frontend: Token stored in localStorage (persistent) or sessionStorage (session-only)
|
||||
|
||||
**Authorization:**
|
||||
- Backend: RolesGuard checks @Roles() metadata
|
||||
- Service methods verify campaign/character ownership before allowing operations
|
||||
- Pattern: Check campaign membership → check character ownership → allow operation
|
||||
|
||||
**Real-Time Sync:**
|
||||
- WebSocket Gateway manages rooms (one room per character/session)
|
||||
- Clients join room on component mount, leave on unmount
|
||||
- Broadcasts to all clients in room except sender (optional)
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-04-27*
|
||||
509
.planning/codebase/CONCERNS.md
Normal file
509
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Tech Debt
|
||||
|
||||
### Debug Console Logging in Production Code
|
||||
|
||||
**Issue:** Production code contains debug console.log statements that should be removed before deployment
|
||||
|
||||
**Files:**
|
||||
- `server/src/modules/characters/characters.service.ts` (4 console.log statements in updateHp method, lines 263, 283, 303, 310)
|
||||
- `client/src/features/battle/hooks/use-battle-socket.ts` (3 console.log statements)
|
||||
- `client/src/features/characters/components/alchemy-tab.tsx` (3 console.log statements)
|
||||
- `client/src/features/characters/components/character-sheet-page.tsx` (1 console.log statement)
|
||||
- `client/src/shared/hooks/use-character-socket.ts` (4 console.log statements)
|
||||
|
||||
**Impact:** Exposes internal logic and data in browser/server console. Can leak sensitive information and degrade performance in high-volume operations.
|
||||
|
||||
**Fix approach:** Implement proper structured logging (Winston, Pino, or similar) for development vs. production environments. Remove or replace all console.log statements with conditional logging.
|
||||
|
||||
---
|
||||
|
||||
### N+1 Query Problem in Character Details Endpoint
|
||||
|
||||
**Issue:** The findOne method in CharactersService performs multiple sequential async API calls for translation enrichment, causing N+1 query pattern
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.service.ts` (lines 186-222)
|
||||
|
||||
**Problem Details:**
|
||||
- Fetches items, formulas, and preparedItems from database (3 queries)
|
||||
- For EACH item, formula, and prepared item, calls `enrichEquipmentWithTranslations()` which queries the Translations table or Claude API
|
||||
- If a character has 50 items, this results in 50+ additional Prisma queries (or Claude API calls)
|
||||
|
||||
**Impact:** Dramatically slow character sheet loading. Creates rate-limiting pressure on Claude API. Potential timeout on high-inventory characters.
|
||||
|
||||
**Fix approach:**
|
||||
1. Use Prisma's `include` to load equipment relationships in the initial query
|
||||
2. Batch translation lookups: collect all unique equipment IDs, translate in one batch, then merge results
|
||||
3. Consider caching translations at the service layer for repeated requests
|
||||
|
||||
---
|
||||
|
||||
### Oversized Service Class
|
||||
|
||||
**Issue:** CharactersService is 1,454 lines - violates single responsibility principle and makes testing difficult
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.service.ts`
|
||||
|
||||
**Contains:**
|
||||
- Character CRUD operations
|
||||
- HP management with dying/wounded condition logic
|
||||
- Item management
|
||||
- Condition management
|
||||
- Resource management
|
||||
- Rest system logic
|
||||
- Alchemy system logic (vials, formulas, prepared items)
|
||||
- Multiple helper methods (getDyingConditions, getDeathThreshold, etc.)
|
||||
|
||||
**Impact:** Hard to locate code, difficult to test individual features, increased risk of bugs when modifying one feature affecting another.
|
||||
|
||||
**Fix approach:** Split into multiple focused services:
|
||||
- `CharactersCoreService` - CRUD operations
|
||||
- `CharacterHpService` - HP and dying/wounded logic
|
||||
- `CharacterAlchemyService` - Alchemy system
|
||||
- `CharacterRestService` - Rest and recovery logic
|
||||
|
||||
---
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Missing Level-Up System
|
||||
|
||||
**Issue:** Level progression is mentioned in CLAUDE.md as "Noch zu implementieren" but no implementation exists
|
||||
|
||||
**Files:** No implementation - feature completely absent
|
||||
|
||||
**Impact:** Cannot advance character levels, blocking career progression mechanic.
|
||||
|
||||
**Workaround:** Level can be updated via direct database manipulation or API patching, but no UI or validation logic.
|
||||
|
||||
**Priority:** High - Critical gameplay feature
|
||||
|
||||
---
|
||||
|
||||
### Unused equipmentlevel.json File at Project Root
|
||||
|
||||
**Issue:** File `equipmentlevel.json` (250KB+) exists at project root but is not referenced by any code
|
||||
|
||||
**Files:** `/c/Users/ZielonkaA/Documents/Dimension-47/equipmentlevel.json`
|
||||
|
||||
**Impact:**
|
||||
- Takes up disk space
|
||||
- Ignored by git, clogs project root
|
||||
- Suggests abandoned equipment import/level-mapping system
|
||||
- Unknown purpose - may be partial/corrupted data
|
||||
|
||||
**Fix approach:**
|
||||
1. Determine original purpose (equipment level mapping?)
|
||||
2. Either integrate into equipment seed system or delete
|
||||
3. Check git history to understand why it was abandoned
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### WebSocket Gateway Authentication is Minimal
|
||||
|
||||
**Issue:** Token validation in WebSocket connection handler is basic but lacks some best practices
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.gateway.ts` (lines 60-93)
|
||||
|
||||
**Current implementation:**
|
||||
- Extracts token from `client.handshake.auth.token` or Authorization header
|
||||
- Verifies token signature with JWT_SECRET
|
||||
- Verifies user exists in database
|
||||
- No rate limiting on connection attempts
|
||||
- No IP whitelisting or additional authentication layers
|
||||
|
||||
**Potential risks:**
|
||||
- Rapid connection attempts could brute-force token validation or create connection storms
|
||||
- No tracking of legitimate vs. malicious connection attempts
|
||||
- Token rotation/revocation not supported (token valid until expiry)
|
||||
|
||||
**Recommendations:**
|
||||
1. Implement connection rate limiting per IP/user
|
||||
2. Add token revocation mechanism
|
||||
3. Consider requiring additional re-authentication for sensitive operations
|
||||
4. Log suspicious connection patterns
|
||||
|
||||
---
|
||||
|
||||
### Input Validation on Equipment Search Query
|
||||
|
||||
**Issue:** Equipment search endpoint accepts query parameters without upper bounds, potentially enabling resource exhaustion
|
||||
|
||||
**Files:** `server/src/modules/equipment/equipment.controller.ts` (lines 23-42)
|
||||
|
||||
**Current implementation:**
|
||||
- Takes `limit` parameter (defaults to 50, user-configurable)
|
||||
- No maximum limit enforced
|
||||
- Accepts large pagination page numbers
|
||||
- `traits` parameter is comma-separated without validation on count
|
||||
|
||||
**Potential risks:**
|
||||
- `?limit=999999999` could cause OOM or timeout
|
||||
- `?page=999999999999` could overflow internal calculations
|
||||
- Unbounded trait queries could cause DoS
|
||||
|
||||
**Fix approach:**
|
||||
1. Set hard maximum limits: `limit` ≤ 500, `page` ≤ 10000
|
||||
2. Validate and cap traits array length ≤ 50
|
||||
3. Implement pagination cursor-based approach instead of offset-based
|
||||
4. Add rate limiting per user/IP on search endpoint
|
||||
|
||||
---
|
||||
|
||||
### CORS Configuration Overly Permissive in Development
|
||||
|
||||
**Issue:** Development CORS allows all localhost origins, which is standard but could leak requests to unintended local services
|
||||
|
||||
**Files:** `server/src/main.ts` (lines 26-47)
|
||||
|
||||
**Current implementation:**
|
||||
```typescript
|
||||
if (nodeEnv === 'development' && /^https?:\/\/localhost(:\d+)?$/.test(origin)) {
|
||||
return callback(null, true);
|
||||
}
|
||||
```
|
||||
|
||||
**Impact:** Any localhost service can make cross-origin requests to the API. Low risk in pure development, but could leak credentials or data if other services run on localhost.
|
||||
|
||||
**Fix approach:** In development, explicitly list expected origins (e.g., `http://localhost:5173`) rather than wildcard localhost.
|
||||
|
||||
---
|
||||
|
||||
## Performance Bottlenecks
|
||||
|
||||
### Equipment Database with 5,482 Items - Unindexed Search
|
||||
|
||||
**Issue:** Full-text search on Equipment table lacks proper indexing for performance
|
||||
|
||||
**Files:**
|
||||
- `server/src/modules/equipment/equipment.service.ts` (search implementation)
|
||||
- `server/prisma/schema.prisma` (Equipment model definition)
|
||||
- `server/prisma/seed-equipment.ts` (5,482 items)
|
||||
|
||||
**Current queries use:**
|
||||
- `.contains()` filters on `name`, `summary`, `effect` fields
|
||||
- No full-text search index
|
||||
- No composite indexes for common filter combinations
|
||||
|
||||
**Impact:**
|
||||
- Search queries on large equipment set may be slow
|
||||
- If multiple users search simultaneously, database load spikes
|
||||
- Trait filtering requires collection of all results then filtering in application code (N+1 pattern with filters)
|
||||
|
||||
**Improvement path:**
|
||||
1. Add PostgreSQL `@@@` full-text search with tsvector indexes
|
||||
2. Add composite indexes: `(category, itemCategory)`, `(level, name)`
|
||||
3. Use Prisma's `findRaw` for complex searches with proper SQL optimization
|
||||
4. Consider search caching with Redis for common queries
|
||||
|
||||
---
|
||||
|
||||
### Translation Caching at Item Retrieval Time
|
||||
|
||||
**Issue:** Translations are fetched on-demand during character detail retrieval, causing repeated Claude API calls for same items
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.service.ts` (lines 126-147, 186-222)
|
||||
|
||||
**Current behavior:**
|
||||
- First time a character's item is viewed: cache miss, Claude API called
|
||||
- If multiple characters have same item: Claude API called multiple times
|
||||
- No cache invalidation strategy or TTL
|
||||
|
||||
**Impact:**
|
||||
- Increased Claude API costs and latency
|
||||
- Rate limiting pressure if many characters queried
|
||||
- Slow character sheet loads
|
||||
|
||||
**Fix approach:**
|
||||
1. Pre-cache translations for common items at server startup
|
||||
2. Implement TTL-based cache expiration (e.g., 30 days)
|
||||
3. Batch missing translations when loading characters with multiple items
|
||||
4. Cache at equipment level, not at character level
|
||||
|
||||
---
|
||||
|
||||
## Fragile Areas
|
||||
|
||||
### Character Death/Resurrection State Management
|
||||
|
||||
**Issue:** Dying/Wounded conditions interact with HP changes in complex ways that could have race conditions
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.service.ts` (lines 262-406)
|
||||
|
||||
**Fragile interactions:**
|
||||
- When HP drops to 0: checks Wounded/Doomed values, calculates Dying value, adds/updates condition, broadcasts update
|
||||
- When HP increases from 0: removes Dying, adds/increments Wounded, broadcasts update
|
||||
- Multiple async operations between checking and updating
|
||||
- No transaction wrapping
|
||||
|
||||
**Risks:**
|
||||
- If two updates happen simultaneously (WebSocket + rest endpoint), conditions could get corrupted
|
||||
- Wounded value calculation depends on database state that could change between read and write
|
||||
- Broadcasting happens after database update, potential sync issues if broadcast fails
|
||||
|
||||
**Safe modification:**
|
||||
1. Wrap Dying/Wounded logic in Prisma transaction
|
||||
2. Add optimistic locking (version field) to Character model
|
||||
3. Consolidate HP update and condition management into single atomic operation
|
||||
4. Test scenarios: multiple simultaneous HP changes, network race conditions
|
||||
|
||||
**Test coverage:** Currently untested (no test files in server codebase)
|
||||
|
||||
---
|
||||
|
||||
### Alchemy System State Consistency
|
||||
|
||||
**Issue:** Alchemy state (vials, formulas, prepared items) spans multiple database models with complex interdependencies
|
||||
|
||||
**Files:**
|
||||
- `server/src/modules/characters/characters.service.ts` (alchemy methods ~200+ lines)
|
||||
- `server/prisma/schema.prisma` (CharacterAlchemyState, CharacterFormula, CharacterPreparedItem, CharacterResource)
|
||||
|
||||
**Fragile points:**
|
||||
- Prepared items created by `createPreparedItem()` with `isInfused: true`, then deleted by rest system
|
||||
- Versatile vials count stored in CharacterAlchemyState but actual vials tracked in CharacterResource
|
||||
- Formula learning updates both CharacterFormula and CharacterResource
|
||||
- No constraint ensuring consistency between these tables
|
||||
|
||||
**Risk:** Orphaned records, inconsistent vial counts, invalid formula states
|
||||
|
||||
**Safe modification:**
|
||||
1. Add unique constraint validation in service methods
|
||||
2. Use Prisma transactions for multi-table alchemy operations
|
||||
3. Add data integrity checks in rest system
|
||||
4. Create migration to validate existing data
|
||||
|
||||
---
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
### No Backend Tests
|
||||
|
||||
**Issue:** Server codebase has zero test files - all business logic untested
|
||||
|
||||
**Files:**
|
||||
- `server/src/` directory contains no `.test.ts` or `.spec.ts` files
|
||||
- Test infrastructure exists (Jest, @nestjs/testing configured) but unused
|
||||
|
||||
**Untested functionality:**
|
||||
- Authentication and authorization guards
|
||||
- Character CRUD with access control
|
||||
- HP updates with Dying/Wounded condition logic (most critical)
|
||||
- Rest system with conditional resource resets
|
||||
- Alchemy system with formula learning and vial tracking
|
||||
- WebSocket message handlers (race conditions, auth)
|
||||
- Equipment search with filtering
|
||||
|
||||
**Priority areas for testing:**
|
||||
1. Character death/resurrection state transitions (HIGH - bug risk)
|
||||
2. Authentication guards on protected endpoints (HIGH - security)
|
||||
3. Access control: ensure players can't edit others' characters (HIGH - security)
|
||||
4. Rest system calculations with various ability scores (MEDIUM)
|
||||
5. Alchemy vial/formula consistency (MEDIUM)
|
||||
|
||||
**Fix approach:**
|
||||
1. Create test files for each module: `auth.service.spec.ts`, `characters.service.spec.ts`, etc.
|
||||
2. Start with authentication and authorization tests (security-critical)
|
||||
3. Add tests for HP/Dying state transitions (highest bug risk)
|
||||
4. Use Prisma's test database pattern for integration tests
|
||||
|
||||
---
|
||||
|
||||
### No Frontend Tests
|
||||
|
||||
**Issue:** Client codebase has zero test files - no component or hook testing
|
||||
|
||||
**Files:**
|
||||
- `client/src/` directory contains no `.test.tsx` or `.spec.tsx` files
|
||||
- Test infrastructure missing (no Jest/Vitest config, no testing-library)
|
||||
|
||||
**Untested components:**
|
||||
- Character sheet tabs (Inventory, Alchemy, Actions, Talents)
|
||||
- HP control component with temporary/current/max interactions
|
||||
- Condition management modal
|
||||
- Rest modal with preview calculation
|
||||
- WebSocket hooks for real-time sync
|
||||
- Item search and filtering UI
|
||||
|
||||
**Impact:** Cannot verify UI correctly reflects data, no regression protection, refactoring is risky
|
||||
|
||||
**Fix approach:**
|
||||
1. Install Vitest + React Testing Library
|
||||
2. Create test files for critical components: HP control, Rest modal, Item search
|
||||
3. Test WebSocket connection/disconnection handling
|
||||
4. Add snapshot tests for data-heavy components (character sheet)
|
||||
|
||||
---
|
||||
|
||||
## Missing Critical Features
|
||||
|
||||
### Level-Up System
|
||||
|
||||
**Issue:** Character advancement is incomplete
|
||||
|
||||
**Problem:**
|
||||
- Level field exists in database and UI, but no level-up mechanics
|
||||
- Cannot allocate ability score increases at certain levels
|
||||
- Cannot select new feats when leveling
|
||||
- Cannot add/remove skills when leveling
|
||||
|
||||
**Files:** No implementation exists
|
||||
|
||||
**Blocks:** Full character progression gameplay
|
||||
|
||||
---
|
||||
|
||||
## Dependencies at Risk
|
||||
|
||||
### Claude API Integration as Critical Path
|
||||
|
||||
**Issue:** German translation system depends on Claude API for on-demand translation calls
|
||||
|
||||
**Files:**
|
||||
- `server/src/modules/claude/claude.service.ts`
|
||||
- `server/src/modules/translations/translations.service.ts`
|
||||
|
||||
**Risk points:**
|
||||
1. No fallback if API is unavailable - character sheets fail to load with translations
|
||||
2. Rate limiting: high-traffic scenarios could hit Claude API limits
|
||||
3. Cost: unbounded - every unique item/feat/spell triggers an API call
|
||||
4. Latency: API calls on the critical path of character sheet loading (3+ second potential delay)
|
||||
|
||||
**Migration plan:**
|
||||
1. Pre-populate translations for all 5,482 equipment items on server startup
|
||||
2. Batch translate feats/spells during seeding
|
||||
3. Use Claude API only for user-created custom items
|
||||
4. Cache everything in database with TTL
|
||||
5. Implement graceful degradation: show English names if translation fails
|
||||
|
||||
---
|
||||
|
||||
### Prisma 7.2.0 - Check for Security/Critical Updates
|
||||
|
||||
**Files:**
|
||||
- `server/package.json` - `@prisma/client: ^7.2.0`
|
||||
- `server/prisma/schema.prisma` - Using latest client
|
||||
|
||||
**Note:** Version is recent (January 2025+) but "^" allows minor updates. Monitor for critical patches in Prisma security advisories.
|
||||
|
||||
---
|
||||
|
||||
## Scaling Limits
|
||||
|
||||
### Equipment Search at 5,482 items
|
||||
|
||||
**Current capacity:** Linear search with `.contains()` filters
|
||||
|
||||
**Limit:** Performance degrades beyond ~10,000 items, potential timeout at 50,000+
|
||||
|
||||
**Scaling path:**
|
||||
1. Add PostgreSQL full-text search indexes (supports millions of items)
|
||||
2. Implement search result caching
|
||||
3. Consider Elasticsearch if full-text becomes bottleneck
|
||||
4. Pagination already uses `take/skip`, good foundation
|
||||
|
||||
---
|
||||
|
||||
### WebSocket Connections per Campaign
|
||||
|
||||
**Issue:** No connection pooling limits, no per-campaign connection quota
|
||||
|
||||
**Files:** `server/src/modules/characters/characters.gateway.ts`
|
||||
|
||||
**Current behavior:**
|
||||
- Each connection stores socket in `connectedClients` Map
|
||||
- Broadcasts go to all sockets in character room
|
||||
- No limits on concurrent connections per campaign or globally
|
||||
|
||||
**Potential issue:** If campaign has 100 characters and each has 10 connected users watching, that's 1,000 broadcast operations per update. At scale this causes CPU/memory pressure.
|
||||
|
||||
**Scaling recommendations:**
|
||||
1. Set max connections per campaign (e.g., 500)
|
||||
2. Implement connection pooling/groups (watch list vs. active participants)
|
||||
3. Use socket.io's built-in room management more aggressively
|
||||
4. Monitor memory usage of `connectedClients` Map for leaks
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Concerns
|
||||
|
||||
### Character pathbuilderData Stored as JSON Blob
|
||||
|
||||
**Issue:** Original Pathbuilder export is stored unstructured as JSON
|
||||
|
||||
**Files:** `server/prisma/schema.prisma` (line 198: `pathbuilderData Json?`)
|
||||
|
||||
**Potential issues:**
|
||||
- Cannot query Pathbuilder fields directly
|
||||
- Data validation relies on application code, not schema
|
||||
- Difficult to report or audit what data came from imports
|
||||
- No versioning if Pathbuilder export format changes
|
||||
|
||||
**Impact:** Low - used primarily for reference, but limits data flexibility
|
||||
|
||||
**Improvement path:**
|
||||
1. Parse and normalize Pathbuilder data into proper database fields
|
||||
2. Consider storing hash of original for audit trail
|
||||
3. Add migration to extract key fields from JSON for querying
|
||||
|
||||
---
|
||||
|
||||
## Environmental Configuration
|
||||
|
||||
### JWT_SECRET Management
|
||||
|
||||
**Issue:** JWT_SECRET passed via environment variable with no rotation mechanism
|
||||
|
||||
**Files:**
|
||||
- `server/src/modules/auth/strategies/jwt.strategy.ts`
|
||||
- `server/src/modules/characters/characters.gateway.ts`
|
||||
|
||||
**Concern:**
|
||||
- If secret is leaked, all existing tokens become invalid
|
||||
- No way to rotate secret without invalidating all sessions
|
||||
- No secret versioning
|
||||
|
||||
**Recommendation:**
|
||||
1. Implement secret versioning in token header
|
||||
2. Support multiple valid secrets during rotation period
|
||||
3. Add secret rotation schedule (e.g., quarterly)
|
||||
4. Store secrets in vaulting system, not .env
|
||||
|
||||
---
|
||||
|
||||
### ENV File Example Status
|
||||
|
||||
**Files:**
|
||||
- `server/.env.example`
|
||||
- `client/.env.example`
|
||||
|
||||
**Status:** Confirmed present in CLAUDE.md - properly structured
|
||||
|
||||
**Verification:** Users can reference templates when setting up environment
|
||||
|
||||
---
|
||||
|
||||
## API Documentation
|
||||
|
||||
### Swagger API Docs Enabled but may Expose Sensitive Operations
|
||||
|
||||
**Files:** `server/src/main.ts` (lines 49-64)
|
||||
|
||||
**Current state:**
|
||||
- Swagger UI enabled at `/api/docs`
|
||||
- Available to all users with API access
|
||||
- Could expose internal API structure to unauthorized users
|
||||
|
||||
**Recommendation:**
|
||||
- In production: require ADMIN role to view Swagger
|
||||
- In development: keep open for debugging
|
||||
- Add basic authentication to `/api/docs` endpoint
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-04-27*
|
||||
221
.planning/codebase/CONVENTIONS.md
Normal file
221
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Components: kebab-case (e.g., `character-sheet-page.tsx`, `add-condition-modal.tsx`, `hp-control.tsx`)
|
||||
- Utilities/Services: kebab-case (e.g., `use-character-socket.ts`, `export-character-html.ts`)
|
||||
- Directories: kebab-case (e.g., `features/`, `shared/`, `components/`)
|
||||
|
||||
**Functions:**
|
||||
- camelCase for all function names
|
||||
- Hooks prefixed with `use` (e.g., `useAuthStore`, `useCharacterSocket`, `useMemo`, `useState`)
|
||||
- Event handlers prefixed with `handle` (e.g., `handleApply`, `handleConnection`, `handleDisconnect`)
|
||||
- Getter/setter methods follow camelCase (e.g., `getToken()`, `setToken()`)
|
||||
|
||||
**Variables:**
|
||||
- camelCase for all variable names
|
||||
- Constants in UPPER_SNAKE_CASE (e.g., `PROFICIENCY_BONUS`, `SKILL_DATA`, `TABS`)
|
||||
- Type unions with capitalized names (e.g., `CharacterType`, `AbilityType`, `TabType`)
|
||||
- Boolean prefixes: `is`, `has`, `should`, `can` (e.g., `isLoading`, `isOwner`, `hasAccess`)
|
||||
|
||||
**Types:**
|
||||
- PascalCase for all types, interfaces, and enums
|
||||
- Domain types in shared: `Character`, `Campaign`, `User`, `CharacterItem`, `CharacterFeat`
|
||||
- Props interfaces suffixed with `Props` (e.g., `HpControlProps`, `AddConditionModalProps`)
|
||||
- DTO interfaces suffixed with `Dto` (e.g., `CreateCharacterDto`, `LoginDto`, `RegisterDto`)
|
||||
- State types in Zustand: domain-specific (e.g., `AuthState`)
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- Prettier configured in `server/.prettierrc` with:
|
||||
- Single quotes (`singleQuote: true`)
|
||||
- Trailing commas on all (`trailingComma: "all"`)
|
||||
- Line length: implicit (Prettier default ~80 chars but allows overflow)
|
||||
- Indentation: 2 spaces
|
||||
|
||||
**Linting:**
|
||||
- Client: ESLint 9 with flat config (`eslint.config.js`)
|
||||
- Uses TypeScript ESLint recommended rules
|
||||
- React Hooks plugin with recommended rules
|
||||
- React Refresh plugin for Vite
|
||||
- No custom strict rules beyond defaults
|
||||
- Server: ESLint 9 with flat config (`eslint.config.mjs`)
|
||||
- Uses TypeScript ESLint recommended rules with type checking (`recommendedTypeChecked`)
|
||||
- Prettier plugin for code formatting integration
|
||||
- **Important:** `@typescript-eslint/no-explicit-any` is **turned OFF** (line 29)
|
||||
- Warnings (not errors) for: `no-floating-promises`, `no-unsafe-argument`
|
||||
- Custom Prettier rule with `endOfLine: "auto"` for Windows compatibility
|
||||
|
||||
**TypeScript:**
|
||||
- **Strict Mode Enforced** on client (`client/tsconfig.app.json`):
|
||||
- `"strict": true` ✓
|
||||
- `"noUnusedLocals": true` ✓
|
||||
- `"noUnusedParameters": true` ✓
|
||||
- `"noFallthroughCasesInSwitch": true` ✓
|
||||
- `"noUncheckedSideEffectImports": true` ✓
|
||||
- Server TypeScript (`server/tsconfig.json`):
|
||||
- `"strictNullChecks": true` ✓
|
||||
- `"forceConsistentCasingInFileNames": true` ✓
|
||||
- `"noImplicitAny": false` (allows any, soft enforcement)
|
||||
- `"skipLibCheck": true` (skips lib type checking)
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order:** (observed pattern)
|
||||
1. React/Framework imports (`react`, `react-router-dom`)
|
||||
2. Third-party utilities (`zustand`, `axios`, `socket.io-client`)
|
||||
3. NestJS/Framework imports (`@nestjs/*`)
|
||||
4. Type-only imports (`type { ... }`)
|
||||
5. Relative imports from shared (`@/shared/...`, `@/features/...`)
|
||||
6. Decorators and metadata (after other imports)
|
||||
|
||||
**Path Aliases:**
|
||||
- Client: `@/*` → `./src/*` (defined in `vite.config.ts` and `tsconfig.app.json`)
|
||||
- Server: No path alias configured; uses relative paths
|
||||
- Always use `@/` prefix on client to avoid relative path hell
|
||||
|
||||
**Module exports:**
|
||||
- Barrel files used: `features/*/index.ts`, `shared/components/ui/index.ts`
|
||||
- Default exports on pages: `export default App`
|
||||
- Named exports for utilities, types, hooks: `export { HpControl }`, `export const api = new ApiClient()`
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Philosophy (per CLAUDE.md):** No shortcuts. Proper error handling, not try/catch with console.log.
|
||||
|
||||
**Server-side (NestJS):**
|
||||
- Use NestJS exception classes: `NotFoundException`, `ForbiddenException`, `UnauthorizedException`, `ConflictException`
|
||||
- Access checks before operations (e.g., `checkCampaignAccess()`, `checkCharacterAccess()` in `characters.service.ts`)
|
||||
- Return meaningful error messages: "Campaign not found", "No access to this campaign"
|
||||
- Prisma operations wrapped in try-catch only when necessary for data validation
|
||||
- JWT verification throws exceptions through guard: `JwtAuthGuard` handles 401s
|
||||
|
||||
**Client-side (React):**
|
||||
- Axios interceptors for request/response handling (in `api.ts`)
|
||||
- 401 errors trigger logout and redirect to `/login` (except auth endpoints)
|
||||
- Error state in components: `isLoading`, `error`, `pendingChange` states
|
||||
- Modal operations: try/catch with `setIsLoading(false)` in finally block
|
||||
- Error logging: `console.error()` for debugging, no silent failures
|
||||
- User feedback: Navigation on error (`navigate()`) or via error state
|
||||
|
||||
**Socket.io (WebSocket Gateway):**
|
||||
- Token validation on connection (throws `client.disconnect()`)
|
||||
- Logger for all connection/disconnection events
|
||||
- Errors logged but don't crash server
|
||||
|
||||
## Logging
|
||||
|
||||
**Framework:**
|
||||
- Server: NestJS `Logger` (in modules/gateways)
|
||||
- E.g., `private logger = new Logger('CharactersGateway')`
|
||||
- Used for connection events, warnings, errors
|
||||
- Client: `console.error()` for error debugging
|
||||
|
||||
**Patterns:**
|
||||
- Server logs connection events with user context
|
||||
- Client silently catches most errors, logs critical ones
|
||||
- No debug logging infrastructure in place
|
||||
|
||||
## Comments
|
||||
|
||||
**When to Comment:**
|
||||
- Constants with unclear meaning (e.g., `PROFICIENCY_BONUS` values, skill-to-ability mappings)
|
||||
- Complex calculations (e.g., HP percentage calculations)
|
||||
- Business logic that isn't immediately obvious (e.g., Pathfinder 2e rules)
|
||||
- NOT used for obvious code ("increment counter")
|
||||
|
||||
**JSDoc/TSDoc:**
|
||||
- Minimal usage observed
|
||||
- API methods documented with Swagger decorators on server: `@ApiOperation()`, `@ApiResponse()`
|
||||
- No runtime JSDoc comments in source
|
||||
|
||||
## Function Design
|
||||
|
||||
**Size:**
|
||||
- Modal components: 500-1700 lines (large, but feature-complete)
|
||||
- Service methods: 10-50 lines (concise, focused)
|
||||
- Utility functions: 5-20 lines
|
||||
|
||||
**Parameters:**
|
||||
- Interface-based: Pass objects instead of multiple params
|
||||
- E.g., `onAdd(condition: { name: string; nameGerman: string; value?: number })`
|
||||
- DTO pattern on server: `CreateCharacterDto`, `LoginDto`
|
||||
- Optional params with defaults: `remember: boolean = false`
|
||||
|
||||
**Return Values:**
|
||||
- Async functions return typed Promises: `async getCharacter(): Promise<Character>`
|
||||
- Component callbacks: callbacks are async promises (`onHpChange: (newHp: number) => Promise<void>`)
|
||||
- Services return full domain objects with relations included
|
||||
|
||||
## Module Design
|
||||
|
||||
**Exports:**
|
||||
- Components export as named: `export function HpControl() { ... }`
|
||||
- Utilities export as named: `export const api = new ApiClient()`
|
||||
- One export per file (generally)
|
||||
|
||||
**Barrel Files:**
|
||||
- Used in features: `features/auth/index.ts` re-exports `useAuthStore`, `LoginPage`, `RegisterPage`
|
||||
- Used for UI components: `shared/components/ui/index.ts` re-exports all buttons, cards, inputs
|
||||
- Reduces import complexity
|
||||
|
||||
**Dependency Injection (Server):**
|
||||
- NestJS @Injectable() decorator for services
|
||||
- Constructor injection: `constructor(private prisma: PrismaService)`
|
||||
- Circular dependencies resolved with @Inject(forwardRef()): seen in `CharactersService`
|
||||
|
||||
## State Management
|
||||
|
||||
**Client (Zustand):**
|
||||
- Single store pattern: `create<AuthState>()`
|
||||
- Persisted state with middleware: `persist()`
|
||||
- Partialize: stores only non-sensitive fields (`user`, `isAuthenticated`)
|
||||
- Actions as methods in store: `login()`, `logout()`, `checkAuth()`
|
||||
- Error state in store: `error: string | null`
|
||||
|
||||
**Server (NestJS):**
|
||||
- No client state management needed
|
||||
- WebSocket gateway maintains connection map: `connectedClients = new Map()`
|
||||
|
||||
## Design Tokens (UI)
|
||||
|
||||
**Colors:**
|
||||
- Primary: Magenta `#c26dbc` (in Tailwind as `primary-500`, `primary-600`, etc.)
|
||||
- Secondary: Dark grays for dark mode (`secondary-800`, `secondary-700`)
|
||||
- Text: Primary (light), secondary (dimmer), tertiary (dimmest)
|
||||
- Background: Primary, secondary, tertiary layers
|
||||
- Error: Red (`error-500`, `error-600`)
|
||||
- Success: Green (`green-500`)
|
||||
- Warning: Yellow (`yellow-500`)
|
||||
|
||||
**Typography:**
|
||||
- Font sizes: sm, base, lg (Tailwind defaults)
|
||||
- Weights: normal, medium (600), semibold
|
||||
- All UI text in German except code/technical terms
|
||||
|
||||
**Icons:**
|
||||
- Lucide React exclusively (e.g., `Heart`, `Swords`, `BookOpen`, `Package`)
|
||||
- No emoji anywhere
|
||||
- Icon sizes: `h-4 w-4` (standard), `h-6 w-6` (large)
|
||||
|
||||
**Touch Targets:**
|
||||
- Minimum 44px (mobile-first, accessible)
|
||||
- Buttons: `h-11` (44px), `h-9` (36px for small), `h-12` (48px for large)
|
||||
- Icon buttons: `h-11 w-11` (44x44px)
|
||||
|
||||
**Spacing:**
|
||||
- Tailwind v4 defaults: `p-4`, `gap-2`, `rounded-lg`, `rounded-2xl`
|
||||
- Modals: `rounded-t-2xl sm:rounded-2xl` (bottom sheet on mobile, centered on desktop)
|
||||
|
||||
**Responsive:**
|
||||
- Mobile-first breakpoints: `sm:` prefix for tablet+ (e.g., `sm:items-center`, `sm:rounded-2xl`)
|
||||
- Flexbox for layouts: `flex`, `flex-col`, `items-center`, `justify-between`
|
||||
- Grid rarely used
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-04-27*
|
||||
226
.planning/codebase/INTEGRATIONS.md
Normal file
226
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## APIs & External Services
|
||||
|
||||
**Anthropic Claude API:**
|
||||
- Service: Claude API for real-time translation of game content to German
|
||||
- What it's used for:
|
||||
- On-demand translation of feats, equipment, spells, items, conditions, and other PF2e content
|
||||
- Translates item effect text for alchemical items
|
||||
- Batches translations in groups for efficiency
|
||||
- Results cached in `Translation` table with quality ratings (HIGH/MEDIUM/LOW)
|
||||
- Fallback: System returns untranslated English names if API key not configured
|
||||
- Model: `claude-haiku-4-5-20251001` (lightweight model for cost-effective translations)
|
||||
|
||||
- SDK/Client: `@anthropic-ai/sdk` 0.71.2
|
||||
- Auth: Environment variable `ANTHROPIC_API_KEY`
|
||||
- Implementation: `server/src/modules/claude/claude.service.ts`
|
||||
- Module: `server/src/modules/claude/claude.module.ts`
|
||||
- Usage: Called by `server/src/modules/translations/translations.service.ts`
|
||||
|
||||
## Data Storage
|
||||
|
||||
**Databases:**
|
||||
- PostgreSQL 16 (via Docker Compose or production instance)
|
||||
- Connection: Environment variable `DATABASE_URL`
|
||||
- Client: Prisma ORM 7.2.0 with PostgreSQL adapter
|
||||
- Schema: `server/prisma/schema.prisma` (25 models covering users, campaigns, characters, equipment, alchemy, battle, documents)
|
||||
- Migrations: Version-controlled in `server/prisma/migrations/`
|
||||
- Seeding: `server/prisma/seed.ts` (base data), `server/prisma/seed-equipment.ts` (5,482 PF2e items), `server/prisma/seed-feats.ts`
|
||||
|
||||
**File Storage:**
|
||||
- Local filesystem only (currently)
|
||||
- Directory: Configured via `UPLOAD_DIR` env var (default `./uploads`)
|
||||
- Served via NestJS `ServeStaticModule` at `/uploads` endpoint
|
||||
- Max file size: `MAX_FILE_SIZE` env var (default 10MB)
|
||||
- Types supported: HTML, PDF (from schema `Document` model)
|
||||
|
||||
**Caching:**
|
||||
- In-memory client state via Zustand (auth store): `client/src/features/auth/hooks/use-auth-store.ts`
|
||||
- Server-side caching via TanStack React Query: Automatic cache of API responses with configurable stale times
|
||||
- Translation cache in `Translation` table with `@@unique([type, englishName])` constraint
|
||||
- No Redis or external cache layer currently
|
||||
|
||||
## Authentication & Identity
|
||||
|
||||
**Auth Provider:**
|
||||
- Custom JWT-based authentication (no external OAuth provider)
|
||||
- Implementation: `server/src/modules/auth/auth.service.ts`
|
||||
- Strategy: Passport.js with JWT strategy (`server/src/modules/auth/strategies/jwt.strategy.ts`)
|
||||
|
||||
**Authentication Flow:**
|
||||
1. Users register with username, email, password via `POST /api/auth/register`
|
||||
2. Passwords hashed with bcrypt (salt rounds: 10, configurable)
|
||||
3. Login returns JWT token valid for `JWT_EXPIRES_IN` (default "7d")
|
||||
4. Client stores token in localStorage (persistent) or sessionStorage (session-only)
|
||||
- Determined by "Remember me" checkbox during login
|
||||
- Implementation: `client/src/shared/lib/api.ts`
|
||||
5. Token attached to all subsequent requests via Authorization header
|
||||
6. Token verified on WebSocket connection via `characters.gateway.ts` and `battle.gateway.ts`
|
||||
|
||||
**User Roles:**
|
||||
- ADMIN - Full system access
|
||||
- GM - Game Master, can manage campaigns, create NPCs, control battles
|
||||
- PLAYER - Standard user, can own characters
|
||||
|
||||
**Guards:**
|
||||
- Global JWT auth guard: `server/src/modules/auth/guards/jwt-auth.guard.ts`
|
||||
- Role-based guard: `server/src/modules/auth/guards/roles.guard.ts`
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
**Error Tracking:**
|
||||
- None (no Sentry, Rollbar, or external service configured)
|
||||
- Server logs to console via NestJS Logger
|
||||
- Client console errors only
|
||||
|
||||
**Logs:**
|
||||
- Server: NestJS Logger (console output in development, can be piped to file in production)
|
||||
- Client: Browser console only
|
||||
- No centralized logging infrastructure
|
||||
|
||||
## CI/CD & Deployment
|
||||
|
||||
**Hosting:**
|
||||
- Not configured (manual deployment expected)
|
||||
- Docker Compose available for local development
|
||||
|
||||
**CI Pipeline:**
|
||||
- None detected
|
||||
- ESLint, Prettier, and tests are run locally before commit
|
||||
|
||||
## WebSockets & Real-Time Communication
|
||||
|
||||
**Transport:**
|
||||
- Socket.io 4.8.3
|
||||
- Client library: `socket.io-client` 4.8.3
|
||||
- Server: NestJS WebSocket gateways with Socket.io adapter
|
||||
|
||||
**Character Updates (Real-Time Sync):**
|
||||
- Gateway: `server/src/modules/characters/characters.gateway.ts`
|
||||
- Namespace: `/characters`
|
||||
- CORS configured from environment variable `CORS_ORIGINS`
|
||||
- Authentication: JWT token verified on connection
|
||||
- Update types:
|
||||
- `hp` - Health points (current, temp, max)
|
||||
- `conditions` - Add/remove/update character conditions
|
||||
- `item` - Add/remove inventory items
|
||||
- `inventory` - Bulk inventory updates
|
||||
- `money` - Update credits/currency
|
||||
- `level` - Character level changes
|
||||
- `equipment_status` - Mark items as equipped/unequipped/invested
|
||||
- `rest` - Rest cycle triggers (HP restore, condition cleanup)
|
||||
- `alchemy_vials` - Update alchemist versatile vials
|
||||
- `alchemy_formulas` - Add/remove formulae
|
||||
- `alchemy_prepared` - Update prepared alchemical items
|
||||
- `alchemy_state` - Update research field and advanced alchemy state
|
||||
- `dying` - Dying and recovery state
|
||||
|
||||
**Battle Updates (Real-Time Sync):**
|
||||
- Gateway: `server/src/modules/battle/battle.gateway.ts`
|
||||
- Update types:
|
||||
- `token_added` - New combatant token on map
|
||||
- `token_removed` - Token removed
|
||||
- `token_moved` - Position changed
|
||||
- `token_updated` - Stat/property updates
|
||||
- `token_hp_changed` - HP/damage tracking
|
||||
- `initiative_set` - Initiative order
|
||||
- `round_advanced` - Round counter
|
||||
- `session_updated` - Session state
|
||||
- `session_deleted` - Battle session ended
|
||||
|
||||
**Client Hooks:**
|
||||
- `client/src/features/characters/hooks/use-character-socket.ts` - Character update subscriptions
|
||||
- `client/src/features/battle/hooks/use-battle-socket.ts` - Battle update subscriptions
|
||||
- Singleton socket pattern to prevent multiple connections
|
||||
- Reference counting for cleanup on unmount
|
||||
|
||||
## Pathbuilder Integration
|
||||
|
||||
**Import Format:**
|
||||
- Reads JSON export from Pathbuilder 2e character builder
|
||||
- Stored as raw JSON blob in `Character.pathbuilderData` field
|
||||
- Used to populate character stats, feats, skills, spells on initial import
|
||||
|
||||
**Data Mapping:**
|
||||
- Character abilities (STR, DEX, CON, INT, WIS, CHA)
|
||||
- Skills with proficiency levels
|
||||
- Feats with sources (Class, Ancestry, General, Skill, Bonus, Archetype)
|
||||
- Spells with traditions and prepared status
|
||||
- Equipment and inventory
|
||||
- Alchemy (if applicable)
|
||||
|
||||
**Endpoint:**
|
||||
- `POST /api/characters/import-pathbuilder` - Import character from Pathbuilder JSON
|
||||
|
||||
## Equipment Database
|
||||
|
||||
**Data Source:**
|
||||
- 5,482 Pathfinder 2e equipment items imported from JSON
|
||||
- Seed script: `server/prisma/seed-equipment.ts`
|
||||
- Source: JSON data files in `server/prisma/data/`
|
||||
|
||||
**Categories:**
|
||||
- Weapons (with damage, range, properties)
|
||||
- Armor (with AC, penalties, strength requirements)
|
||||
- Shields (with HP, hardness, broken threshold)
|
||||
- Consumables (potions, scrolls, talismans, etc.)
|
||||
- General equipment
|
||||
|
||||
**API Endpoints:**
|
||||
- `GET /api/equipment` - Search/list equipment
|
||||
- `GET /api/equipment/categories` - List available categories
|
||||
- `GET /api/equipment/weapons` - Weapon-specific query
|
||||
- `GET /api/equipment/armor` - Armor-specific query
|
||||
|
||||
## External References
|
||||
|
||||
**Archon Omni Documentation (AONPRD):**
|
||||
- Links stored in `Feat.url`, `Equipment.url`, `Spell.url`, `Trait.url`
|
||||
- Frontend links to official Pathfinder 2e documentation
|
||||
- No direct API integration, read-only external links
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
**Required Environment Variables:**
|
||||
|
||||
Server (`server/.env`):
|
||||
- `DATABASE_URL` - PostgreSQL connection string (e.g., `postgresql://user:pass@localhost:5432/dimension47?schema=public`)
|
||||
- `JWT_SECRET` - Secret key for signing JWT tokens (minimum 32 characters recommended)
|
||||
- `JWT_EXPIRES_IN` - Token expiration (default "7d")
|
||||
- `PORT` - Server listen port (default 5000)
|
||||
- `NODE_ENV` - Environment mode (development/production)
|
||||
- `CORS_ORIGINS` - Comma-separated allowed origins (e.g., `http://localhost:5173,http://localhost:3000`)
|
||||
- `ANTHROPIC_API_KEY` - Claude API key (optional, disables translations if missing)
|
||||
- `UPLOAD_DIR` - File upload directory (default `./uploads`)
|
||||
- `MAX_FILE_SIZE` - Max upload size in bytes (default 10485760 = 10MB)
|
||||
|
||||
Client (`client/.env`):
|
||||
- `VITE_API_URL` - Backend API URL (e.g., `http://localhost:5000/api`)
|
||||
|
||||
**Secrets Location:**
|
||||
- `.env` files (NOT committed to git, see `.gitignore`)
|
||||
- Template files: `server/.env.example` and `client/.env.example`
|
||||
- Developers copy examples to `.env` and fill in secrets
|
||||
|
||||
## Webhooks & Callbacks
|
||||
|
||||
**Incoming:**
|
||||
- None configured
|
||||
|
||||
**Outgoing:**
|
||||
- None configured
|
||||
|
||||
## Cross-Domain Communication
|
||||
|
||||
**CORS Policy:**
|
||||
- Configured via `CORS_ORIGINS` environment variable
|
||||
- Applied to HTTP endpoints and WebSocket connections
|
||||
- Development allows all `localhost:*` origins
|
||||
- Production requires explicit whitelist
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-04-27*
|
||||
164
.planning/codebase/STACK.md
Normal file
164
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- TypeScript 5.9.3 (server), 5.7.3 (client) - Strict mode enabled, full type safety across codebase
|
||||
|
||||
**Secondary:**
|
||||
- JavaScript (build outputs, scripts)
|
||||
- HTML/CSS (frontend templates)
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- Node.js (version not pinned, assumed LTS) - Used for both server and client dev/build
|
||||
- ES2023 target (server), ES2022 target (client)
|
||||
|
||||
**Package Manager:**
|
||||
- npm - Lockfiles present for both client and server
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core Frontend:**
|
||||
- React 19.2.0 - UI framework
|
||||
- React Router DOM 7.12.0 - Client-side routing
|
||||
- Vite 7.2.4 - Build tool and dev server
|
||||
|
||||
**Core Backend:**
|
||||
- NestJS 11.0.1 - Web framework with TypeScript-first design
|
||||
- Express (via `@nestjs/platform-express`) - Underlying HTTP server
|
||||
|
||||
**Frontend Styling:**
|
||||
- Tailwind CSS 4.1.18 - Utility-first CSS framework
|
||||
- `@tailwindcss/vite` plugin for Vite integration
|
||||
|
||||
**Testing & Development:**
|
||||
- Jest 30.0.0 - Test runner (server-side only, configured in package.json)
|
||||
- ts-jest - TypeScript support for Jest
|
||||
- Supertest 7.0.0 - HTTP assertion library (server integration tests)
|
||||
- @vitejs/plugin-react - React fast refresh for Vite
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Critical Server:**
|
||||
- `@prisma/client` 7.2.0 - ORM and database abstraction layer
|
||||
- `prisma` 7.2.0 - CLI and schema management tool
|
||||
- `@anthropic-ai/sdk` 0.71.2 - Claude API for on-demand German translations
|
||||
- `socket.io` 4.8.3 - WebSocket library for real-time communication
|
||||
- `@nestjs/websockets` 11.1.12 - NestJS WebSocket integration
|
||||
- `@nestjs/platform-socket.io` 11.1.12 - Socket.io adapter for NestJS
|
||||
- `@nestjs/jwt` 11.0.2 - JWT authentication provider
|
||||
- `@nestjs/passport` 11.0.5 - Passport.js integration for authentication
|
||||
- `passport-jwt` 4.0.1 - JWT strategy for Passport
|
||||
- `bcrypt` 6.0.0 - Password hashing
|
||||
- `class-validator` 0.14.3 - Request DTO validation
|
||||
- `class-transformer` 0.5.1 - DTO transformation
|
||||
- `@nestjs/swagger` 11.2.5 - OpenAPI/Swagger documentation
|
||||
- `@nestjs/config` 4.0.2 - Environment configuration management
|
||||
- `dotenv` 17.2.3 - .env file loading
|
||||
|
||||
**Critical Client:**
|
||||
- `axios` 1.13.2 - HTTP client library for API requests
|
||||
- `socket.io-client` 4.8.3 - WebSocket client for real-time updates
|
||||
- `zustand` 5.0.10 - Client state management (auth store)
|
||||
- `@tanstack/react-query` 5.90.19 - Server state and data fetching (caching, synchronization)
|
||||
- `react-router-dom` 7.12.0 - Client-side routing
|
||||
- `framer-motion` 12.26.2 - Animation library
|
||||
- `lucide-react` 0.562.0 - Icon library (SVG icons)
|
||||
- `clsx` 2.1.1 - Conditional CSS class utilities
|
||||
- `tailwind-merge` 3.4.0 - Tailwind class merging utility
|
||||
|
||||
**Development (Backend):**
|
||||
- `@nestjs/cli` 11.0.0 - NestJS code generation and project scaffolding
|
||||
- `@nestjs/schematics` 11.0.0 - Code generators for NestJS
|
||||
- `prettier` 3.4.2 - Code formatter
|
||||
- `eslint` 9.18.0 - Linting
|
||||
- `typescript-eslint` 8.20.0 - TypeScript ESLint support
|
||||
- `tsx` 4.21.0 - TypeScript execution (used for seed scripts)
|
||||
- `tsconfig-paths` 4.2.0 - TypeScript path alias resolution
|
||||
- `ts-node` 10.9.2 - TypeScript REPL and script runner
|
||||
- `ts-loader` 9.5.2 - TypeScript webpack loader
|
||||
|
||||
**Development (Frontend):**
|
||||
- `eslint` 9.39.1 - Linting
|
||||
- `typescript-eslint` 8.46.4 - TypeScript ESLint support
|
||||
- `eslint-plugin-react-hooks` 7.0.1 - React Hooks linting rules
|
||||
- `eslint-plugin-react-refresh` 0.4.24 - Vite React refresh linting
|
||||
|
||||
## Configuration
|
||||
|
||||
**Environment:**
|
||||
- Server loads from `.env` file (see `.env.example`):
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- `JWT_SECRET` - Secret key for signing tokens
|
||||
- `JWT_EXPIRES_IN` - Token expiration time (default "7d")
|
||||
- `PORT` - Server port (default 5000)
|
||||
- `NODE_ENV` - Environment (development/production)
|
||||
- `CORS_ORIGINS` - Comma-separated list of allowed origins
|
||||
- `ANTHROPIC_API_KEY` - Claude API key for translations (optional)
|
||||
- `UPLOAD_DIR` - Directory for file uploads
|
||||
- `MAX_FILE_SIZE` - Maximum upload file size
|
||||
|
||||
- Client loads from `.env` or `.env.local`:
|
||||
- `VITE_API_URL` - Backend API base URL (e.g., `http://localhost:5000/api`)
|
||||
- WebSocket URL derived automatically by removing `/api` suffix
|
||||
|
||||
**TypeScript:**
|
||||
- Server: `server/tsconfig.json` with target ES2023, decorators enabled
|
||||
- `strictNullChecks: true`
|
||||
- `forceConsistentCasingInFileNames: true`
|
||||
- `noImplicitAny: false` (allows implicit any in some cases)
|
||||
- Path aliases via `baseUrl: ./`
|
||||
|
||||
- Client: `client/tsconfig.app.json` with target ES2022, strict mode
|
||||
- `strict: true` - All strict options enabled
|
||||
- `noUnusedLocals: true`, `noUnusedParameters: true`
|
||||
- `jsx: react-jsx` - JSX without React import required
|
||||
- Path alias `@/*` → `./src/*`
|
||||
|
||||
**Build:**
|
||||
- Client Vite config: `client/vite.config.ts`
|
||||
- Dev server port 5173
|
||||
- API proxy `/api` → `http://localhost:5000`
|
||||
- React plugin with React 19 support
|
||||
- Tailwind CSS Vite plugin
|
||||
- Type checking via `tsc -b` before build
|
||||
|
||||
- Server NestJS config: `server/nest-cli.json` (if exists)
|
||||
- Build output to `dist/`
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Node.js LTS (tested with v22)
|
||||
- npm (lockfiles version management)
|
||||
- PostgreSQL 16+ (via Docker Compose)
|
||||
- Docker & Docker Compose (for database)
|
||||
- Git
|
||||
|
||||
**Production:**
|
||||
- Node.js LTS
|
||||
- PostgreSQL 16+ (managed service or self-hosted)
|
||||
- Anthropic API key for translations (optional but recommended)
|
||||
- File storage: Local filesystem or cloud storage (currently local via `./uploads`)
|
||||
|
||||
## Database
|
||||
|
||||
**PostgreSQL 16 Alpine** (via Docker Compose):
|
||||
- Port 5432 (internal), 5433 (exposed for dev)
|
||||
- Database name: `dimension47`
|
||||
- Default credentials in docker-compose (dev only)
|
||||
- pgAdmin 4 included for database management (port 5050)
|
||||
|
||||
**Prisma Configuration:**
|
||||
- Schema: `server/prisma/schema.prisma`
|
||||
- Generator: Prisma Client with CommonJS module format
|
||||
- Adapter: `@prisma/adapter-pg` for optimized PostgreSQL queries
|
||||
- Output: `src/generated/prisma/`
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-04-27*
|
||||
458
.planning/codebase/STRUCTURE.md
Normal file
458
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
dimension47/
|
||||
├── client/ # React 19 + Vite frontend
|
||||
│ ├── src/
|
||||
│ │ ├── main.tsx # React root + DOM render
|
||||
│ │ ├── App.tsx # Router setup (BrowserRouter, Routes)
|
||||
│ │ ├── index.css # Global styles (Tailwind v4)
|
||||
│ │ ├── app/ # App configuration & routing
|
||||
│ │ ├── features/ # Feature modules (auth, characters, campaigns, etc.)
|
||||
│ │ │ ├── auth/ # Authentication (login, register, useAuthStore)
|
||||
│ │ │ │ ├── components/
|
||||
│ │ │ │ │ ├── login-page.tsx
|
||||
│ │ │ │ │ └── register-page.tsx
|
||||
│ │ │ │ ├── hooks/
|
||||
│ │ │ │ │ └── use-auth-store.ts
|
||||
│ │ │ │ └── index.ts # Barrel export
|
||||
│ │ │ ├── characters/ # Character sheet & inventory
|
||||
│ │ │ │ ├── components/ # Modals, tabs, controls
|
||||
│ │ │ │ │ ├── character-sheet-page.tsx # Main tab container
|
||||
│ │ │ │ │ ├── actions-tab.tsx # PF2e actions list
|
||||
│ │ │ │ │ ├── alchemy-tab.tsx # Alchemy system
|
||||
│ │ │ │ │ ├── add-condition-modal.tsx # Add status effect
|
||||
│ │ │ │ │ ├── add-item-modal.tsx # Equipment search & add
|
||||
│ │ │ │ │ ├── add-feat-modal.tsx # Add talent
|
||||
│ │ │ │ │ ├── feat-detail-modal.tsx # Talent details
|
||||
│ │ │ │ │ ├── item-detail-modal.tsx # Equipment details
|
||||
│ │ │ │ │ ├── hp-control.tsx # HP management
|
||||
│ │ │ │ │ ├── rest-modal.tsx # Rest system
|
||||
│ │ │ │ │ ├── recovery-check-modal.tsx # Dying state recovery
|
||||
│ │ │ │ │ ├── dying-indicator.tsx # Dying condition display
|
||||
│ │ │ │ │ ├── import-character-modal.tsx # Pathbuilder import
|
||||
│ │ │ │ │ ├── create-character-modal.tsx # New character
|
||||
│ │ │ │ │ ├── edit-character-modal.tsx # Edit character info
|
||||
│ │ │ │ │ └── image-crop-modal.tsx # Avatar cropping
|
||||
│ │ │ │ ├── utils/
|
||||
│ │ │ │ │ └── export-character-html.ts # Character sheet HTML export
|
||||
│ │ │ │ └── index.ts
|
||||
│ │ │ ├── campaigns/ # Campaign management
|
||||
│ │ │ │ ├── components/
|
||||
│ │ │ │ │ ├── campaigns-page.tsx # Campaign list (home)
|
||||
│ │ │ │ │ ├── campaign-detail-page.tsx # Campaign overview
|
||||
│ │ │ │ │ ├── create-campaign-modal.tsx # New campaign
|
||||
│ │ │ │ │ ├── edit-campaign-modal.tsx # Edit campaign info
|
||||
│ │ │ │ │ └── add-member-modal.tsx # Add player to campaign
|
||||
│ │ │ │ └── index.ts
|
||||
│ │ │ ├── battle/ # Battle screen & real-time combat
|
||||
│ │ │ │ ├── components/
|
||||
│ │ │ │ │ ├── battle-page.tsx # Main battle screen
|
||||
│ │ │ │ │ ├── battle-canvas.tsx # Map & token rendering
|
||||
│ │ │ │ │ ├── battle-session-list.tsx # Session selection
|
||||
│ │ │ │ │ └── token.tsx # Token component
|
||||
│ │ │ │ ├── hooks/
|
||||
│ │ │ │ │ └── use-battle-socket.ts # Battle WebSocket hook
|
||||
│ │ │ │ └── index.ts
|
||||
│ │ │ └── library/ # GM library (maps, combatants)
|
||||
│ │ │ ├── components/
|
||||
│ │ │ │ ├── library-page.tsx # Maps & combatants list
|
||||
│ │ │ │ ├── upload-map-modal.tsx # Add battle map
|
||||
│ │ │ │ └── create-combatant-modal.tsx # Create NPC template
|
||||
│ │ │ └── index.ts
|
||||
│ │ ├── shared/ # Reusable code across features
|
||||
│ │ │ ├── components/
|
||||
│ │ │ │ ├── layout.tsx # App wrapper (navbar, layout)
|
||||
│ │ │ │ ├── protected-route.tsx # Auth enforcement wrapper
|
||||
│ │ │ │ └── ui/ # shadcn/ui components
|
||||
│ │ │ │ ├── button.tsx
|
||||
│ │ │ │ ├── card.tsx
|
||||
│ │ │ │ ├── input.tsx
|
||||
│ │ │ │ ├── spinner.tsx
|
||||
│ │ │ │ ├── action-icon.tsx
|
||||
│ │ │ │ └── index.ts
|
||||
│ │ │ ├── hooks/
|
||||
│ │ │ │ ├── use-character-socket.ts # Character real-time sync
|
||||
│ │ │ │ └── (battle hooks if added)
|
||||
│ │ │ ├── lib/
|
||||
│ │ │ │ ├── api.ts # API client (axios + token mgmt)
|
||||
│ │ │ │ └── utils.ts # Utility functions
|
||||
│ │ │ └── types/
|
||||
│ │ │ └── index.ts # All TypeScript interfaces & types
|
||||
│ │ └── assets/ # Images, fonts
|
||||
│ ├── public/
|
||||
│ │ ├── data/ # JSON data files (equipment, etc.)
|
||||
│ │ └── icons/ # SVG/icon files
|
||||
│ ├── index.html # HTML template
|
||||
│ ├── vite.config.ts # Vite configuration
|
||||
│ ├── tsconfig.json # TypeScript configuration
|
||||
│ ├── tailwind.config.js # Tailwind CSS v4 config
|
||||
│ ├── postcss.config.js # PostCSS plugins
|
||||
│ ├── package.json
|
||||
│ └── dist/ # Build output (generated)
|
||||
│
|
||||
├── server/ # NestJS backend
|
||||
│ ├── src/
|
||||
│ │ ├── main.ts # Server bootstrap & app setup
|
||||
│ │ ├── app.module.ts # Root NestJS module (imports all features)
|
||||
│ │ ├── common/ # Shared utilities across modules
|
||||
│ │ │ └── decorators/
|
||||
│ │ │ ├── current-user.decorator.ts # Extract user from request
|
||||
│ │ │ ├── public.decorator.ts # Skip auth on endpoints
|
||||
│ │ │ ├── roles.decorator.ts # Specify required roles
|
||||
│ │ │ └── index.ts
|
||||
│ │ ├── prisma/
|
||||
│ │ │ └── prisma.service.ts # Database service wrapper
|
||||
│ │ ├── modules/ # Feature modules (NestJS-style)
|
||||
│ │ │ ├── auth/ # Authentication & JWT
|
||||
│ │ │ │ ├── auth.controller.ts # POST /auth/login, /auth/register
|
||||
│ │ │ │ ├── auth.service.ts # User validation, JWT signing
|
||||
│ │ │ │ ├── auth.module.ts # Module exports & dependencies
|
||||
│ │ │ │ ├── guards/
|
||||
│ │ │ │ │ ├── jwt-auth.guard.ts # JWT validation (global)
|
||||
│ │ │ │ │ └── roles.guard.ts # Role checking
|
||||
│ │ │ │ ├── strategies/
|
||||
│ │ │ │ │ └── jwt.strategy.ts # Passport JWT strategy
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ │ ├── login.dto.ts
|
||||
│ │ │ │ └── register.dto.ts
|
||||
│ │ │ ├── campaigns/ # Campaign management
|
||||
│ │ │ │ ├── campaigns.controller.ts # CRUD campaigns, member management
|
||||
│ │ │ │ ├── campaigns.service.ts # Campaign operations & access checks
|
||||
│ │ │ │ ├── campaigns.module.ts
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ │ └── (campaign DTOs)
|
||||
│ │ │ ├── characters/ # Character system (core feature)
|
||||
│ │ │ │ ├── characters.controller.ts # Character CRUD, conditions, items, feats
|
||||
│ │ │ │ ├── characters.service.ts # Character logic, HP, conditions, inventory
|
||||
│ │ │ │ ├── characters.gateway.ts # WebSocket real-time sync
|
||||
│ │ │ │ ├── characters.module.ts
|
||||
│ │ │ │ ├── pathbuilder-import.service.ts # Pathbuilder JSON parsing
|
||||
│ │ │ │ ├── alchemy.controller.ts # Alchemy endpoints
|
||||
│ │ │ │ ├── alchemy.service.ts # Alchemy system logic
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ │ └── (character, alchemy DTOs)
|
||||
│ │ │ ├── equipment/ # Equipment database
|
||||
│ │ │ │ ├── equipment.controller.ts # GET /equipment (search, categories)
|
||||
│ │ │ │ ├── equipment.service.ts # Equipment search & filtering
|
||||
│ │ │ │ ├── equipment.module.ts
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ ├── feats/ # Feat database
|
||||
│ │ │ │ ├── feats.controller.ts # GET /feats (search)
|
||||
│ │ │ │ ├── feats.service.ts # Feat queries
|
||||
│ │ │ │ ├── feats.module.ts
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ ├── battle/ # Battle system (maps, sessions, tokens)
|
||||
│ │ │ │ ├── battle.controller.ts # POST/GET /battles
|
||||
│ │ │ │ ├── battle.service.ts # Battle session logic
|
||||
│ │ │ │ ├── battle.gateway.ts # WebSocket battle updates
|
||||
│ │ │ │ ├── battle.module.ts
|
||||
│ │ │ │ ├── battle-maps.controller.ts # Battle map CRUD
|
||||
│ │ │ │ ├── battle-maps.service.ts
|
||||
│ │ │ │ ├── combatants.controller.ts # NPC/monster templates
|
||||
│ │ │ │ ├── combatants.service.ts
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ ├── translations/ # German translations cache
|
||||
│ │ │ │ ├── translations.controller.ts # GET translations
|
||||
│ │ │ │ ├── translations.service.ts # Cache & Claude API calls
|
||||
│ │ │ │ ├── translations.module.ts
|
||||
│ │ │ │ └── dto/
|
||||
│ │ │ └── claude/ # Claude API integration
|
||||
│ │ │ ├── claude.module.ts
|
||||
│ │ │ └── claude.service.ts # Claude API wrapper
|
||||
│ │ └── generated/ # Generated code (do not edit)
|
||||
│ │ └── prisma/ # Prisma client types & enums
|
||||
│ │ ├── client.ts
|
||||
│ │ ├── models.ts
|
||||
│ │ ├── enums.ts
|
||||
│ │ └── ...
|
||||
│ ├── prisma/ # Prisma ORM configuration
|
||||
│ │ ├── schema.prisma # Database schema (tables, relations, enums)
|
||||
│ │ ├── migrations/ # Versioned database migrations
|
||||
│ │ │ ├── 20260118162916_init/
|
||||
│ │ │ ├── 20260118225853_add_equipment_detail_fields/
|
||||
│ │ │ ├── 20260119083024_add_credits_to_character/
|
||||
│ │ │ └── 20260119111209_add_item_custom_fields/
|
||||
│ │ ├── seed.ts # Base seed script (users, etc.)
|
||||
│ │ └── data/ # JSON source data for seeding
|
||||
│ │ ├── equipment.json # 5,482 PF2e equipment items
|
||||
│ │ ├── feats.json # PF2e talents
|
||||
│ │ ├── spells.json # PF2e spells
|
||||
│ │ ├── actions.json # PF2e combat actions
|
||||
│ │ ├── conditions.json # PF2e conditions (Pathfinder 2e ruleset)
|
||||
│ │ └── ...
|
||||
│ ├── src/generated/ # Generated Prisma types
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── nest-cli.json
|
||||
│ └── dist/ # Build output (generated)
|
||||
│
|
||||
├── .planning/ # Phase planning documents
|
||||
│ └── codebase/ # Architecture analysis (this file)
|
||||
│ ├── ARCHITECTURE.md
|
||||
│ ├── STRUCTURE.md
|
||||
│ ├── STACK.md
|
||||
│ ├── INTEGRATIONS.md
|
||||
│ ├── CONVENTIONS.md
|
||||
│ ├── TESTING.md
|
||||
│ └── CONCERNS.md
|
||||
├── docs/ # Documentation
|
||||
├── .claude/ # Claude settings & instructions
|
||||
│ └── settings.local.json
|
||||
├── package-lock.json # Lock file (top-level)
|
||||
└── CLAUDE.md # Project guidelines (Quality > Speed)
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
### Client Structure
|
||||
|
||||
**`client/src/features/`** - Feature-based modules
|
||||
- Each feature is a semi-independent domain (auth, characters, campaigns, battle, library)
|
||||
- Contains components specific to that feature
|
||||
- Exports public API via barrel exports (`index.ts`)
|
||||
- Examples: AuthFeature manages login/register, CharactersFeature manages character sheet
|
||||
- Pattern: Feature-based organization allows independent testing and scaling
|
||||
|
||||
**`client/src/shared/`** - Cross-feature reusables
|
||||
- `components/ui/` - shadcn/ui primitive components (Button, Card, Input, etc.)
|
||||
- `components/layout.tsx` - App wrapper with navbar
|
||||
- `components/protected-route.tsx` - Auth enforcement for routes
|
||||
- `hooks/` - Custom React hooks (useCharacterSocket, etc.)
|
||||
- `lib/api.ts` - HTTP client with token management
|
||||
- `lib/utils.ts` - Helper functions
|
||||
- `types/` - All TypeScript interfaces shared across app
|
||||
- Pattern: Single source of truth for types, no duplication
|
||||
|
||||
**`client/public/`** - Static files
|
||||
- `data/` - JSON files (if any live here, but equipment comes from backend)
|
||||
- `icons/` - SVG icons used in UI
|
||||
|
||||
### Server Structure
|
||||
|
||||
**`server/src/modules/`** - NestJS modules by feature
|
||||
- Each module is self-contained: controller → service → gateway
|
||||
- Module exports what other modules need via `exports: []` in decorator
|
||||
- Examples:
|
||||
- CharactersModule exports CharactersService, CharactersGateway for cross-module use
|
||||
- AuthModule exports AuthService, guards for other modules to use
|
||||
- Pattern: Module-based organization with explicit dependency injection
|
||||
|
||||
**`server/src/common/`** - Shared utilities
|
||||
- Decorators: @CurrentUser, @Public, @Roles
|
||||
- Guards: Shared by all modules
|
||||
- Pattern: Cross-cutting concerns in one place
|
||||
|
||||
**`server/prisma/`** - Database layer
|
||||
- `schema.prisma` - Single source of truth for data model
|
||||
- `migrations/` - Versioned schema changes (never use `db push`)
|
||||
- `seed.ts` - Initial data
|
||||
- `data/` - JSON source files for seed scripts
|
||||
- Pattern: Database-first approach, all data in DB not JSON files
|
||||
|
||||
## Key File Locations
|
||||
|
||||
### Entry Points
|
||||
|
||||
**Backend:**
|
||||
- `server/src/main.ts` - Bootstrap NestJS app, enable pipes/guards/swagger
|
||||
- AppModule at `server/src/app.module.ts` - Wires all modules together
|
||||
|
||||
**Frontend:**
|
||||
- `client/src/main.tsx` - React root render
|
||||
- Router at `client/src/App.tsx` - BrowserRouter with route protection
|
||||
|
||||
### Configuration
|
||||
|
||||
**Environment:**
|
||||
- `server/.env` - PostgreSQL, JWT_SECRET, ANTHROPIC_API_KEY, CORS_ORIGINS
|
||||
- `client/.env` - VITE_API_URL (must match server PORT)
|
||||
- Example files: `server/.env.example`, `client/.env.example`
|
||||
|
||||
**Build & Development:**
|
||||
- Backend:
|
||||
- `server/nest-cli.json` - NestJS CLI config
|
||||
- `server/tsconfig.json` - TypeScript settings
|
||||
- Frontend:
|
||||
- `client/vite.config.ts` - Vite bundler (alias paths with `@`)
|
||||
- `client/tsconfig.json` - TypeScript settings (strict mode)
|
||||
- `client/tailwind.config.js` - Tailwind v4 customization
|
||||
- `client/postcss.config.js` - CSS processing
|
||||
|
||||
### Core Logic
|
||||
|
||||
**Character System:**
|
||||
- Service: `server/src/modules/characters/characters.service.ts` - HP, conditions, items
|
||||
- Gateway: `server/src/modules/characters/characters.gateway.ts` - Real-time sync
|
||||
- Import: `server/src/modules/characters/pathbuilder-import.service.ts` - Pathbuilder parsing
|
||||
- Alchemy: `server/src/modules/characters/alchemy.service.ts` - Alchemy logic
|
||||
- UI: `client/src/features/characters/components/character-sheet-page.tsx` - Main page
|
||||
|
||||
**Battle System:**
|
||||
- Service: `server/src/modules/battle/battle.service.ts` - Sessions & tokens
|
||||
- Gateway: `server/src/modules/battle/battle.gateway.ts` - Real-time sync
|
||||
- Canvas: `client/src/features/battle/components/battle-canvas.tsx` - Map & tokens
|
||||
|
||||
**Equipment Database:**
|
||||
- Service: `server/src/modules/equipment/equipment.service.ts` - Search & filtering
|
||||
- Controller: `server/src/modules/equipment/equipment.controller.ts` - Endpoints
|
||||
- UI: `client/src/features/characters/components/add-item-modal.tsx` - Search UI
|
||||
- Data: `server/prisma/data/equipment.json` - 5,482 items (seeded to DB)
|
||||
|
||||
**Authentication:**
|
||||
- Service: `server/src/modules/auth/auth.service.ts` - Login/register/JWT
|
||||
- Guard: `server/src/modules/auth/guards/jwt-auth.guard.ts` - Applied globally
|
||||
- Store: `client/src/features/auth/hooks/use-auth-store.ts` - Client-side auth state
|
||||
- API: `client/src/shared/lib/api.ts` - Token persistence (localStorage vs sessionStorage)
|
||||
|
||||
### Testing
|
||||
|
||||
**Backend:**
|
||||
- Test files: `server/src/**/*.spec.ts` (not yet created - opportunity)
|
||||
- Config: `server/jest.config.js` (if exists)
|
||||
|
||||
**Frontend:**
|
||||
- Test files: `client/src/**/*.spec.tsx` (not yet created - opportunity)
|
||||
- Config: `client/vitest.config.ts` (if exists) or `client/jest.config.js`
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Files
|
||||
|
||||
**Component files (React):**
|
||||
- Pattern: kebab-case `.tsx`
|
||||
- Example: `character-sheet-page.tsx`, `add-item-modal.tsx`, `hp-control.tsx`
|
||||
- Convention: Lowercase with hyphens, `.tsx` for JSX
|
||||
|
||||
**Service files (NestJS):**
|
||||
- Pattern: kebab-case `.ts`
|
||||
- Example: `characters.service.ts`, `pathbuilder-import.service.ts`, `alchemy.service.ts`
|
||||
- Convention: Feature name + responsibility (service/controller/gateway)
|
||||
|
||||
**Guard/Strategy files:**
|
||||
- Pattern: kebab-case `.ts`
|
||||
- Example: `jwt-auth.guard.ts`, `jwt.strategy.ts`
|
||||
|
||||
**DTO files:**
|
||||
- Pattern: kebab-case `.ts` inside `dto/` folder
|
||||
- Example: `login.dto.ts`, `register.dto.ts`
|
||||
|
||||
**Module files:**
|
||||
- Pattern: Feature name + `.module.ts`
|
||||
- Example: `auth.module.ts`, `characters.module.ts`
|
||||
|
||||
### Directories
|
||||
|
||||
**Feature directories:**
|
||||
- Pattern: kebab-case (lowercase with hyphens)
|
||||
- Example: `auth/`, `campaigns/`, `characters/`, `battle/`, `library/`
|
||||
- Structure: Each feature has `components/`, `hooks/`, `utils/`, `index.ts`
|
||||
|
||||
**Shared directories:**
|
||||
- Pattern: Descriptive plural or functional names
|
||||
- Examples: `components/`, `hooks/`, `lib/`, `types/`, `decorators/`, `strategies/`
|
||||
|
||||
**Module sub-directories:**
|
||||
- Pattern: `guards/`, `strategies/`, `dto/` (lowercase)
|
||||
- Convention: Consistent across all modules
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
### New Feature
|
||||
|
||||
**Backend:**
|
||||
1. Create `server/src/modules/[feature]/` directory
|
||||
2. Create files:
|
||||
- `[feature].module.ts` - NestJS module, imports dependencies
|
||||
- `[feature].controller.ts` - HTTP endpoints
|
||||
- `[feature].service.ts` - Business logic
|
||||
- `[feature].gateway.ts` (optional) - WebSocket if real-time needed
|
||||
- `dto/` folder with DTOs
|
||||
3. Import module in `server/src/app.module.ts`
|
||||
|
||||
**Frontend:**
|
||||
1. Create `client/src/features/[feature]/` directory
|
||||
2. Create sub-folders:
|
||||
- `components/` - React components
|
||||
- `hooks/` - Custom hooks (if state needed)
|
||||
- `index.ts` - Barrel export
|
||||
3. Add routes in `client/src/App.tsx`
|
||||
|
||||
**Database (if adding entities):**
|
||||
1. Add model to `server/prisma/schema.prisma`
|
||||
2. Run: `cd server && npm run db:migrate:dev`
|
||||
3. Name migration descriptively: "add_new_feature_table"
|
||||
4. Seeds automatically regenerate Prisma client
|
||||
|
||||
### New Component/Module
|
||||
|
||||
**React Component:**
|
||||
- Location: `client/src/features/[feature]/components/[name].tsx`
|
||||
- Pattern: Named export + default export
|
||||
- Import shared types from `client/src/shared/types/index.ts`
|
||||
- Use API client from `client/src/shared/lib/api.ts`
|
||||
|
||||
**NestJS Service:**
|
||||
- Location: `server/src/modules/[feature]/[name].service.ts`
|
||||
- Pattern: @Injectable() class with public methods
|
||||
- Inject PrismaService via constructor
|
||||
- Keep business logic in service, HTTP concern in controller
|
||||
|
||||
**WebSocket Gateway:**
|
||||
- Location: `server/src/modules/[feature]/[name].gateway.ts`
|
||||
- Pattern: @WebSocketGateway() @Injectable() class
|
||||
- Authenticate in handleConnection()
|
||||
- Emit broadcasts to rooms via socket.server.to(room).emit()
|
||||
|
||||
### Utilities
|
||||
|
||||
**Shared Frontend Helpers:**
|
||||
- Location: `client/src/shared/lib/utils.ts`
|
||||
- Pattern: Exported functions (no classes)
|
||||
- Use for date formatting, type checks, calculations
|
||||
|
||||
**Shared Backend Helpers:**
|
||||
- Location: `server/src/common/` (create subdirectory if needed)
|
||||
- Pattern: Injectable services or utility functions
|
||||
- Example: CommonService for shared operations
|
||||
|
||||
## Special Directories
|
||||
|
||||
**`server/prisma/migrations/`**
|
||||
- Purpose: Version control for database schema
|
||||
- Generated: Auto-created by `npm run db:migrate:dev`
|
||||
- Committed: YES (must be in git for reproducible deployments)
|
||||
- Manual edits: Rarely needed, but can fix SQL if migration is broken
|
||||
- Pattern: Timestamp-prefixed folders with `.sql` files inside
|
||||
|
||||
**`server/src/generated/`**
|
||||
- Purpose: Prisma-generated types and client
|
||||
- Generated: Auto-created by `npm run db:generate`
|
||||
- Committed: NO (regenerated on install)
|
||||
- Manual edits: DO NOT EDIT — regenerate instead
|
||||
|
||||
**`client/public/data/`**
|
||||
- Purpose: Static JSON files (currently unused, equipment in DB)
|
||||
- Generated: NO (manually added if needed)
|
||||
- Committed: YES
|
||||
- Pattern: Only for truly static reference data
|
||||
|
||||
**`server/dist/` and `client/dist/`**
|
||||
- Purpose: Compiled/bundled output
|
||||
- Generated: YES (`npm run build`)
|
||||
- Committed: NO (.gitignore)
|
||||
- Pattern: Build artifacts, environment-specific
|
||||
|
||||
**`server/node_modules/` and `client/node_modules/`**
|
||||
- Purpose: Installed dependencies
|
||||
- Generated: YES (`npm install`)
|
||||
- Committed: NO (.gitignore)
|
||||
- Pattern: Use package-lock.json for reproducibility
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-04-27*
|
||||
340
.planning/codebase/TESTING.md
Normal file
340
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Test Framework Status
|
||||
|
||||
**Current State:** **Minimal test coverage - testing infrastructure in place but not actively used**
|
||||
|
||||
### Jest (Server)
|
||||
|
||||
**Runner:**
|
||||
- Jest 30.x
|
||||
- Config: Inline in `server/package.json` (lines 88-104)
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
cd server
|
||||
npm test # Run all unit tests (seeks *.spec.ts files)
|
||||
npm run test:watch # Watch mode for development
|
||||
npm run test:cov # Generate coverage report
|
||||
npm run test:debug # Debug mode with breakpoints
|
||||
npm run test:e2e # Run end-to-end tests (config: test/jest-e2e.json)
|
||||
```
|
||||
|
||||
**Configuration Details:**
|
||||
```json
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": { "^.+\\.(t|j)s$": "ts-jest" },
|
||||
"collectCoverageFrom": ["**/*.(t|j)s"],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
```
|
||||
|
||||
**Testing Libraries:**
|
||||
- Jest 30.x (test runner)
|
||||
- ts-jest (TypeScript support)
|
||||
- @nestjs/testing (NestJS module testing utilities)
|
||||
- supertest (HTTP request testing)
|
||||
|
||||
### Vitest (Client)
|
||||
|
||||
**Status:** Not installed. No vitest config present in `client/package.json`
|
||||
|
||||
**Implications:** Client has zero automated test infrastructure. Any future testing would require adding vitest and configuring it.
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Server:**
|
||||
- Location: Unit tests alongside source in `src/`, E2E tests in `test/`
|
||||
- Naming: `*.spec.ts` for unit tests, `*.e2e-spec.ts` for integration tests
|
||||
- Example: No unit test files currently exist in `server/src/`
|
||||
|
||||
**Current Test Suite:**
|
||||
- Location: `server/test/app.e2e-spec.ts`
|
||||
- Scope: Single basic integration test for app startup
|
||||
- Status: Placeholder test (checks 'Hello World!' endpoint that doesn't exist)
|
||||
|
||||
**Client:**
|
||||
- No test files present
|
||||
- No test directory structure
|
||||
|
||||
## Test Structure (Server)
|
||||
|
||||
**E2E Test Example** (`server/test/app.e2e-spec.ts`):
|
||||
```typescript
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Patterns:**
|
||||
- `describe()` for test suites
|
||||
- `it()` for individual tests (or `test()`)
|
||||
- `beforeEach()` for setup (module initialization)
|
||||
- Async/await for async operations
|
||||
- Chained `.expect()` assertions with supertest
|
||||
|
||||
## Mocking
|
||||
|
||||
### Server (Jest/NestJS)
|
||||
|
||||
**Framework:** @nestjs/testing provides test module builder
|
||||
|
||||
**Pattern Observed:**
|
||||
```typescript
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule], // Or specific module imports
|
||||
}).compile();
|
||||
|
||||
const service = moduleFixture.get<ServiceName>(ServiceName);
|
||||
```
|
||||
|
||||
**What to Mock:**
|
||||
- Database (Prisma) - would use mock provider
|
||||
- External APIs - would use jest.mock()
|
||||
- Services with dependencies - inject via module builder
|
||||
|
||||
**What NOT to Mock:**
|
||||
- NestJS core decorators
|
||||
- Guards/Middleware (test through integration)
|
||||
- Database schema (use test database)
|
||||
|
||||
### Client
|
||||
|
||||
**No mocking patterns established yet** - testing infrastructure not present.
|
||||
|
||||
Would require:
|
||||
- Vitest for runner
|
||||
- `@testing-library/react` for component testing
|
||||
- Mock fetch/axios calls
|
||||
- Mock WebSocket connections
|
||||
|
||||
## Fixtures and Factories
|
||||
|
||||
**Server:**
|
||||
|
||||
**Test Data Approach:** Not fully established, but patterns available:
|
||||
- Prisma seed scripts exist in `server/prisma/`:
|
||||
- `seed.ts` - Basic seed
|
||||
- `seed-equipment.ts` - 5,482 equipment items
|
||||
- `seed-feats.ts` - Feat database
|
||||
- Could be reused for test fixtures
|
||||
|
||||
**Location:** Would be in test directory or integrated with jest setup
|
||||
|
||||
**No factory pattern observed** - tests would need to build objects inline or create helper functions
|
||||
|
||||
**Client:**
|
||||
|
||||
**Not applicable** - no test infrastructure
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:** None enforced (no CI/CD pipeline checking coverage)
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
cd server
|
||||
npm run test:cov
|
||||
# Generates: server/coverage/
|
||||
```
|
||||
|
||||
**Current Coverage:** Unknown (no tests running)
|
||||
|
||||
**Gaps:** **Entire codebase untested**
|
||||
- Auth module: No unit tests (`server/src/modules/auth/`)
|
||||
- Characters module: No unit tests (`server/src/modules/characters/`)
|
||||
- Battle module: No unit tests (`server/src/modules/battle/`)
|
||||
- WebSocket gateway: No tests
|
||||
- Guards/Decorators: No tests
|
||||
- Client components: No tests
|
||||
- Client hooks: No tests
|
||||
- State management (Zustand): No tests
|
||||
|
||||
## Test Types
|
||||
|
||||
### Unit Tests
|
||||
|
||||
**Not currently used**
|
||||
|
||||
**When implemented, pattern would be:**
|
||||
- Test individual services in isolation
|
||||
- Mock Prisma client
|
||||
- Location: `server/src/modules/[module]/[module].service.spec.ts`
|
||||
- Example test structure (not yet used):
|
||||
```typescript
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
let prisma: PrismaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [AuthService, { provide: PrismaService, useValue: mockPrisma }],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should register user', async () => {
|
||||
// Test implementation
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Single example:** `server/test/app.e2e-spec.ts`
|
||||
|
||||
**Scope:**
|
||||
- Tests full HTTP request/response cycle
|
||||
- Uses real app instance
|
||||
- Uses Test.createTestingModule() to create app
|
||||
|
||||
**Pattern:**
|
||||
```typescript
|
||||
return request(app.getHttpServer())
|
||||
.post('/auth/login')
|
||||
.send({ identifier: 'user', password: 'pass' })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.token).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
**Would need:** Database test fixtures, cleanup between tests
|
||||
|
||||
### E2E Tests
|
||||
|
||||
**Config:** `server/test/jest-e2e.json`
|
||||
|
||||
**Difference from Integration:**
|
||||
- Run against deployed/separate server
|
||||
- Test user workflows end-to-end
|
||||
- Full database setup/teardown
|
||||
|
||||
**Currently:** Not implemented beyond placeholder
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Async Testing
|
||||
|
||||
**Server (Jest):**
|
||||
```typescript
|
||||
it('should load data', async () => {
|
||||
const result = await service.loadData();
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
- async/await preferred
|
||||
- Supertest chainable: `request().post().send().expect()`
|
||||
|
||||
**Client (not yet implemented):**
|
||||
Would use:
|
||||
```typescript
|
||||
const { result } = await renderHook(() => useCharacterSocket());
|
||||
await waitFor(() => expect(result.current.socket).toBeDefined());
|
||||
```
|
||||
|
||||
### Error Testing
|
||||
|
||||
**Not yet implemented, but pattern would be:**
|
||||
|
||||
**Server:**
|
||||
```typescript
|
||||
it('should throw NotFoundException', async () => {
|
||||
await expect(service.getCharacter('invalid-id'))
|
||||
.rejects
|
||||
.toThrow(NotFoundException);
|
||||
});
|
||||
```
|
||||
|
||||
**Actual exception handling verified through:**
|
||||
- Controllers catch exceptions
|
||||
- Guards handle auth failures
|
||||
- Integration tests verify HTTP status codes
|
||||
|
||||
## Critical Test Gaps
|
||||
|
||||
**High Priority (Authentication):**
|
||||
- AuthService.register() - conflicts, hashing, JWT generation
|
||||
- AuthService.login() - invalid credentials, token generation
|
||||
- JWT guard - token validation, public routes
|
||||
- File: `server/src/modules/auth/auth.service.ts` (lines 20-104)
|
||||
|
||||
**High Priority (Character Management):**
|
||||
- CharactersService access control (checkCampaignAccess, checkCharacterAccess)
|
||||
- HP updates and state sync via WebSocket
|
||||
- Condition application (dying, wounded, doomed mechanics)
|
||||
- Item management (equip, invest, notes)
|
||||
- File: `server/src/modules/characters/characters.service.ts`
|
||||
|
||||
**High Priority (Real-time Sync):**
|
||||
- CharactersGateway connection authentication
|
||||
- WebSocket message broadcasting
|
||||
- Concurrent client updates
|
||||
- File: `server/src/modules/characters/characters.gateway.ts`
|
||||
|
||||
**Medium Priority (Client UI):**
|
||||
- HpControl component (damage, heal, direct modes)
|
||||
- AddConditionModal (search, filtering, validation)
|
||||
- Character import from Pathbuilder JSON
|
||||
- File: `client/src/features/characters/components/`
|
||||
|
||||
**Medium Priority (Validation):**
|
||||
- DTO validation (CreateCharacterDto, LoginDto)
|
||||
- Prisma query results
|
||||
- File: `server/src/modules/*/dto/`
|
||||
|
||||
## Recommendations for Implementation
|
||||
|
||||
**Phase 1 (Auth):**
|
||||
1. Write AuthService unit tests with mocked Prisma
|
||||
2. Write JwtAuthGuard tests
|
||||
3. Write integration tests for login/register endpoints
|
||||
|
||||
**Phase 2 (Character CRUD):**
|
||||
1. Test CharactersService access control
|
||||
2. Test HP update logic
|
||||
3. Test condition application
|
||||
|
||||
**Phase 3 (WebSocket):**
|
||||
1. Mock socket.io for gateway tests
|
||||
2. Test connection/disconnect
|
||||
3. Test broadcast patterns
|
||||
|
||||
**Phase 4 (Client):**
|
||||
1. Setup vitest
|
||||
2. Test HpControl component
|
||||
3. Test modal components
|
||||
4. Test Zustand store
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-04-27*
|
||||
Reference in New Issue
Block a user