Tạo file `.npmrc` để đảm bảo Prisma Client hoạt động với pnpm:
```txt
shamefully-hoist=true
```
---
## 4. Cấu trúc thư mục đề xuất
```txt
data-crawler-be/
├── prisma/
│ ├── schema.prisma
│ ├── seed.ts
│ └── migrations/
│ └── ...
│
├── src/
│ ├── app.ts
│ ├── server.ts
│ │
│ ├── config/
│ │ ├── env.config.ts
│ │ ├── database.config.ts
│ │ ├── jwt.config.ts
│ │ ├── firecrawl.config.ts
│ │ └── storage.config.ts
│ │
│ ├── database/
│ │ └── prisma.client.ts
│ │
│ ├── common/
│ │ ├── constants/
│ │ │ ├── role.constant.ts
│ │ │ ├── job-status.constant.ts
│ │ │ └── export-type.constant.ts
│ │ ├── errors/
│ │ │ ├── app-error.ts
│ │ │ └── error-code.ts
│ │ ├── helpers/
│ │ │ ├── url.helper.ts
│ │ │ ├── file.helper.ts
│ │ │ └── slug.helper.ts
│ │ └── types/
│ │ └── express.d.ts
│ │
│ ├── middlewares/
│ │ ├── auth.middleware.ts
│ │ ├── role.middleware.ts
│ │ ├── validate.middleware.ts
│ │ ├── error.middleware.ts
│ │ └── rate-limit.middleware.ts
│ │
│ ├── modules/
│ │ ├── auth/
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── auth.repository.ts
│ │ │ ├── auth.route.ts
│ │ │ ├── auth.dto.ts
│ │ │ └── auth.validation.ts
│ │ │
│ │ ├── users/
│ │ │ ├── user.controller.ts
│ │ │ ├── user.service.ts
│ │ │ ├── user.repository.ts
│ │ │ ├── user.route.ts
│ │ │ ├── user.dto.ts
│ │ │ └── user.validation.ts
│ │ │
│ │ ├── crawl-jobs/
│ │ │ ├── crawl-job.controller.ts
│ │ │ ├── crawl-job.service.ts
│ │ │ ├── crawl-job.repository.ts
│ │ │ ├── crawl-job.route.ts
│ │ │ ├── crawl-job.dto.ts
│ │ │ └── crawl-job.validation.ts
│ │ │
│ │ ├── crawl-pages/
│ │ │ ├── crawl-page.service.ts
│ │ │ ├── crawl-page.repository.ts
│ │ │ └── crawl-page.dto.ts
│ │ │
│ │ ├── crawl-assets/
│ │ │ ├── crawl-asset.service.ts
│ │ │ ├── crawl-asset.repository.ts
│ │ │ └── crawl-asset.dto.ts
│ │ │
│ │ ├── crawl-exports/
│ │ │ ├── crawl-export.controller.ts
│ │ │ ├── crawl-export.service.ts
│ │ │ ├── crawl-export.repository.ts
│ │ │ ├── crawl-export.route.ts
│ │ │ └── crawl-export.dto.ts
│ │ │
│ │ ├── firecrawl/
│ │ │ ├── firecrawl.service.ts
│ │ │ ├── firecrawl.client.ts
│ │ │ └── firecrawl.dto.ts
│ │ │
│ │ └── exports/
│ │ ├── export.service.ts
│ │ ├── json-export.service.ts
│ │ ├── csv-export.service.ts
│ │ ├── xlsx-export.service.ts
│ │ └── markdown-export.service.ts
│ │
│ ├── queues/
│ │ ├── crawl.queue.ts
│ │ └── crawl.worker.ts
│ │
│ └── routes/
│ └── index.ts
│
├── storage/
│ └── exports/
│ └── .gitkeep
│
├── .env
├── .env.example
├── .gitignore
├── .npmrc
├── package.json
├── tsconfig.json
├── docker-compose.yml
└── README.md
```
---
## 5. Cấu hình PostgreSQL trong `.env`
Tạo file `.env`:
```env
NODE_ENV=development
PORT=3000
# Database Configuration
DB_HOST=27.74.255.96
DB_PORT=5430
DB_USER=postgres
DB_PASSWORD="your_password_here"
DB_NAME=datacrawler
JWT_ACCESS_SECRET=change_me_access_secret
JWT_REFRESH_SECRET=change_me_refresh_secret
JWT_ACCESS_EXPIRES_IN=1d
JWT_REFRESH_EXPIRES_IN=7d
FIRECRAWL_API_KEY=your_firecrawl_api_key
FIRECRAWL_BASE_URL=https://api.firecrawl.dev
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
STORAGE_DRIVER=local
STORAGE_EXPORT_DIR=storage/exports
MAX_CRAWL_PAGES=100
MAX_CRAWL_DEPTH=3
```
File `.env.example` cũng cần có đủ key nhưng không chứa secret thật.
---
## 6. Cấu hình Prisma
Khởi tạo Prisma:
```bash
pnpm dlx prisma init
```
> **Lưu ý:** Prisma schema dùng `env("DATABASE_URL")`. Vì `.env` chỉ có `DB_*` vars, script `scripts/prisma-run.js` sẽ tự xây dựng `DATABASE_URL` từ `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME` trước khi chạy lệnh Prisma.