Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 18s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m15s
Deploy / Build API Image (push) Failing after 28s
Deploy / Build Web Image (push) Failing after 16s
Deploy / Build AI Services Image (push) Failing after 17s
E2E Tests / Playwright E2E (push) Failing after 31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m46s
Security Scanning / Trivy Scan — Web Image (push) Failing after 1m7s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 53s
Security Scanning / Trivy Filesystem Scan (push) Failing after 35s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 0s
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Hoàn tất đợt cuối của nhiệm vụ chuyển toàn bộ tài liệu sang tiếng Việt. Đã dịch 22 file `.md` còn sót (~9.7k dòng) — gồm RUNBOOK, audits, docs/architecture, docs/load-testing, libs READMEs và các quick references. Giữ nguyên code blocks, đường dẫn, identifier kỹ thuật, URL và biến môi trường. Co-Authored-By: Paperclip <noreply@paperclip.ing>
805 lines
22 KiB
Markdown
805 lines
22 KiB
Markdown
# GoodGo Platform API — Hướng Dẫn K6 Load Testing
|
|
|
|
## 🎯 Tóm Tắt Nhanh
|
|
|
|
**Base URL**: `http://localhost:3001/api/v1`
|
|
**Phiên bản Node**: >= 22.0.0
|
|
**Testing Framework**: Playwright (E2E), Vitest (Unit)
|
|
**Chưa có thiết lập K6 hoặc load testing nào tồn tại**
|
|
|
|
---
|
|
|
|
## 📋 Cấu Trúc Dự Án
|
|
|
|
### Thư Mục Gốc
|
|
```
|
|
goodgo-platform/
|
|
├── apps/api # NestJS backend (port 3001)
|
|
├── apps/web # Next.js 15 frontend (port 3000)
|
|
├── libs/mcp-servers # MCP tool server library
|
|
├── prisma/ # Database schema & migrations
|
|
├── e2e/ # Playwright E2E tests (api + web)
|
|
├── turbo.json # Turborepo config
|
|
├── package.json # Root workspace scripts
|
|
├── .env.example # Environment variables template
|
|
└── playwright.config.ts # Playwright configuration
|
|
```
|
|
|
|
### Các Script Chính (package.json)
|
|
```bash
|
|
pnpm dev # Start all apps (API :3001, Web :3000)
|
|
pnpm test # Unit tests via Vitest (API only)
|
|
pnpm test:e2e # Playwright E2E tests
|
|
pnpm test:e2e:api # API E2E tests only
|
|
pnpm test:e2e:web # Web E2E tests only
|
|
pnpm build # Production build
|
|
pnpm lint # ESLint
|
|
pnpm typecheck # TypeScript checking
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ Cấu Trúc Module API
|
|
|
|
### Kiến Trúc Cơ Sở API: `apps/api/src/modules/`
|
|
|
|
Mỗi module tuân theo các tầng DDD: `domain/` → `application/` → `infrastructure/` → `presentation/`
|
|
|
|
```
|
|
modules/
|
|
├── auth/ # Authentication & JWT
|
|
├── listings/ # Property listings CRUD
|
|
├── payments/ # Payment processing (VNPay, MoMo, ZaloPay)
|
|
├── search/ # Full-text & geo search (Typesense)
|
|
├── subscriptions/ # Plans, quotas, usage tracking
|
|
├── admin/ # Moderation, KYC, user management
|
|
├── analytics/ # Market data, heatmaps, price trends
|
|
├── reviews/ # User reviews
|
|
├── notifications/ # Email, push (FCM), in-app
|
|
├── metrics/ # Prometheus metrics
|
|
├── health/ # Health checks
|
|
├── shared/ # Domain primitives, guards, pipes, logging
|
|
└── mcp/ # MCP tool server endpoints
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 MODULE AUTH
|
|
|
|
### Controllers & Endpoints
|
|
|
|
#### File: `apps/api/src/modules/auth/presentation/controllers/auth.controller.ts`
|
|
|
|
| Method | Endpoint | Rate Limit | Auth | Mô tả |
|
|
|--------|----------|-----------|------|-------------|
|
|
| POST | `/auth/register` | 5/giờ | Không | Đăng ký người dùng mới |
|
|
| POST | `/auth/login` | 5/giờ | LocalAuth | Đăng nhập bằng số điện thoại + mật khẩu |
|
|
| POST | `/auth/refresh` | 5/giờ | Không | Làm mới access token |
|
|
| POST | `/auth/logout` | Không giới hạn | Không | Xóa cookie xác thực |
|
|
| POST | `/auth/exchange-token` | Không giới hạn | Không | Đổi OAuth token lấy cookie |
|
|
| GET | `/auth/profile` | Không giới hạn | JWT | Lấy hồ sơ người dùng hiện tại |
|
|
| GET | `/auth/profile/agent` | Không giới hạn | JWT | Lấy hồ sơ agent của người dùng |
|
|
| PATCH | `/auth/kyc` | Không giới hạn | JWT+Admin | Xác minh KYC người dùng (chỉ admin) |
|
|
|
|
### DTOs
|
|
|
|
#### LoginDto
|
|
```typescript
|
|
{
|
|
phone: string // Required, example: "0901234567"
|
|
password: string // Required, example: "P@ssw0rd!"
|
|
}
|
|
```
|
|
|
|
#### RegisterDto
|
|
```typescript
|
|
{
|
|
phone: string // Required, example: "0901234567"
|
|
password: string // Required, min 8 chars, example: "P@ssw0rd!"
|
|
fullName: string // Required, example: "Nguyen Van A"
|
|
email?: string // Optional, valid email format
|
|
}
|
|
```
|
|
|
|
#### RefreshTokenDto
|
|
```typescript
|
|
{
|
|
refreshToken?: string // Optional if using cookie
|
|
}
|
|
```
|
|
|
|
#### VerifyKycDto
|
|
```typescript
|
|
{
|
|
userId: string
|
|
kycStatus: string
|
|
kycData?: object
|
|
}
|
|
```
|
|
|
|
### Cookies & Xác Thực
|
|
|
|
**Access Token**:
|
|
- Cookie: `access_token`
|
|
- Max Age: 15 phút (900s)
|
|
- HttpOnly: true
|
|
- Secure: true (chỉ production)
|
|
- SameSite: strict
|
|
- Path: /
|
|
|
|
**Refresh Token**:
|
|
- Cookie: `refresh_token`
|
|
- Max Age: 30 ngày
|
|
- HttpOnly: true
|
|
- Secure: true (chỉ production)
|
|
- SameSite: strict
|
|
- Path: `/auth`
|
|
|
|
**Chỉ Báo Phiên**:
|
|
- Cookie: `goodgo_authenticated` = "1"
|
|
- HttpOnly: false (frontend nhìn thấy được)
|
|
|
|
### Hỗ Trợ OAuth
|
|
- Google OAuth 2.0
|
|
- Zalo OAuth (nền tảng Việt Nam)
|
|
- Environment: `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `ZALO_APP_ID`, `ZALO_APP_SECRET`
|
|
|
|
---
|
|
|
|
## 🏠 MODULE LISTINGS
|
|
|
|
### Controllers & Endpoints
|
|
|
|
#### File: `apps/api/src/modules/listings/presentation/controllers/listings.controller.ts`
|
|
|
|
| Method | Endpoint | Auth | Quota | Mô tả |
|
|
|--------|----------|------|-------|-------------|
|
|
| POST | `/listings` | JWT | Có | Tạo listing mới |
|
|
| GET | `/listings` | Không | Không | Tìm kiếm/lọc listing (công khai) |
|
|
| GET | `/listings/:id` | Không | Không | Lấy chi tiết listing |
|
|
| GET | `/listings/pending` | JWT+Admin | Không | Lấy listing đang chờ kiểm duyệt |
|
|
| PATCH | `/listings/:id/status` | JWT | Không | Cập nhật trạng thái listing |
|
|
| POST | `/listings/:id/media` | JWT | Không | Tải lên ảnh/video |
|
|
| PATCH | `/listings/:id/moderate` | JWT+Admin | Không | Kiểm duyệt listing (admin) |
|
|
|
|
### DTOs
|
|
|
|
#### CreateListingDto
|
|
```typescript
|
|
{
|
|
transactionType: 'SALE' | 'RENT',
|
|
priceVND: bigint | string,
|
|
propertyType: 'APARTMENT' | 'HOUSE' | 'LAND' | etc.,
|
|
title: string, // Min 5 chars
|
|
description: string, // Min 10 chars
|
|
address: string,
|
|
ward: string,
|
|
district: string,
|
|
city: string,
|
|
latitude: number, // -90 to 90
|
|
longitude: number, // -180 to 180
|
|
areaM2: number, // Total area
|
|
usableAreaM2?: number,
|
|
bedrooms?: number,
|
|
bathrooms?: number,
|
|
floors?: number, // For houses
|
|
floor?: number, // For apartments
|
|
totalFloors?: number,
|
|
direction?: 'EAST' | 'WEST' | 'NORTH' | 'SOUTH' | etc.,
|
|
yearBuilt?: number,
|
|
legalStatus?: string,
|
|
amenities?: string[], // e.g., ['Hồ bơi', 'Gym']
|
|
nearbyPOIs?: object, // e.g., { schools: [], hospitals: [] }
|
|
metroDistanceM?: number,
|
|
projectName?: string,
|
|
agentId?: string,
|
|
rentPriceMonthly?: bigint | string,
|
|
commissionPct?: number,
|
|
}
|
|
```
|
|
|
|
#### SearchListingsDto
|
|
```typescript
|
|
{
|
|
status?: 'ACTIVE' | 'INACTIVE' | 'ARCHIVED',
|
|
transactionType?: 'SALE' | 'RENT',
|
|
propertyType?: 'APARTMENT' | 'HOUSE' | 'LAND' | etc.,
|
|
city?: string,
|
|
district?: string,
|
|
minPrice?: bigint | string,
|
|
maxPrice?: bigint | string,
|
|
minArea?: number,
|
|
maxArea?: number,
|
|
bedrooms?: number,
|
|
page?: number, // Default: 1
|
|
limit?: number, // Default: 20, Max: 100
|
|
}
|
|
```
|
|
|
|
#### UpdateListingStatusDto
|
|
```typescript
|
|
{
|
|
status: string,
|
|
moderationNotes?: string,
|
|
}
|
|
```
|
|
|
|
#### ModerateListingDto
|
|
```typescript
|
|
{
|
|
action: 'APPROVE' | 'REJECT',
|
|
moderationScore?: number,
|
|
notes?: string,
|
|
}
|
|
```
|
|
|
|
### Cấu Trúc Phản Hồi
|
|
|
|
#### ListingDetailData
|
|
Chứa thông tin đầy đủ về listing bao gồm:
|
|
- id, title, description
|
|
- propertyType, transactionType
|
|
- address, latitude, longitude, ward, district, city
|
|
- priceVND, rentPriceMonthly
|
|
- areaM2, usableAreaM2, bedrooms, bathrooms, floors
|
|
- amenities, nearbyPOIs
|
|
- legalStatus, yearBuilt, direction
|
|
- mediaUrls (ảnh/video)
|
|
- agentInfo
|
|
- createdAt, updatedAt
|
|
|
|
#### PaginatedResult<ListingSearchItem>
|
|
```typescript
|
|
{
|
|
items: ListingSearchItem[],
|
|
total: number,
|
|
page: number,
|
|
limit: number,
|
|
totalPages: number,
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 💳 MODULE PAYMENTS
|
|
|
|
### Controllers & Endpoints
|
|
|
|
#### File: `apps/api/src/modules/payments/presentation/controllers/payments.controller.ts`
|
|
|
|
| Method | Endpoint | Auth | Rate Limit | Mô tả |
|
|
|--------|----------|------|-----------|-------------|
|
|
| POST | `/payments` | JWT | Không | Tạo thanh toán |
|
|
| GET | `/payments` | JWT | Không | Liệt kê giao dịch của người dùng |
|
|
| GET | `/payments/:id` | JWT | Không | Lấy trạng thái thanh toán |
|
|
| POST | `/payments/callback/:provider` | Không | 20/phút | Xử lý callback thanh toán (webhook) |
|
|
| POST | `/payments/:id/refund` | JWT+Admin | Không | Hoàn tiền (admin) |
|
|
|
|
### DTOs
|
|
|
|
#### CreatePaymentDto
|
|
```typescript
|
|
{
|
|
provider: 'VNPAY' | 'MOMO' | 'ZALOPAY',
|
|
type: 'LISTING_FEE' | 'SUBSCRIPTION' | 'AGENT_COMMISSION',
|
|
amountVND: number, // 1 to 100,000,000,000
|
|
description: string, // Payment description
|
|
returnUrl: string, // URL (must be valid)
|
|
transactionId?: string, // External ID
|
|
idempotencyKey?: string, // For idempotency
|
|
}
|
|
```
|
|
|
|
#### ListTransactionsDto
|
|
```typescript
|
|
{
|
|
status?: string,
|
|
limit?: number,
|
|
offset?: number,
|
|
}
|
|
```
|
|
|
|
#### RefundPaymentDto
|
|
```typescript
|
|
{
|
|
reason: string,
|
|
}
|
|
```
|
|
|
|
### Nhà Cung Cấp Thanh Toán
|
|
|
|
- **VNPay** (Chính cho Việt Nam)
|
|
- Environment: `VNPAY_TMN_CODE`, `VNPAY_HASH_SECRET`
|
|
- Sandbox: `https://sandbox.vnpayment.vn/paymentv2/vpcpay.html`
|
|
- API: `https://sandbox.vnpayment.vn/merchant_webapi/api/transaction`
|
|
|
|
- **MoMo** (Ví di động)
|
|
- Environment: `MOMO_PARTNER_CODE`, `MOMO_ACCESS_KEY`, `MOMO_SECRET_KEY`
|
|
- Endpoint: `https://test-payment.momo.vn/v2/gateway/api`
|
|
|
|
- **ZaloPay** (Tích hợp Zalo)
|
|
- Environment: `ZALOPAY_APP_ID`, `ZALOPAY_KEY1`, `ZALOPAY_KEY2`
|
|
- Endpoint: `https://sb-openapi.zalopay.vn/v2`
|
|
|
|
### Xử Lý Callback
|
|
|
|
**Mẫu URL Webhook**: `/payments/callback/{provider}`
|
|
|
|
Hỗ trợ cả:
|
|
- Query parameters (VNPay)
|
|
- Request body (MoMo, ZaloPay)
|
|
- Xử lý dữ liệu hợp nhất nội bộ
|
|
|
|
---
|
|
|
|
## 🔍 MODULE SEARCH
|
|
|
|
### Controllers & Endpoints
|
|
|
|
#### File: `apps/api/src/modules/search/presentation/controllers/search.controller.ts`
|
|
|
|
| Method | Endpoint | Auth | Mô tả |
|
|
|--------|----------|------|-------------|
|
|
| GET | `/search` | Không | Tìm kiếm full-text (công khai) |
|
|
| GET | `/search/geo` | Không | Tìm kiếm theo bán kính địa lý (công khai) |
|
|
| POST | `/search/reindex` | JWT+Admin | Lập chỉ mục lại tất cả bất động sản (admin) |
|
|
|
|
### DTOs
|
|
|
|
#### SearchPropertiesDto (Tìm kiếm full-text)
|
|
```typescript
|
|
{
|
|
q?: string, // Free-text query, e.g., 'chung cu quan 7'
|
|
propertyType?: string, // Filter by type
|
|
transactionType?: string, // 'sale' or 'rent'
|
|
priceMin?: number, // Min price in VND
|
|
priceMax?: number, // Max price in VND
|
|
areaMin?: number, // Min area in m²
|
|
areaMax?: number, // Max area in m²
|
|
bedrooms?: number, // Number of bedrooms
|
|
district?: string, // District name
|
|
city?: string, // City name
|
|
sortBy?: 'price_asc' | 'price_desc' | 'date_desc' | 'relevance',
|
|
page?: number, // 1-based, default: 1
|
|
perPage?: number, // Default: 20, Max: 100
|
|
}
|
|
```
|
|
|
|
#### GeoSearchDto (Tìm kiếm địa lý)
|
|
```typescript
|
|
{
|
|
lat: number, // Latitude, -90 to 90
|
|
lng: number, // Longitude, -180 to 180
|
|
radiusKm: number, // Radius, 0.1 to 100
|
|
propertyType?: string,
|
|
transactionType?: string,
|
|
priceMin?: number,
|
|
priceMax?: number,
|
|
sortBy?: 'distance' | 'price_asc' | 'price_desc' | 'date_desc',
|
|
page?: number, // Default: 1
|
|
perPage?: number, // Default: 20, Max: 100
|
|
}
|
|
```
|
|
|
|
### Search Engine
|
|
|
|
Tích hợp **Typesense** cho tìm kiếm full-text & faceted nhanh
|
|
- Environment: `TYPESENSE_HOST`, `TYPESENSE_PORT`, `TYPESENSE_API_KEY`
|
|
- Mặc định: `http://localhost:8108`
|
|
|
|
### Cấu Trúc Phản Hồi
|
|
|
|
#### SearchResult
|
|
```typescript
|
|
{
|
|
results: SearchHit[],
|
|
facets?: {
|
|
propertyType?: { value: string; count: number }[],
|
|
district?: { value: string; count: number }[],
|
|
transactionType?: { value: string; count: number }[],
|
|
},
|
|
total: number,
|
|
page: number,
|
|
perPage: number,
|
|
totalPages: number,
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🗄️ Database & Environment
|
|
|
|
### PostgreSQL với PostGIS
|
|
|
|
```
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=goodgo
|
|
DB_USER=goodgo
|
|
DB_PASSWORD=<change_me>
|
|
DATABASE_URL=postgresql://goodgo:password@localhost:5432/goodgo?schema=public
|
|
```
|
|
|
|
### Redis Cache
|
|
|
|
```
|
|
REDIS_URL=redis://localhost:6379
|
|
```
|
|
|
|
### Các Biến Môi Trường Chính
|
|
|
|
```bash
|
|
# JWT Secrets (REQUIRED)
|
|
JWT_SECRET=<openssl rand -base64 48>
|
|
JWT_REFRESH_SECRET=<openssl rand -base64 48>
|
|
JWT_EXPIRES_IN=15m
|
|
JWT_REFRESH_EXPIRES_IN=7d
|
|
|
|
# CORS
|
|
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
|
|
|
|
# Node Environment
|
|
NODE_ENV=development|test|production
|
|
PORT=3001 # API port
|
|
|
|
# OAuth
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
ZALO_APP_ID=
|
|
ZALO_APP_SECRET=
|
|
|
|
# Typesense Search
|
|
TYPESENSE_HOST=localhost
|
|
TYPESENSE_PORT=8108
|
|
TYPESENSE_API_KEY=
|
|
|
|
# MinIO/S3 Storage
|
|
MINIO_ENDPOINT=localhost
|
|
MINIO_PORT=9000
|
|
MINIO_ACCESS_KEY=
|
|
MINIO_SECRET_KEY=
|
|
MINIO_BUCKET=goodgo-media
|
|
|
|
# Payment Gateways
|
|
VNPAY_TMN_CODE=
|
|
VNPAY_HASH_SECRET=
|
|
MOMO_PARTNER_CODE=
|
|
ZALOPAY_APP_ID=
|
|
|
|
# Logging
|
|
LOG_LEVEL=info
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Thiết Lập Test Hiện Có
|
|
|
|
### Cấu Hình Playwright
|
|
|
|
**File**: `playwright.config.ts`
|
|
|
|
```typescript
|
|
testDir: './e2e'
|
|
globalSetup: './e2e/global-setup.ts'
|
|
globalTeardown: './e2e/global-teardown.ts'
|
|
|
|
Projects:
|
|
- "api": Tests NestJS API (port 3001)
|
|
baseURL: http://localhost:3001/api/v1
|
|
- "web": Tests Next.js frontend (port 3000)
|
|
baseURL: http://localhost:3000
|
|
```
|
|
|
|
### Các Script Playwright
|
|
|
|
```bash
|
|
pnpm test:e2e # Run all E2E tests
|
|
pnpm test:e2e:api # API tests only
|
|
pnpm test:e2e:web # Web tests only
|
|
pnpm test:e2e:report # Show HTML report
|
|
```
|
|
|
|
### Database Test
|
|
|
|
- CI sử dụng database `goodgo_test`
|
|
- Local sử dụng `.env.test` cho URL database test
|
|
- Migration & seed chạy trong `global-setup.ts`
|
|
- Dọn dẹp trong `global-teardown.ts`
|
|
|
|
### Ví Dụ E2E Test
|
|
|
|
**File**: `e2e/api/auth-register.spec.ts`
|
|
|
|
```typescript
|
|
import { test, expect } from '@playwright/test';
|
|
import { createTestUser } from '../fixtures';
|
|
|
|
test.describe('POST /auth/register', () => {
|
|
test('registers a new user and returns token pair', async ({ request }) => {
|
|
const user = createTestUser();
|
|
const res = await request.post('/auth/register', { data: user });
|
|
|
|
expect(res.status()).toBe(201);
|
|
const body = await res.json();
|
|
expect(body).toHaveProperty('accessToken');
|
|
expect(body).toHaveProperty('refreshToken');
|
|
});
|
|
});
|
|
```
|
|
|
|
### Unit Tests (Vitest)
|
|
|
|
```bash
|
|
pnpm test # Run unit tests (API only)
|
|
pnpm test:integration # Integration tests
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Thiết Lập CI/CD
|
|
|
|
### Workflow GitHub Actions
|
|
|
|
#### `ci.yml` - Lint → Typecheck → Test → Build
|
|
- Chạy khi: `push main` và `pull_request`
|
|
- Services: PostgreSQL 16 + PostGIS
|
|
- Các bước: lint → typecheck → test → build
|
|
|
|
#### `e2e.yml` - Playwright E2E Tests
|
|
- Chạy khi: `push main` và `pull_request`
|
|
- Services:
|
|
- PostgreSQL 16 + PostGIS
|
|
- Redis 7
|
|
- Typesense 27.1
|
|
- MinIO (lưu trữ tương thích S3)
|
|
- Artifacts: HTML report + traces
|
|
|
|
#### `security.yml` - Bảo Mật Mã Nguồn
|
|
- Quét dependency
|
|
- Phân tích SAST
|
|
|
|
#### `deploy.yml` - Triển Khai Production
|
|
- Build Docker
|
|
- Push registry
|
|
- Điều phối triển khai
|
|
|
|
---
|
|
|
|
## 📊 Mẫu Kiến Trúc
|
|
|
|
### Mẫu CQRS NestJS
|
|
|
|
Mỗi module sử dụng:
|
|
- **Commands** (Thao tác ghi)
|
|
- `CommandBus.execute(command)`
|
|
- Đặt trong `application/commands/`
|
|
- Handlers trong `application/commands/{command}/`
|
|
|
|
- **Queries** (Thao tác đọc)
|
|
- `QueryBus.execute(query)`
|
|
- Đặt trong `application/queries/`
|
|
- Handlers trong `application/queries/{query}/`
|
|
|
|
Ví dụ:
|
|
```typescript
|
|
// In controller
|
|
const result = await this.commandBus.execute(
|
|
new CreateListingCommand(userId, ...)
|
|
);
|
|
|
|
const profile = await this.queryBus.execute(
|
|
new GetProfileQuery(userId)
|
|
);
|
|
```
|
|
|
|
### Guards & Interceptors
|
|
|
|
- `JwtAuthGuard` - Xác thực JWT token
|
|
- `LocalAuthGuard` - Xác thực email/mật khẩu
|
|
- `RolesGuard` - Kiểm soát truy cập dựa trên vai trò
|
|
- `QuotaGuard` - Thi hành hạn ngạch subscription
|
|
- `FileValidationPipe` - Xác thực file upload
|
|
|
|
---
|
|
|
|
## 🚀 Khởi Động API
|
|
|
|
### Phát Triển Cục Bộ
|
|
|
|
```bash
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Generate Prisma client
|
|
pnpm db:generate
|
|
|
|
# Run migrations
|
|
pnpm db:migrate:dev
|
|
|
|
# Seed data (users, listings, etc.)
|
|
pnpm db:seed
|
|
|
|
# Start API (and Web)
|
|
pnpm dev
|
|
|
|
# API will be available at:
|
|
# http://localhost:3001/api/v1
|
|
# Swagger UI: http://localhost:3001/api/v1/docs
|
|
```
|
|
|
|
### Với Docker
|
|
|
|
```bash
|
|
docker-compose up
|
|
# Services: PostgreSQL, Redis, Typesense, MinIO, API, Web
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Khuyến Nghị K6 Load Testing
|
|
|
|
### Các Endpoint Chính Cần Test
|
|
|
|
1. **Authentication** (Ưu tiên cao)
|
|
- Register: `POST /auth/register`
|
|
- Login: `POST /auth/login`
|
|
- Refresh: `POST /auth/refresh`
|
|
- Profile: `GET /auth/profile` (đã xác thực)
|
|
|
|
2. **Listings** (Ưu tiên cao)
|
|
- Create: `POST /listings` (giới hạn quota)
|
|
- Search: `GET /listings` (công khai, lưu lượng cao)
|
|
- Detail: `GET /listings/:id` (công khai, lưu lượng cao)
|
|
|
|
3. **Search** (Ưu tiên cao)
|
|
- Full-text: `GET /search?q=...` (công khai, lưu lượng cao)
|
|
- Geo: `GET /search/geo?lat=...&lng=...` (công khai, lưu lượng cao)
|
|
|
|
4. **Payments** (Ưu tiên trung bình)
|
|
- Create: `POST /payments` (đã xác thực)
|
|
- List: `GET /payments` (đã xác thực)
|
|
- Webhook: `POST /payments/callback/:provider` (không giới hạn throttle)
|
|
|
|
5. **Endpoint Admin** (Ưu tiên trung bình, hạn chế)
|
|
- Kiểm duyệt listing: `PATCH /listings/:id/moderate`
|
|
- Liệt kê pending: `GET /listings/pending`
|
|
- Xác minh KYC: `PATCH /auth/kyc`
|
|
- Reindex: `POST /search/reindex`
|
|
|
|
### Cấu Trúc Script K6
|
|
|
|
```javascript
|
|
import http from 'k6/http';
|
|
import { check, group, sleep } from 'k6';
|
|
|
|
const BASE_URL = 'http://localhost:3001/api/v1';
|
|
|
|
// Stage-based load: ramp up → sustained → ramp down
|
|
export const options = {
|
|
stages: [
|
|
{ duration: '2m', target: 100 }, // Ramp up
|
|
{ duration: '5m', target: 100 }, // Sustained
|
|
{ duration: '2m', target: 0 }, // Ramp down
|
|
],
|
|
thresholds: {
|
|
http_req_duration: ['p(95)<500', 'p(99)<1000'],
|
|
http_req_failed: ['rate<0.1'],
|
|
},
|
|
};
|
|
|
|
export default function() {
|
|
// Test scenarios here
|
|
}
|
|
```
|
|
|
|
### Mẹo Tạo Dữ Liệu
|
|
|
|
- Sử dụng test fixture user từ Playwright tests
|
|
- Tận dụng dữ liệu Prisma seed (quận, loại bất động sản)
|
|
- Tạo các truy vấn tìm kiếm thực tế
|
|
- Test với các tọa độ địa lý khác nhau (TP.HCM: ~10.77°N, 106.70°E)
|
|
|
|
---
|
|
|
|
## 📁 Tham Khảo Nhanh Vị Trí File
|
|
|
|
```
|
|
apps/api/
|
|
├── src/
|
|
│ ├── main.ts # API entry point (port 3001)
|
|
│ ├── app.module.ts # Root module
|
|
│ └── modules/
|
|
│ ├── auth/
|
|
│ │ ├── presentation/controllers/auth.controller.ts
|
|
│ │ ├── presentation/dto/
|
|
│ │ │ ├── login.dto.ts
|
|
│ │ │ ├── register.dto.ts
|
|
│ │ │ ├── refresh-token.dto.ts
|
|
│ │ │ └── verify-kyc.dto.ts
|
|
│ │ ├── application/commands/
|
|
│ │ ├── application/queries/
|
|
│ │ ├── infrastructure/services/token.service.ts
|
|
│ │ └── domain/
|
|
│ ├── listings/
|
|
│ │ ├── presentation/controllers/listings.controller.ts
|
|
│ │ ├── presentation/dto/
|
|
│ │ │ ├── create-listing.dto.ts
|
|
│ │ │ ├── search-listings.dto.ts
|
|
│ │ │ ├── update-listing-status.dto.ts
|
|
│ │ │ └── moderate-listing.dto.ts
|
|
│ │ └── ...
|
|
│ ├── payments/
|
|
│ │ ├── presentation/controllers/payments.controller.ts
|
|
│ │ ├── presentation/dto/
|
|
│ │ │ ├── create-payment.dto.ts
|
|
│ │ │ ├── list-transactions.dto.ts
|
|
│ │ │ └── refund-payment.dto.ts
|
|
│ │ └── ...
|
|
│ ├── search/
|
|
│ │ ├── presentation/controllers/search.controller.ts
|
|
│ │ ├── presentation/dto/
|
|
│ │ │ ├── search-properties.dto.ts
|
|
│ │ │ └── geo-search.dto.ts
|
|
│ │ └── ...
|
|
│ └── ...
|
|
│
|
|
└── package.json # Dependencies, scripts
|
|
|
|
e2e/
|
|
├── api/ # Playwright API tests
|
|
│ ├── auth-register.spec.ts
|
|
│ ├── auth-refresh.spec.ts
|
|
│ └── ...
|
|
├── web/ # Playwright web tests
|
|
├── fixtures.ts # Test data generators
|
|
├── global-setup.ts # DB setup before tests
|
|
└── global-teardown.ts # DB cleanup after tests
|
|
|
|
playwright.config.ts # Playwright config
|
|
.github/workflows/
|
|
├── ci.yml # Lint → typecheck → test → build
|
|
├── e2e.yml # Playwright E2E
|
|
├── security.yml # Security scanning
|
|
└── deploy.yml # Production deployment
|
|
|
|
.env.example # Environment variable template
|
|
.env.test # Test database connection
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 Liên Kết & Tham Khảo Hữu Ích
|
|
|
|
- **API Swagger Docs**: `http://localhost:3001/api/v1/docs`
|
|
- **Tài Liệu Gốc Dự Án**: `CLAUDE.md`
|
|
- **Phân Tích Hiện Có**: `CODEBASE_ANALYSIS.md`, `EXPLORATION_REPORT.md`
|
|
- **Tài Liệu Frontend**: `docs/audits/FRONTEND_EXPLORATION.md`
|
|
|
|
---
|
|
|
|
## ✅ Tóm Tắt Triển Khai K6
|
|
|
|
**Chưa có thiết lập K6 nào** — bạn có một khởi đầu hoàn toàn mới!
|
|
|
|
**Các endpoint chính** được xác định trên các module:
|
|
- Auth (register, login, refresh, profile)
|
|
- Listings (create, search, detail, moderate)
|
|
- Search (full-text, geo)
|
|
- Payments (create, callback, list, refund)
|
|
- Admin (moderate, KYC, reindex)
|
|
|
|
**Rate limit** cần xem xét:
|
|
- Auth: 5/giờ mỗi endpoint
|
|
- Payments callback: 20/phút
|
|
- Khác: Không giới hạn (trừ quota guard cho thao tác create)
|
|
|
|
**Hạ tầng sẵn sàng**:
|
|
- Turbo monorepo cho quản lý dependency
|
|
- PostgreSQL + PostGIS cho dữ liệu không gian
|
|
- Typesense cho lập chỉ mục tìm kiếm
|
|
- Redis cho caching
|
|
- MinIO cho lưu trữ media
|
|
- Endpoint Prometheus metrics
|
|
|
|
**Tests có thể được tích hợp** vào CI/CD pipeline qua `.github/workflows/` (đề xuất: `load-test.yml` mới)
|