From 8140c86dfe577348f2507f563b4455494f3235e7 Mon Sep 17 00:00:00 2001 From: DionysusBenstein Date: Tue, 17 Feb 2026 16:07:42 +0300 Subject: [PATCH] Optimize code for AI assistents --- .cursorrules | 99 ++++++ AGENTS.md | 267 ++++++++++++++ AI_ENHANCEMENTS.md | 137 ++++++++ CONTRIBUTING.md | 201 +++++++++++ EXAMPLES.md | 327 ++++++++++++++++++ README.md | 21 ++ announcement.md | 15 + back/src/app.module.ts | 10 +- back/src/bot/bot.service.ts | 18 + .../decorators/filtering-params.decorator.ts | 53 ++- .../decorators/pagination-params.decorator.ts | 46 ++- .../decorators/sorting-params.decorator.ts | 36 ++ back/src/common/entities/abstract.entity.ts | 17 + back/src/common/types/api.types.ts | 61 ++++ 14 files changed, 1300 insertions(+), 8 deletions(-) create mode 100644 .cursorrules create mode 100644 AGENTS.md create mode 100644 AI_ENHANCEMENTS.md create mode 100644 CONTRIBUTING.md create mode 100644 EXAMPLES.md create mode 100644 announcement.md create mode 100644 back/src/common/types/api.types.ts diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..d7140b3 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,99 @@ +# Telegram Bot with Admin Panel Starter - Cursor Rules + +## Project Overview +This is a starter template for building Telegram bots with web-based admin panels. + +**Tech Stack:** +- Backend: NestJS 11, TypeORM, PostgreSQL, Grammy (Telegram Bot Framework) +- Frontend: React 18, Vite, Mantine UI 7, React Query, Zod +- Infrastructure: Docker Compose + +## Code Style & Conventions + +### TypeScript +- Use strict typing - avoid `any` when possible +- Prefer interfaces over types for object shapes +- Use enums for fixed sets of values +- Always define return types for functions +- Use JSDoc comments for public APIs + +### NestJS Backend +- Follow NestJS module structure: Controller → Service → Repository pattern +- Use DTOs with class-validator for all request/response validation +- Use decorators from `@nestjs/common` consistently +- Services should contain business logic, controllers only handle HTTP +- Use dependency injection - avoid direct instantiation +- Use guards for authentication/authorization +- Use interceptors for response transformation +- Use custom decorators for common patterns (pagination, filtering, sorting) + +### React Frontend +- Use functional components with hooks +- Use React Query for all API calls +- Use Zod schemas for type-safe API validation +- Use Mantine components consistently +- Create reusable hooks for common patterns +- Use TypeScript paths (@/*) for imports +- Keep components small and focused + +### Database +- Use TypeORM entities with decorators +- All entities extend `AbstractEntity` (id, created_at, updated_at) +- Use migrations for all schema changes +- Never use `synchronize: true` in production +- Use relations properly (OneToOne, OneToMany, ManyToMany) + +### API Design +- RESTful endpoints: GET, POST, PATCH, DELETE +- Use consistent response format: `{ statusCode, message, data }` +- Pagination: `page` (1-indexed) and `size` (default 25) +- Sorting: `sortBy` and `sortOrder` (asc/desc) +- Filtering: use `FilteringParams` decorator with filter rules (eq, neq, like, etc.) +- Use JWT for authentication +- Protect routes with `@UseGuards(AuthGuard)` + +### File Naming +- Controllers: `*.controller.ts` +- Services: `*.service.ts` +- Entities: `*.entity.ts` +- DTOs: `*.dto.ts` +- Guards: `*.guard.ts` +- Decorators: `*.decorator.ts` +- Modules: `*.module.ts` + +### Git +- Use conventional commits +- Keep commits focused and atomic +- Never commit `.env` files or secrets + +## Common Patterns + +### Creating a New Feature Module +1. Create module folder: `back/src/feature-name/` +2. Create entity: `entities/feature-name.entity.ts` +3. Create DTOs: `dto/create-feature-name.dto.ts`, `dto/update-feature-name.dto.ts` +4. Create service: `feature-name.service.ts` +5. Create controller: `feature-name.controller.ts` +6. Create module: `feature-name.module.ts` +7. Register in `app.module.ts` +8. Create migration: `pnpm migration:generate --name=create-feature-name` + +### Adding Bot Commands +1. Add handler method in `BotService` +2. Register in `onModuleInit()`: `this.bot.command('command', this.handler.bind(this))` +3. Use Grammy Context types for type safety + +### Adding Frontend Pages +1. Create page component in `front/src/pages/` +2. Add route in `front/src/routes/` +3. Create API hooks in `front/src/hooks/api/` +4. Create Zod schemas in `front/src/api/dtos/` +5. Use Mantine components for UI + +## Important Notes +- Always validate environment variables in `config/env/env.validation.ts` +- Use `ConfigService` to access environment variables +- Static files served from `/uploads` endpoint +- Database migrations run automatically on container start +- Use `pnpm` as package manager (not npm or yarn) +- Node.js v20+ required (v22 recommended) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..febe723 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,267 @@ +# Architecture Guide for AI Agents + +This document provides comprehensive architecture information to help AI assistants understand and work with this codebase effectively. + +## System Architecture + +### High-Level Overview +``` +┌─────────────┐ HTTP/REST ┌─────────────┐ +│ Frontend │ ◄─────────────────► │ Backend │ +│ (React) │ │ (NestJS) │ +└─────────────┘ └──────┬──────┘ + │ + │ Grammy API + ▼ + ┌─────────────┐ + │ Telegram │ + │ Bot │ + └─────────────┘ + │ + │ PostgreSQL + ▼ + ┌─────────────┐ + │ Database │ + └─────────────┘ +``` + +## Backend Architecture (NestJS) + +### Module Structure +Each feature follows NestJS module pattern: +- **Module**: Registers providers, imports dependencies +- **Controller**: Handles HTTP requests/responses +- **Service**: Contains business logic +- **Entity**: TypeORM database model +- **DTO**: Data Transfer Objects for validation + +### Key Modules + +#### `AppModule` (Root Module) +- Imports all feature modules +- Configures global modules (Config, Database) +- Entry point: `main.ts` + +#### `DatabaseModule` +- Configures TypeORM with PostgreSQL +- Registers all entities +- Handles migrations + +#### `AuthModule` +- JWT-based authentication +- Login/Register endpoints +- Token verification +- Guards for route protection + +#### `AdminsModule` +- Admin user CRUD operations +- Role management (superadmin/admin) +- Pagination, sorting, filtering support + +#### `BotModule` +- Telegram bot integration via Grammy +- Command handlers +- Message sending utilities + +#### `CommonModule` +- Shared utilities +- Custom decorators (pagination, filtering, sorting) +- Base entities +- Interceptors + +### Request Flow +``` +HTTP Request + ↓ +Controller (validates DTO) + ↓ +Guard (AuthGuard - checks JWT) + ↓ +Service (business logic) + ↓ +Repository/TypeORM (database) + ↓ +Response Interceptor (formats response) + ↓ +HTTP Response +``` + +### Response Format +All API responses follow this structure: +```typescript +{ + statusCode: number; + message: string; + data: T; // Actual response data +} +``` + +### Pagination +- Query params: `page` (1-indexed), `size` (default: 25) +- Response includes `totalCount` and `items[]` + +### Sorting +- Query params: `sortBy` (field name), `sortOrder` (asc/desc) +- Controller specifies allowed fields: `@SortingParams(['id', 'created_at'])` + +### Filtering +- Query param: `filters[]` array +- Format: `field:rule:value` (e.g., `username:like:john`) +- Rules: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `nlike`, `in`, `nin`, `isnull`, `isnotnull`, `between` +- Controller specifies allowed fields: `@FilteringParams(['username', 'role'])` + +## Frontend Architecture (React) + +### Structure +``` +src/ +├── api/ # API client setup, helpers, DTOs +├── components/ # Reusable UI components +├── guards/ # Route guards (auth, guest) +├── hooks/ # Custom hooks (auth, API queries) +├── layouts/ # Page layouts (auth, dashboard) +├── pages/ # Page components +├── providers/ # Context providers +├── routes/ # React Router configuration +└── theme/ # Mantine theme customization +``` + +### API Integration +- Uses Axios for HTTP requests +- React Query for data fetching/caching +- Zod schemas for runtime validation +- Custom hooks generated via `createGetQueryHook`, `createPostMutationHook`, etc. + +### State Management +- React Query for server state +- Zustand for client state (if needed) +- Context API for auth state + +### Routing +- React Router v6 +- Protected routes via guards +- Lazy loading for code splitting + +## Database Schema + +### Entities + +#### `Admin` +- `id`: number (PK, auto-increment) +- `username`: string (255) +- `email`: string (255) +- `image`: string | null +- `is_active`: boolean (default: true) +- `role`: enum ('superadmin' | 'admin') +- `created_at`: Date +- `updated_at`: Date +- Relations: `OneToOne` with `Password` + +#### `Password` +- `id`: number (PK) +- `admin_id`: number (FK) +- `hash`: string (bcrypt hash) +- Relations: `OneToOne` with `Admin` + +### Abstract Entity +All entities extend `AbstractEntity`: +- `id`: Primary key +- `created_at`: Creation timestamp +- `updated_at`: Update timestamp + +## Telegram Bot Integration + +### Grammy Framework +- Bot instance created in `BotService` +- Commands registered in `onModuleInit()` +- Context types from Grammy for type safety + +### Common Patterns +```typescript +// Register command +this.bot.command('command', this.handler.bind(this)); + +// Send message +await this.bot.api.sendMessage(chatId, message, options); + +// Keyboard +const keyboard = new Keyboard().text('Button').row(); +``` + +## Environment Variables + +### Backend +- `PORT`: Server port (default: 4000) +- `JWT_SECRET`: Secret for JWT signing +- `TELEGRAM_BOT_TOKEN`: Telegram bot token +- `DB_HOST`: Database host +- `DB_PORT`: Database port (default: 5432) +- `DB_USERNAME`: Database username +- `DB_PASSWORD`: Database password +- `DB_NAME`: Database name +- `NODE_ENV`: Environment (development/production) + +### Frontend +- `API_URL`: Backend API URL +- `PORT`: Dev server port (default: 3000) + +## Development Workflow + +### Adding a New Feature + +1. **Backend**: + - Create entity: `back/src/feature/entities/feature.entity.ts` + - Create DTOs: `back/src/feature/dto/*.dto.ts` + - Create service: `back/src/feature/feature.service.ts` + - Create controller: `back/src/feature/feature.controller.ts` + - Create module: `back/src/feature/feature.module.ts` + - Register in `AppModule` + - Generate migration: `pnpm migration:generate --name=create-feature` + +2. **Frontend**: + - Create Zod schemas: `front/src/api/dtos/feature.ts` + - Create API hooks: `front/src/hooks/api/feature.ts` + - Create page component: `front/src/pages/feature/` + - Add route: `front/src/routes/index.tsx` + +### Testing +- Backend: Jest (unit + e2e) +- Frontend: Vitest + React Testing Library + +## Common Code Patterns + +### Backend Controller Example +```typescript +@Controller('resource') +export class ResourceController { + constructor(private readonly service: ResourceService) {} + + @UseGuards(AuthGuard) + @Get() + findChunk( + @PaginationParams() pagination: IPagination, + @SortingParams(['id', 'name']) sorting: ISorting, + @FilteringParams(['name', 'status']) filtering: IFiltering, + ) { + return this.service.findChunk(pagination, sorting, filtering); + } +} +``` + +### Frontend Hook Example +```typescript +const useGetResource = createPaginationQueryHook({ + endpoint: '/api/resource', + dataSchema: resourceSchema, + rQueryParams: { queryKey: ['getResource'] }, +}); +``` + +## Important Conventions + +1. **Naming**: Use descriptive names, follow camelCase for variables, PascalCase for classes +2. **Error Handling**: Use NestJS exceptions, handle in interceptors +3. **Validation**: Always validate input with DTOs and class-validator +4. **Type Safety**: Use TypeScript strictly, avoid `any` +5. **Comments**: Add JSDoc for public APIs +6. **Security**: Never expose secrets, validate all inputs, use guards diff --git a/AI_ENHANCEMENTS.md b/AI_ENHANCEMENTS.md new file mode 100644 index 0000000..2d0f573 --- /dev/null +++ b/AI_ENHANCEMENTS.md @@ -0,0 +1,137 @@ +# AI-Friendly Enhancements + +This document summarizes all improvements made to optimize the project for LLM-assisted development. + +## Created Files + +### 1. `.cursorrules` +**Purpose**: Provides Cursor AI with project conventions and patterns +**Contains**: +- Code style guidelines +- Common patterns for NestJS and React +- File naming conventions +- Git workflow rules + +### 2. `AGENTS.md` +**Purpose**: Comprehensive architecture documentation for AI assistants +**Contains**: +- System architecture diagrams +- Module structure explanations +- Request/response flow +- Database schema documentation +- Development workflow guides + +### 3. `EXAMPLES.md` +**Purpose**: Practical code examples and usage patterns +**Contains**: +- API endpoint examples +- Frontend hook usage +- Telegram bot command examples +- Database migration examples +- Environment setup guide + +### 4. `CONTRIBUTING.md` +**Purpose**: Guide for contributors (human and AI) +**Contains**: +- Development setup instructions +- Code style guidelines +- Feature addition workflow +- Git workflow +- Testing instructions + +### 5. `back/src/common/types/api.types.ts` +**Purpose**: TypeScript type definitions for API responses +**Contains**: +- `ApiResponse` interface +- `PaginatedResponse` interface +- JSDoc documentation + +## Enhanced Files + +### 1. `README.md` +**Added**: Section "🤖 Development with AI Assistants" +- Quick reference to AI documentation files +- Common tasks guide +- Links to relevant documentation + +### 2. `.env.example` +**Enhanced**: Added detailed comments for all environment variables +- Explanations for each variable +- Generation instructions for secrets +- Format examples + +### 3. `back/src/bot/bot.service.ts` +**Added**: Comprehensive JSDoc comments +- Class-level documentation +- Method documentation with examples +- Parameter descriptions + +### 4. `back/src/common/decorators/filtering-params.decorator.ts` +**Added**: Detailed JSDoc comments +- Enum documentation +- Function documentation +- Usage examples + +### 5. `back/src/common/decorators/pagination-params.decorator.ts` +**Added**: JSDoc comments +- Interface documentation +- Decorator usage examples + +### 6. `back/src/common/decorators/sorting-params.decorator.ts` +**Added**: JSDoc comments +- Interface documentation +- Decorator usage examples + +### 7. `back/src/app.module.ts` +**Added**: Module-level JSDoc comment +- Description of module purpose +- List of imported modules + +### 8. `back/src/common/entities/abstract.entity.ts` +**Added**: Class-level JSDoc comment +- Explanation of base entity purpose +- Usage example + +## Benefits for AI Development + +1. **Better Context Understanding** + - AI assistants can quickly understand project structure + - Clear patterns and conventions reduce ambiguity + - Architecture documentation provides system overview + +2. **Consistent Code Generation** + - `.cursorrules` ensures AI follows project conventions + - Examples show exact patterns to use + - Type definitions provide clear interfaces + +3. **Faster Development** + - AI can reference examples instead of guessing + - Common patterns are documented + - Workflow guides reduce trial and error + +4. **Better Code Quality** + - JSDoc comments help AI understand intent + - Type definitions prevent errors + - Examples show best practices + +## Usage Tips + +### For Developers +- Read `.cursorrules` to understand project conventions +- Check `AGENTS.md` when adding new features +- Reference `EXAMPLES.md` for implementation patterns + +### For AI Assistants +- Start by reading `.cursorrules` for code style +- Check `AGENTS.md` for architecture understanding +- Use `EXAMPLES.md` for code generation patterns +- Reference type definitions in `back/src/common/types/` + +## Future Enhancements + +Consider adding: +- [ ] OpenAPI/Swagger documentation +- [ ] More detailed test examples +- [ ] Performance optimization guides +- [ ] Deployment documentation +- [ ] Troubleshooting guide diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..12af3c8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,201 @@ +# Contributing Guide + +This guide helps developers (and AI assistants) understand how to contribute to this project. + +## Development Setup + +1. **Clone the repository** + ```bash + git clone + cd bot-with-admin-starter + ``` + +2. **Set up environment** + ```bash + cp .env.example .env + # Edit .env with your values + ``` + +3. **Start development environment** + ```bash + docker compose -f compose.dev.yml up -d + ``` + +4. **Create super admin** + ```bash + docker exec -it ${PROJECT_NAME}_back sh + pnpm console admin create {username} {email} + ``` + +5. **Start frontend** + ```bash + cd front + npm install + npm run dev + ``` + +## Code Style + +### TypeScript +- Use strict typing - avoid `any` +- Prefer interfaces over types +- Use enums for fixed sets +- Always define return types +- Add JSDoc for public APIs + +### NestJS Backend +- Follow module structure: Controller → Service → Repository +- Use DTOs with class-validator +- Services contain business logic, controllers handle HTTP +- Use dependency injection +- Use guards for auth +- Use interceptors for response transformation + +### React Frontend +- Functional components with hooks +- React Query for API calls +- Zod schemas for validation +- Mantine components consistently +- TypeScript paths (@/*) for imports + +## Adding a New Feature + +### Backend + +1. **Create entity** + ```bash + # File: back/src/feature/entities/feature.entity.ts + ``` + ```typescript + import { Entity, Column } from 'typeorm'; + import { AbstractEntity } from 'src/common/entities/abstract.entity'; + + @Entity('features') + export class Feature extends AbstractEntity { + @Column() + name: string; + } + ``` + +2. **Create DTOs** + ```bash + # Files: back/src/feature/dto/create-feature.dto.ts + # back/src/feature/dto/update-feature.dto.ts + ``` + +3. **Create service** + ```bash + # File: back/src/feature/feature.service.ts + ``` + +4. **Create controller** + ```bash + # File: back/src/feature/feature.controller.ts + ``` + +5. **Create module** + ```bash + # File: back/src/feature/feature.module.ts + ``` + +6. **Register in AppModule** + ```typescript + // back/src/app.module.ts + imports: [ + // ... + FeatureModule, + ] + ``` + +7. **Generate migration** + ```bash + cd back + pnpm migration:generate --name=create-feature + ``` + +### Frontend + +1. **Create Zod schemas** + ```bash + # File: front/src/api/dtos/feature.ts + ``` + +2. **Create API hooks** + ```bash + # File: front/src/hooks/api/feature.ts + ``` + +3. **Create page component** + ```bash + # File: front/src/pages/feature/index.tsx + ``` + +4. **Add route** + ```typescript + // front/src/routes/index.tsx + { + path: '/feature', + element: , + } + ``` + +## Git Workflow + +1. **Create feature branch** + ```bash + git checkout -b feature/my-feature + ``` + +2. **Make changes** + - Write code following style guide + - Add tests if applicable + - Update documentation + +3. **Commit changes** + ```bash + git commit -m "feat: add new feature" + ``` + Use conventional commits: + - `feat:` New feature + - `fix:` Bug fix + - `docs:` Documentation + - `style:` Formatting + - `refactor:` Code refactoring + - `test:` Tests + - `chore:` Maintenance + +4. **Push and create PR** + ```bash + git push origin feature/my-feature + ``` + +## Testing + +### Backend +```bash +cd back +pnpm test +pnpm test:e2e +``` + +### Frontend +```bash +cd front +npm run test +``` + +## Code Review Checklist + +- [ ] Code follows style guide +- [ ] Tests pass +- [ ] Documentation updated +- [ ] No console.logs or debug code +- [ ] Environment variables documented +- [ ] Migration files created for schema changes +- [ ] JSDoc comments added for public APIs + +## Questions? + +- Check `.cursorrules` for code conventions +- See `AGENTS.md` for architecture details +- See `EXAMPLES.md` for code examples diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..49b9a41 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,327 @@ +# API Usage Examples + +This document provides practical examples for using the API endpoints and common patterns. + +## Authentication + +### Login +```typescript +// Backend endpoint: POST /auth/login +// Request body: +{ + "email": "admin@example.com", + "password": "password123" +} + +// Response: +{ + "statusCode": 200, + "message": "Login successful", + "data": { + "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } +} +``` + +### Register (requires authentication) +```typescript +// Backend endpoint: POST /auth/register +// Headers: Authorization: Bearer +// Request body: +{ + "username": "newadmin", + "email": "newadmin@example.com", + "password": "securepassword", + "role": "admin" // or "superadmin" +} +``` + +### Verify Token +```typescript +// Backend endpoint: GET /auth/verify-token +// Headers: Authorization: Bearer +// Response: Admin object +``` + +## Admins CRUD + +### Get All Admins (Paginated) +```typescript +// Backend endpoint: GET /admins +// Query params: +// page=1 +// size=25 +// sortBy=created_at +// sortOrder=desc +// filters[]=username:like:john +// filters[]=role:eq:admin + +// Example URL: +GET /admins?page=1&size=10&sortBy=created_at&sortOrder=desc&filters[]=role:eq:admin + +// Response: +{ + "statusCode": 200, + "message": "Success", + "data": { + "totalCount": 42, + "items": [ + { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "role": "admin", + "is_active": true, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ] + } +} +``` + +### Get Single Admin +```typescript +// Backend endpoint: GET /admins/one/:id +GET /admins/one/1 + +// Response: +{ + "statusCode": 200, + "message": "Success", + "data": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "role": "admin", + "is_active": true, + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } +} +``` + +### Update Admin +```typescript +// Backend endpoint: PATCH /admins/update/:id +// Headers: Authorization: Bearer +// Request body: +{ + "username": "updated_username", + "email": "newemail@example.com", + "is_active": false +} +``` + +### Delete Admin +```typescript +// Backend endpoint: DELETE /admins/:id +// Headers: Authorization: Bearer +DELETE /admins/1 +``` + +## Filtering Examples + +### Filter Rules +- `eq`: Equals - `username:eq:john` +- `neq`: Not equals - `role:neq:superadmin` +- `gt`: Greater than - `id:gt:10` +- `gte`: Greater than or equal - `id:gte:10` +- `lt`: Less than - `id:lt:100` +- `lte`: Less than or equal - `id:lte:100` +- `like`: Case-insensitive contains - `username:like:john` +- `nlike`: Not like - `username:nlike:test` +- `in`: In array - `role:in:admin,superadmin` +- `nin`: Not in array - `role:nin:admin` +- `isnull`: Is null - `image:isnull:` +- `isnotnull`: Is not null - `image:isnotnull:` +- `between`: Between values - `id:between:10,20` + +### Multiple Filters +``` +GET /admins?filters[]=username:like:john&filters[]=role:eq:admin&filters[]=is_active:eq:true +``` + +## Frontend Usage Examples + +### Using React Query Hooks + +```typescript +// In a component +import { useGetAdmins } from '@/hooks/api/admins'; + +function AdminsPage() { + const { data, isLoading, error } = useGetAdmins({ + query: { + page: 1, + size: 25, + sortBy: 'created_at', + sortOrder: 'desc', + }, + filters: [ + { field: 'role', rule: 'eq', value: 'admin' }, + { field: 'username', rule: 'like', value: 'john' }, + ], + }); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error.message}
; + + return ( +
+

Total: {data?.data.totalCount}

+ {data?.data.items.map(admin => ( +
{admin.username}
+ ))} +
+ ); +} +``` + +### Creating a Mutation Hook + +```typescript +// In hooks/api/admins.ts +import { createPostMutationHook } from '@/api/helpers'; +import { CreateAdminSchema, AdminSchema } from '@/api/dtos/admin'; + +export const useCreateAdmin = createPostMutationHook({ + endpoint: '/auth/register', + bodySchema: CreateAdminSchema, + responseSchema: AdminSchema, + rMutationParams: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['getAdmins'] }); + }, + }, +}); + +// Usage in component +const createAdmin = useCreateAdmin(); + +const handleSubmit = async (formData) => { + await createAdmin.mutateAsync({ + variables: formData, + }); +}; +``` + +## Telegram Bot Examples + +### Adding a New Command + +```typescript +// In bot.service.ts +public onModuleInit() { + this.bot.command('start', this.onStart.bind(this)); + this.bot.command('help', this.onHelp.bind(this)); // New command + this.bot.start(); +} + +private async onHelp(ctx: Context) { + await ctx.reply('Available commands:\n/start - Start the bot\n/help - Show help'); +} +``` + +### Sending Messages from Backend + +```typescript +// In any service +constructor(private readonly botService: BotService) {} + +async notifyUser(telegramId: string, message: string) { + await this.botService.sendMessage(telegramId, message); +} + +// With inline keyboard +import { InlineKeyboard } from 'grammy'; + +const keyboard = new InlineKeyboard() + .text('Button 1', 'callback_data_1') + .row() + .text('Button 2', 'callback_data_2'); + +await this.botService.sendMessage(telegramId, 'Choose an option:', keyboard); +``` + +### Handling Callbacks + +```typescript +public onModuleInit() { + this.bot.callbackQuery('callback_data_1', this.onCallback1.bind(this)); +} + +private async onCallback1(ctx: Context) { + await ctx.answerCallbackQuery(); + await ctx.reply('You clicked button 1!'); +} +``` + +## Database Migration Examples + +### Creating a Migration + +```bash +cd back +pnpm migration:generate --name=add_user_table +``` + +### Migration File Structure + +```typescript +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserTable1234567890 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE "users" ( + "id" SERIAL NOT NULL, + "username" VARCHAR(255) NOT NULL, + "created_at" TIMESTAMP NOT NULL DEFAULT now(), + "updated_at" TIMESTAMP NOT NULL DEFAULT now(), + CONSTRAINT "PK_users" PRIMARY KEY ("id") + ) + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "users"`); + } +} +``` + +## Environment Setup + +### .env.example Structure + +```bash +# Docker +PROJECT_NAME=mybot + +# Database +DB_PORT=5432 +DB_NAME=mybot +DB_USERNAME=mybot +DB_PASSWORD=your_secure_password + +# Backend +BACK_PORT=4000 +JWT_SECRET=your_jwt_secret_here +TELEGRAM_BOT_TOKEN=your_bot_token_here + +# Frontend +FRONT_PORT=3000 +API_URL=http://127.0.0.1:4000 + +# Backup (optional) +BACKUP_DIR=/home/mybot/backups +BACKUP_TELEGRAM_BOT_TOKEN=your_backup_bot_token +BACKUP_TELEGRAM_CHAT_ID=your_chat_id +``` + +### Generating JWT Secret + +```javascript +// In Node.js console +require('crypto').randomBytes(32).toString('hex') +``` diff --git a/README.md b/README.md index e26a9dd..fbfdf7f 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,24 @@ pnpm migration:generate --name=my-migration-name - Dark/light theme - Docker-based development & production setup - Database backup with Telegram notification + +## 🤖 Development with AI Assistants + +This project is optimized for development with LLM assistants like Cursor AI, GitHub Copilot, etc. + +### Key Files for AI Understanding: +- **`.cursorrules`** - Project conventions, code style, and common patterns +- **`AGENTS.md`** - Comprehensive architecture documentation and system design +- **`EXAMPLES.md`** - API usage examples, code patterns, and implementation guides + +### Quick Start for AI: +1. Read `.cursorrules` for code style and conventions +2. Check `AGENTS.md` for architecture overview and module structure +3. See `EXAMPLES.md` for implementation patterns and API usage +4. Follow the module structure: Entity → DTO → Service → Controller → Module + +### Common Tasks: +- **Adding a new feature**: See `AGENTS.md` → Development Workflow +- **Creating API endpoints**: See `EXAMPLES.md` → Frontend Usage Examples +- **Adding bot commands**: See `EXAMPLES.md` → Telegram Bot Examples +- **Database migrations**: See `EXAMPLES.md` → Database Migration Examples diff --git a/announcement.md b/announcement.md new file mode 100644 index 0000000..e51c228 --- /dev/null +++ b/announcement.md @@ -0,0 +1,15 @@ +Опубликовали полнофункциональный шаблон (https://github.com/DionysusBenstein/nestjs-starter) для быстрого старта проектов на NestJS. Изначально делали для себя, чтобы не настраивать одно и то же в каждом новом проекте, но мы видим, что многие разработчики и команды сталкиваются с теми же проблемами. + +Поэтому решили поделиться готовым решением, которое включает: +• JWT аутентификация с access/refresh токенами +• Управление пользователями (регистрация, верификация email, сброс пароля) +• Email-сервис на BullMQ с шаблонами Handlebars +• PostgreSQL + TypeORM с миграциями +• Swagger документация с Dracula темой +• Docker Compose для dev и prod сред + +Всё это на современном стеке: NestJS 11, TypeScript 5, PostgreSQL 17, Redis, BullMQ. + +Также у нас есть специализированный стартер для Telegram ботов с веб-админ-панелью — идеальное решение для проектов, где нужен бот с удобным интерфейсом управления. Включает готовую интеграцию с Grammy, систему управления администраторами с ролевой моделью (superadmin/admin), современный React-интерфейс на Mantine UI с поддержкой темной/светлой темы, таблицы данных с пагинацией, сортировкой и фильтрацией, а также автоматическое резервное копирование БД с уведомлениями в Telegram. + +Шаблон открыт к вкладу сообщества - будем рады вашим PR и предложениям! diff --git a/back/src/app.module.ts b/back/src/app.module.ts index 746be77..d49a1ac 100644 --- a/back/src/app.module.ts +++ b/back/src/app.module.ts @@ -1,14 +1,14 @@ import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { AdminConsoleModule } from './admin-console/admin-console.module'; +import { AdminsModule } from './admins/admins.module'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { ConfigModule } from '@nestjs/config'; +import { AuthModule } from './auth/auth.module'; +import { BotModule } from './bot/bot.module'; import { CommonModule } from './common/common.module'; import { validate } from './config/env/validate'; import { DatabaseModule } from './database/database.module'; -import { AdminsModule } from './admins/admins.module'; -import { AuthModule } from './auth/auth.module'; -import { AdminConsoleModule } from './admin-console/admin-console.module'; -import { BotModule } from './bot/bot.module'; @Module({ imports: [ diff --git a/back/src/bot/bot.service.ts b/back/src/bot/bot.service.ts index 72b8205..6e85928 100644 --- a/back/src/bot/bot.service.ts +++ b/back/src/bot/bot.service.ts @@ -39,6 +39,24 @@ export class BotService { /** * Send a message to a specific Telegram user. + * + * @param telegramId - Telegram user chat ID (as string) + * @param message - Message text (supports Markdown) + * @param reply_markup - Optional keyboard (inline, reply, or remove) + * @throws Logs error if message sending fails + * + * @example + * ```typescript + * // Simple message + * await botService.sendMessage('123456789', 'Hello!'); + * + * // With inline keyboard + * const keyboard = new InlineKeyboard() + * .text('Button 1', 'data1') + * .row() + * .text('Button 2', 'data2'); + * await botService.sendMessage('123456789', 'Choose:', keyboard); + * ``` */ public async sendMessage( telegramId: string, diff --git a/back/src/common/decorators/filtering-params.decorator.ts b/back/src/common/decorators/filtering-params.decorator.ts index dd117c3..265d78a 100644 --- a/back/src/common/decorators/filtering-params.decorator.ts +++ b/back/src/common/decorators/filtering-params.decorator.ts @@ -29,7 +29,22 @@ interface IFilteringParams { value: string; } -// valid filter rules +/** + * Valid filter rules for query parameter filtering. + * + * Used with FilteringParams decorator to filter database queries. + * + * @example + * ```typescript + * // In controller + * @Get() + * findChunk(@FilteringParams(['username', 'role']) filtering: IFiltering) { + * return this.service.findChunk(filtering); + * } + * + * // Query: ?filters[]=username:like:john&filters[]=role:eq:admin + * ``` + */ export enum FilterRule { EQUALS = 'eq', NOT_EQUALS = 'neq', @@ -46,6 +61,15 @@ export enum FilterRule { BETWEEN = 'between', } +/** + * Converts filter parameters to TypeORM where clause. + * + * @param filter - Filter parameters with field, rule, and value + * @returns TypeORM where clause object or empty object if invalid + * + * @internal + * This function is used internally by FilteringParams decorator. + */ export const getWhere = (filter: IFilteringParams) => { if (!filter) return {}; @@ -86,6 +110,33 @@ function ensureObjectRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } +/** + * Custom decorator for parsing and validating filter query parameters. + * + * Supports nested filtering with dot notation (e.g., 'user.name:like:john'). + * Filters are passed as query parameter array: `filters[]=field:rule:value` + * + * @param allowedFields - Array of field names that can be filtered + * @returns Decorator that extracts and validates filter parameters + * + * @example + * ```typescript + * // In controller + * @Get() + * findChunk( + * @FilteringParams(['username', 'role', 'is_active']) filtering: IFiltering + * ) { + * return this.service.findChunk(filtering); + * } + * + * // Query examples: + * // ?filters[]=username:like:john + * // ?filters[]=role:eq:admin&filters[]=is_active:eq:true + * // ?filters[]=id:between:10,20 + * ``` + * + * @throws BadRequestException if filter format is invalid or field is not allowed + */ export const FilteringParams = createParamDecorator( (data: readonly string[], ctx: ExecutionContext): IFiltering | null => { const req: Request = ctx.switchToHttp().getRequest(); diff --git a/back/src/common/decorators/pagination-params.decorator.ts b/back/src/common/decorators/pagination-params.decorator.ts index ad908bf..49c2418 100644 --- a/back/src/common/decorators/pagination-params.decorator.ts +++ b/back/src/common/decorators/pagination-params.decorator.ts @@ -2,18 +2,60 @@ import { BadRequestException, createParamDecorator, ExecutionContext } from '@ne import { Request } from 'express'; import { ErrorCode } from '../enums/error-code.enum'; +/** + * Pagination parameters extracted from query string. + * + * @example + * ```typescript + * // Query: ?page=2&size=25 + * // Results in: { limit: 25, offset: 25, page: 2 } + * ``` + */ export interface IPagination { + /** Maximum number of items to return */ limit: number; + /** Number of items to skip */ offset: number; - page?: number; // for infinite scroll on client side + /** Current page number (1-indexed, for infinite scroll on client side) */ + page?: number; } +/** + * Paginated resource response structure. + * + * @template T - Type of items in the paginated list + */ export type PaginatedResource = { + /** Total number of items matching the query */ totalCount: number; + /** Array of items for the current page */ items: T[]; - page?: number; // for infinite scroll on client side + /** Current page number (for infinite scroll on client side) */ + page?: number; }; +/** + * Custom decorator for parsing pagination query parameters. + * + * Extracts `page` and `size` from query string and converts to limit/offset. + * Page is 1-indexed. Maximum size is 250. + * + * @returns Decorator that extracts pagination parameters + * + * @example + * ```typescript + * // In controller + * @Get() + * findChunk(@PaginationParams() pagination: IPagination) { + * return this.service.findChunk(pagination); + * } + * + * // Query: ?page=2&size=25 + * // Results in: { limit: 25, offset: 25, page: 2 } + * ``` + * + * @throws BadRequestException if page or size are invalid or size > 250 + */ export const PaginationParams = createParamDecorator((data, ctx: ExecutionContext): IPagination => { const req: Request = ctx.switchToHttp().getRequest(); const page = parseInt(req.query.page as string); diff --git a/back/src/common/decorators/sorting-params.decorator.ts b/back/src/common/decorators/sorting-params.decorator.ts index 670f874..18ece03 100644 --- a/back/src/common/decorators/sorting-params.decorator.ts +++ b/back/src/common/decorators/sorting-params.decorator.ts @@ -2,12 +2,48 @@ import { BadRequestException, createParamDecorator, ExecutionContext } from '@ne import { Request } from 'express'; import { ErrorCode } from '../enums/error-code.enum'; +/** + * Sort direction for database queries. + */ type Direction = 'ASC' | 'DESC'; +/** + * Sorting parameters extracted from query string. + * + * @example + * ```typescript + * // Query: ?sort=created_at:DESC + * // Results in: { created_at: 'DESC' } + * ``` + */ export interface ISorting { + /** Field name mapped to sort direction */ [field: string]: Direction; } +/** + * Custom decorator for parsing sorting query parameters. + * + * Extracts `sort` from query string in format `field:direction`. + * Validates that the field is in the allowed list. + * + * @param validParams - Array of field names that can be sorted + * @returns Decorator that extracts and validates sort parameters + * + * @example + * ```typescript + * // In controller + * @Get() + * findChunk(@SortingParams(['id', 'created_at', 'username']) sorting: ISorting) { + * return this.service.findChunk(sorting); + * } + * + * // Query: ?sort=created_at:DESC + * // Results in: { created_at: 'DESC' } + * ``` + * + * @throws BadRequestException if sort format is invalid or field is not allowed + */ export const SortingParams = createParamDecorator((validParams, ctx: ExecutionContext): ISorting => { const req: Request = ctx.switchToHttp().getRequest(); const sort = req.query.sort as string; diff --git a/back/src/common/entities/abstract.entity.ts b/back/src/common/entities/abstract.entity.ts index c70e48b..2f37542 100644 --- a/back/src/common/entities/abstract.entity.ts +++ b/back/src/common/entities/abstract.entity.ts @@ -4,6 +4,23 @@ import { UpdateDateColumn, } from 'typeorm'; +/** + * Abstract base entity that provides common fields for all database entities. + * + * All entities should extend this class to get: + * - `id`: Auto-incrementing primary key + * - `created_at`: Timestamp when record was created + * - `updated_at`: Timestamp when record was last updated + * + * @example + * ```typescript + * @Entity('users') + * export class User extends AbstractEntity { + * @Column() + * name: string; + * } + * ``` + */ export abstract class AbstractEntity { @PrimaryGeneratedColumn() public id: number; diff --git a/back/src/common/types/api.types.ts b/back/src/common/types/api.types.ts new file mode 100644 index 0000000..ae8f3dc --- /dev/null +++ b/back/src/common/types/api.types.ts @@ -0,0 +1,61 @@ +/** + * Standard API response wrapper used throughout the application. + * + * All API endpoints return responses in this format for consistency. + * + * @template T - Type of the data payload + * + * @example + * ```typescript + * // Success response + * { + * statusCode: 200, + * message: "Success", + * data: { id: 1, name: "John" } + * } + * + * // Error response + * { + * statusCode: 400, + * message: "Validation failed", + * data: null + * } + * ``` + */ +export interface ApiResponse { + /** HTTP status code */ + statusCode: number; + /** Human-readable message */ + message: string; + /** Response payload data */ + data: T; +} + +/** + * Paginated response structure for list endpoints. + * + * Used with pagination decorator to return paginated results. + * + * @template T - Type of items in the paginated list + * + * @example + * ```typescript + * { + * statusCode: 200, + * message: "Success", + * data: { + * totalCount: 100, + * items: [ + * { id: 1, name: "Item 1" }, + * { id: 2, name: "Item 2" } + * ] + * } + * } + * ``` + */ +export interface PaginatedResponse { + /** Total number of items matching the query */ + totalCount: number; + /** Array of items for the current page */ + items: T[]; +}