Imported from MikhailKayumov/mesh-hub (
server/AGENTS.md). Install upstream withnpx skills add MikhailKayumov/mesh-hub --skill server. Copyright stays with the author.
AGENTS.md — MeshHub Server
This file is written for LLM coding agents. It describes the architecture, conventions, and how to safely extend the codebase.
Project Overview
NestJS 11 REST API backend for the MeshHub platform — a 3D model sharing service. Provides JWT-based auth with DB sessions, user management, 3D model file uploads, and reference data (categories, CG software).
- Entry point:
src/main.ts→src/app.bootstrap.ts - Root module:
src/app.module.ts - Database: PostgreSQL with TypeORM 0.3 (4 schemas:
auth,users,resources,model_3d) - Global prefix:
/api(configured viaAPP_GLOBAL_PREFIXenv var) - Swagger UI:
/swagger
Module Map
src/
├── app.module.ts # Root module — imports all feature modules
├── app.bootstrap.ts # Application factory (middleware, guards, pipes, swagger)
├── main.ts # Process entry, calls AppBootstrap
│
├── modules/
│ ├── auth/ # JWT auth, session CRUD, signup/login/logout
│ ├── user/ # Profile, avatar upload, password change/reset
│ ├── models-3d/ # 3D model CRUD, file upload, thumbnail
│ ├── files/ # File storage abstraction (filesystem strategy)
│ ├── resources/ # Categories and CG software reference data
│ ├── notifications/ # Email sending via Yandex SMTP
│ ├── logger/ # Winston logger service
│ └── config/ # Environment-based configuration (global)
│
├── database/
│ ├── entities/ # TypeORM entity classes (one file per entity)
│ ├── migrations/ # TypeORM migrations (grouped by schema)
│ ├── seeds/ # Seed scripts (roles, categories)
│ └── init/ # Schema creation scripts (run before migrations)
│
├── guards/
│ ├── auth/jwt-auth.guard.ts # Global JWT guard (reads cookie)
│ └── throttler-behind-proxy.guard.ts
│
├── interceptors/
│ ├── logging.interceptor.ts # Request/response logging
│ └── cookies.interceptor.ts # Sets JWT cookies on response
│
├── decorators/
│ ├── auth/ # @Public(), @Refresh(), @Roles(), @AuthGuard()
│ ├── user/ # @User(), @OptionalUser()
│ ├── pagination/ # @PaginatedRequest(), @PaginatedResponse()
│ └── validation/ # @Validate(IsNumberOrStringDecorator)
│
├── pipes/
│ ├── file-size-validator.pipe.ts
│ ├── file-type-validator.pipe.ts
│ └── file-extension-validator.pipe.ts
│
├── exceptions/
│ └── app-http.exception.ts # Unified error response shape
│
├── constants/
│ ├── roles.ts # UserRoles enum
│ ├── files.ts # File size limits and MIME type lists
│ ├── regexp.ts # Password and phone regexes
│ └── validation-error-messages.ts # Russian error message strings
│
├── types/
│ └── index.d.ts # Express Request augmentation (session, jwtPayload)
│
└── utils/
├── index.ts # isNil()
├── user-role.helper.ts # hasRole(), hasSomeRoles(), hasEveryRoles()
└── format-bytes.ts # Byte formatting utility
Architecture Patterns
Repository Pattern
Every entity has a dedicated repository class in its module's repositories/ folder. Repositories extend TypeORM's Repository<Entity> and are injected via @InjectRepository(). Direct EntityManager queries go through the repository, not services.
Mapper Pattern
Dedicated mapper classes (in mappers/) convert entities to DTOs and DTOs to entities. Services call mappers before returning responses. Mappers are plain classes with static or instance methods — not NestJS providers.
Strategy Pattern (File Storage)
FileStorageModule exports a FileStorageService backed by an IFileStorageStrategy implementation. Currently the only strategy is FsFilesStrategy (local filesystem). To add a new storage backend (e.g., S3), implement the IFileStorageStrategy interface and swap the provider.
Pagination
All list endpoints use PaginationDto (query params: skip, size, sort[]) and return PaginationResponseDto<T> (data[], skip, size, sort[], totalCount, hasMore). Use the @PaginatedRequest() decorator to extract pagination from the query, and @PaginatedResponse(DtoClass) to annotate the Swagger response.
Sort format: +fieldName (ASC) or -fieldName (DESC). The pagination decorator parses these into PaginationDtoSortItem[].
Global Providers (applied to every request)
ThrottlerBehindProxyGuard— rate limiting, extracts real IP from X-Forwarded-ForJwtAuthGuard— validates JWT from cookie, attachessessionandjwtPayloadtoreqCookiesInterceptor— sets/clears the JWT cookie on responses
Error Handling
Throw AppHttpException (from src/exceptions/app-http.exception.ts) for application errors. It produces a structured JSON body { error, type, status, message, data }. The global ValidationPipe also wraps class-validator errors into AppHttpException with type: 'ValidationError'.
Naming Conventions
| Thing | Convention | Example |
|---|---|---|
| Module folder | kebab-case |
models-3d/ |
| Entity file | kebab-case.entity.ts |
model-3d-file.entity.ts |
| Entity class | PascalCase + Entity suffix |
Model3dFileEntity |
| DTO file | kebab-case.[qualifier].dto.ts |
model-3d.update.request.dto.ts |
| DTO class | PascalCase |
Model3dUpdateRequestDto |
| Controller | one per module (sometimes admin.controller.ts + [name].controller.ts) |
model-3d.controller.ts |
| Service | [name].service.ts |
model-3d.service.ts |
| Repository | [name].repository.ts |
model-3d.repository.ts |
| Mapper | [name].mapper.ts |
model-3d.mapper.ts |
| Guard | [name].guard.ts |
jwt-auth.guard.ts |
| Interceptor | [name].interceptor.ts |
logging.interceptor.ts |
| Migration | {timestamp}-{PascalCaseName}.ts |
1703017999243-Init.ts |
| DB schema | snake_case |
model_3d |
| DB table | snake_case |
model_3d_file |
| DB column | snake_case |
user_meta_id |
| Path aliases | @/ maps to src/ |
import { X } from '@/modules/...' |
Auth Conventions
- Every endpoint is authenticated by default via the global
JwtAuthGuard. - Use
@Public()to opt a route out of authentication (reads refresh or no token). - Use
@Refresh()to require the refresh token instead of the access token (used onPOST /auth/refresh). - Use
@Roles(UserRoles.Admin)to restrict an endpoint to specific roles. - Inject the authenticated user with
@User() user: UserEntityin controller params. - Inject an optional user (for public endpoints that also support logged-in context) with
@OptionalUser() user: UserEntity | undefined. - JWT is transmitted only via HttpOnly cookie (name controlled by
AUTH_JWT_COOKIE_NAMEenv var). Do not use Authorization header.
Database Conventions
- All entities extend one of:
GuidIdEntityBase(UUID PK) orIntIdBaseEntity(serial int PK). - Both base classes include
createdAt,updatedAt,deletedAtcolumns (soft-delete support). - Entities are organized in
src/database/entities/grouped by schema (user/,session/,models-3d/,resources/). - Table names and schema names are defined as string constants in
src/database/constants.ts— always reference these constants in@Entity(),@JoinTable(), etc. - Migrations live in
src/database/migrations/grouped by schema subfolder. - To generate a new migration:
npm run migration:generate -- --schema=<schema> --name=<MigrationName>(Windows: usemigration:generate:win).
How to Add a New Feature Module
-
Create
src/modules/<name>/with:<name>.module.ts—@Module({ imports, controllers, providers, exports })controllers/<name>.controller.tsservices/<name>.service.tsrepositories/<name>.repository.tsmappers/<name>.mapper.tsdto/<name>.response.dto.ts,dto/<name>.request.dto.ts
-
Add the entity in
src/database/entities/<schema>/<name>.entity.ts. ExtendGuidIdEntityBaseorIntIdBaseEntity. Add entity constants insrc/database/constants.ts. -
Import
TypeOrmModule.forFeature([EntityClass])in the feature module. -
Generate a migration:
npm run migration:generate -- --schema=<schema> --name=Add<Name>. -
Import the feature module in
src/app.module.ts.
How to Add a New Endpoint
- Add a method to the controller with the appropriate HTTP decorator (
@Get,@Post,@Patch,@Delete). - Apply auth decorators as needed:
@Public(),@Roles(...),@AuthGuard(). - Use
@User()or@OptionalUser()to access the authenticated user. - For paginated lists: use
@PaginatedRequest() pagination: PaginationDtoand returnPaginationResponseDto<T>. - Add the corresponding service method and (if needed) repository query.
- Add input DTO with class-validator decorators; the global
ValidationPipehandles transformation and validation automatically.
How to Add a New Migration
# Linux/Mac
npm run migration:generate -- --schema=<schema_name> --name=<MigrationName>
# Windows
npm run migration:generate:win --schema=<schema_name> --name=<MigrationName>
# Apply
npm run migration:run
# Revert last
npm run migration:revert
Migration files are generated in src/database/migrations/<schema>/.
Environment Configuration
All configuration is centralized in src/modules/config/config.service.ts. Access config anywhere by injecting ConfigService. Do not use process.env directly in feature code — always go through ConfigService.
See docs/configuration.md for the full environment variable reference.
File Storage
The FileStorageModule is globally available. Inject FileStorageService to save/delete files. The service delegates to IFileStorageStrategy — currently FsFilesStrategy (local disk under server/files/).
- Avatars:
files/avatars/<userId>.<ext>(max 1 MB, image types only) - 3D models:
files/models-3d/<modelId>/<filename>(max 3 GB,.glb/.gltf) - Thumbnails:
files/models-3d/<modelId>/thumbnail.png
See docs/file-storage.md for full details.
Logging
Inject AppLogger (from src/modules/logger/logger.service.ts) in any provider for structured logging. It wraps Winston with console and optional file transports. Log level is controlled by LOGS_LEVEL env var.
Testing
- Unit tests co-located with source files as
*.spec.ts. - E2E tests in
test/using Supertest. - Jest is configured in
package.json(jestkey).rootDir: src, transforms.tsviats-jest. - Path alias
@/is resolved viatsconfig-pathsin both Jest and ts-node.
Key Dependencies
| Package | Purpose |
|---|---|
@nestjs/jwt |
JWT sign/verify |
@nestjs/typeorm |
TypeORM integration |
@nestjs/swagger |
OpenAPI doc generation |
@nestjs/schedule |
Cron jobs (session cleanup) |
@nestjs/throttler |
Rate limiting |
@nestjs/bull |
Job queues (Bull/Redis) |
@nestjs-modules/mailer |
Email sending |
class-validator + class-transformer |
DTO validation and transformation |
cookie-parser |
Cookie parsing for JWT transport |
typeorm |
ORM + migrations |
pg |
PostgreSQL driver |
winston |
Logging |
date-fns |
Date utilities |