Optimize code for AI assistents
This commit is contained in:
parent
aa19c08292
commit
8140c86dfe
99
.cursorrules
Normal file
99
.cursorrules
Normal file
@ -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)
|
||||||
267
AGENTS.md
Normal file
267
AGENTS.md
Normal file
@ -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
|
||||||
137
AI_ENHANCEMENTS.md
Normal file
137
AI_ENHANCEMENTS.md
Normal file
@ -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<T>` interface
|
||||||
|
- `PaginatedResponse<T>` 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
|
||||||
201
CONTRIBUTING.md
Normal file
201
CONTRIBUTING.md
Normal file
@ -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 <repository-url>
|
||||||
|
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: <FeaturePage />,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
327
EXAMPLES.md
Normal file
327
EXAMPLES.md
Normal file
@ -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 <token>
|
||||||
|
// 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 <token>
|
||||||
|
// 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 <token>
|
||||||
|
// Request body:
|
||||||
|
{
|
||||||
|
"username": "updated_username",
|
||||||
|
"email": "newemail@example.com",
|
||||||
|
"is_active": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Admin
|
||||||
|
```typescript
|
||||||
|
// Backend endpoint: DELETE /admins/:id
|
||||||
|
// Headers: Authorization: Bearer <token>
|
||||||
|
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 <div>Loading...</div>;
|
||||||
|
if (error) return <div>Error: {error.message}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Total: {data?.data.totalCount}</p>
|
||||||
|
{data?.data.items.map(admin => (
|
||||||
|
<div key={admin.id}>{admin.username}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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<void> {
|
||||||
|
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<void> {
|
||||||
|
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')
|
||||||
|
```
|
||||||
21
README.md
21
README.md
@ -94,3 +94,24 @@ pnpm migration:generate --name=my-migration-name
|
|||||||
- Dark/light theme
|
- Dark/light theme
|
||||||
- Docker-based development & production setup
|
- Docker-based development & production setup
|
||||||
- Database backup with Telegram notification
|
- 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
|
||||||
|
|||||||
15
announcement.md
Normal file
15
announcement.md
Normal file
@ -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 и предложениям!
|
||||||
@ -1,14 +1,14 @@
|
|||||||
import { Module } from '@nestjs/common';
|
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 { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
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 { CommonModule } from './common/common.module';
|
||||||
import { validate } from './config/env/validate';
|
import { validate } from './config/env/validate';
|
||||||
import { DatabaseModule } from './database/database.module';
|
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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
@ -39,6 +39,24 @@ export class BotService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to a specific Telegram user.
|
* 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(
|
public async sendMessage(
|
||||||
telegramId: string,
|
telegramId: string,
|
||||||
|
|||||||
@ -29,7 +29,22 @@ interface IFilteringParams {
|
|||||||
value: string;
|
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 {
|
export enum FilterRule {
|
||||||
EQUALS = 'eq',
|
EQUALS = 'eq',
|
||||||
NOT_EQUALS = 'neq',
|
NOT_EQUALS = 'neq',
|
||||||
@ -46,6 +61,15 @@ export enum FilterRule {
|
|||||||
BETWEEN = 'between',
|
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) => {
|
export const getWhere = (filter: IFilteringParams) => {
|
||||||
if (!filter) return {};
|
if (!filter) return {};
|
||||||
|
|
||||||
@ -86,6 +110,33 @@ function ensureObjectRecord(value: unknown): value is Record<string, unknown> {
|
|||||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
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(
|
export const FilteringParams = createParamDecorator(
|
||||||
(data: readonly string[], ctx: ExecutionContext): IFiltering | null => {
|
(data: readonly string[], ctx: ExecutionContext): IFiltering | null => {
|
||||||
const req: Request = ctx.switchToHttp().getRequest();
|
const req: Request = ctx.switchToHttp().getRequest();
|
||||||
|
|||||||
@ -2,18 +2,60 @@ import { BadRequestException, createParamDecorator, ExecutionContext } from '@ne
|
|||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { ErrorCode } from '../enums/error-code.enum';
|
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 {
|
export interface IPagination {
|
||||||
|
/** Maximum number of items to return */
|
||||||
limit: number;
|
limit: number;
|
||||||
|
/** Number of items to skip */
|
||||||
offset: number;
|
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<T> = {
|
export type PaginatedResource<T> = {
|
||||||
|
/** Total number of items matching the query */
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
|
/** Array of items for the current page */
|
||||||
items: T[];
|
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 => {
|
export const PaginationParams = createParamDecorator((data, ctx: ExecutionContext): IPagination => {
|
||||||
const req: Request = ctx.switchToHttp().getRequest();
|
const req: Request = ctx.switchToHttp().getRequest();
|
||||||
const page = parseInt(req.query.page as string);
|
const page = parseInt(req.query.page as string);
|
||||||
|
|||||||
@ -2,12 +2,48 @@ import { BadRequestException, createParamDecorator, ExecutionContext } from '@ne
|
|||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { ErrorCode } from '../enums/error-code.enum';
|
import { ErrorCode } from '../enums/error-code.enum';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort direction for database queries.
|
||||||
|
*/
|
||||||
type Direction = 'ASC' | 'DESC';
|
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 {
|
export interface ISorting {
|
||||||
|
/** Field name mapped to sort direction */
|
||||||
[field: string]: 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 => {
|
export const SortingParams = createParamDecorator((validParams, ctx: ExecutionContext): ISorting => {
|
||||||
const req: Request = ctx.switchToHttp().getRequest();
|
const req: Request = ctx.switchToHttp().getRequest();
|
||||||
const sort = req.query.sort as string;
|
const sort = req.query.sort as string;
|
||||||
|
|||||||
@ -4,6 +4,23 @@ import {
|
|||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} 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 {
|
export abstract class AbstractEntity {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
public id: number;
|
public id: number;
|
||||||
|
|||||||
61
back/src/common/types/api.types.ts
Normal file
61
back/src/common/types/api.types.ts
Normal file
@ -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<T> {
|
||||||
|
/** 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<T> {
|
||||||
|
/** Total number of items matching the query */
|
||||||
|
totalCount: number;
|
||||||
|
/** Array of items for the current page */
|
||||||
|
items: T[];
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user