init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
428
README.md
Normal file
428
README.md
Normal file
@@ -0,0 +1,428 @@
|
||||
# Charging Station Mock API (json-server)
|
||||
|
||||
Mock server cho feature Charging Station theo tài liệu:
|
||||
- `docs/charging_station_final_spec.md`
|
||||
|
||||
## 1. Cài dependency
|
||||
|
||||
```bash
|
||||
cd mock/charging-station
|
||||
bun install
|
||||
```
|
||||
|
||||
## 2. Chạy server
|
||||
|
||||
```bash
|
||||
bun run start
|
||||
```
|
||||
|
||||
Mặc định chạy ở `http://localhost:3100`.
|
||||
|
||||
Có thể đổi port:
|
||||
|
||||
```bash
|
||||
PORT=3200 bun run start
|
||||
```
|
||||
|
||||
## 3. Static Files (Images)
|
||||
|
||||
Server serve static files từ folder `public/`. Cấu trúc:
|
||||
|
||||
```
|
||||
mock/charging-station/
|
||||
├── public/
|
||||
│ └── images/
|
||||
│ ├── banners/ # Ảnh banner (800x400 recommended)
|
||||
│ │ ├── banner_summer_sale.png
|
||||
│ │ ├── banner_cashback.png
|
||||
│ │ └── banner_new_year.png
|
||||
│ └── vouchers/ # Ảnh voucher (300x200 recommended)
|
||||
│ ├── starbucks.png
|
||||
│ ├── kfc.png
|
||||
│ ├── grab.png
|
||||
│ └── ...
|
||||
├── db.json
|
||||
└── server.js
|
||||
```
|
||||
|
||||
**Cách thêm ảnh mới:**
|
||||
1. Đặt file ảnh vào `public/images/banners/` hoặc `public/images/vouchers/`
|
||||
2. Cập nhật `imageUrl` trong `db.json` với path tương đối: `/images/banners/ten_file.png`
|
||||
3. Server sẽ tự động build full URL: `http://localhost:3100/images/banners/ten_file.png`
|
||||
|
||||
**Access trực tiếp:**
|
||||
```bash
|
||||
# Xem ảnh trong browser
|
||||
open http://localhost:3100/images/banners/banner_summer_sale.png
|
||||
```
|
||||
|
||||
## 4. Endpoints
|
||||
|
||||
Tất cả endpoint dùng `POST`:
|
||||
|
||||
| Endpoint | Mô tả |
|
||||
|----------|-------|
|
||||
| `POST /charging-station/execute` | Thực thi giao dịch nạp tiền |
|
||||
| `POST /charging-station/getHomeData` | Lấy dữ liệu trang Home |
|
||||
| `POST /charging-station/search` | Tìm kiếm với AI gợi ý |
|
||||
| `POST /charging-station/reward/getList` | Danh sách ưu đãi/voucher |
|
||||
|
||||
## 5. Validation Rules
|
||||
|
||||
### `POST /charging-station/execute`
|
||||
|
||||
**Input validation:**
|
||||
|
||||
| Field | Rule | Regex |
|
||||
|-------|------|-------|
|
||||
| `accountNumber` | Bắt buộc, đúng **9 ký tự**, chỉ chứa số | `^[0-9]{9}$` |
|
||||
| `amount` | Bắt buộc, > 0, tối đa **1,000,000,000** | — |
|
||||
| `accountName` | Không bắt buộc, chỉ chữ cái và khoảng trắng | `^[a-zA-ZÀ-ỹ\s]*$` |
|
||||
|
||||
**Error codes:**
|
||||
|
||||
| Code | Điều kiện | Message |
|
||||
|------|-----------|---------|
|
||||
| `01` | Dữ liệu không hợp lệ / TK không tồn tại | Tùy context |
|
||||
| `02` | Số dư không đủ | "Số dư không đủ" |
|
||||
| `03` | Vượt hạn mức (> 1 tỷ) | "Số tiền tối đa là 1,000,000,000 VND" |
|
||||
|
||||
**Simulate scenarios:**
|
||||
|
||||
| Scenario | Cách test |
|
||||
|----------|-----------|
|
||||
| TK không tồn tại | `accountNumber` bắt đầu bằng `999` |
|
||||
| Số dư không đủ | `accountNumber` bắt đầu bằng `111` + `amount > 500000000` |
|
||||
| Vượt hạn mức | `amount > 1000000000` |
|
||||
|
||||
### `POST /charging-station/search`
|
||||
|
||||
- `query` rỗng: trả full default data (recent + suggested + chips).
|
||||
- `query` có giá trị: filter `suggestions` theo `title/description`.
|
||||
|
||||
### `POST /charging-station/reward/getList`
|
||||
|
||||
- Hỗ trợ `category` để filter voucher/brand.
|
||||
- Hỗ trợ `page` + `pageSize` để phân trang `vouchers`.
|
||||
- Trả thêm `paging` để client dễ test load more.
|
||||
|
||||
## 6. Curl samples
|
||||
|
||||
### Execute success
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"sessionId": "abc123",
|
||||
"deviceId": "device-001",
|
||||
"accountNumber": "012345678",
|
||||
"amount": "1000000",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"isDefaultAccount": true
|
||||
}'
|
||||
```
|
||||
|
||||
### Execute - TK không tồn tại
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"accountNumber": "999123456",
|
||||
"amount": "1000000"
|
||||
}'
|
||||
```
|
||||
|
||||
### Execute - Số dư không đủ
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"accountNumber": "111234567",
|
||||
"amount": "600000000"
|
||||
}'
|
||||
```
|
||||
|
||||
### Execute - Vượt hạn mức
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"accountNumber": "012345678",
|
||||
"amount": "1500000000"
|
||||
}'
|
||||
```
|
||||
|
||||
### Execute - accountNumber sai format
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"accountNumber": "12345",
|
||||
"amount": "1000000"
|
||||
}'
|
||||
```
|
||||
|
||||
### Execute - accountName có ký tự đặc biệt
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"accountNumber": "012345678",
|
||||
"amount": "1000000",
|
||||
"accountName": "Nguyen Van A@123"
|
||||
}'
|
||||
```
|
||||
|
||||
### Get Home Data
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/getHomeData \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId":"abc123","deviceId":"device-001"}'
|
||||
```
|
||||
|
||||
**Response structure:**
|
||||
- `accounts[]` - Danh sách tài khoản cá nhân (DEFAULT, APPLE_PAY, OVERDRAFT, SALARY)
|
||||
- `businessAccounts[]` - Danh sách tài khoản hộ kinh doanh
|
||||
- `loyaltyPoints`, `cashbackAmount`, `memberLevel` - Thông tin loyalty
|
||||
- `recentTransactions[]` - Giao dịch gần đây
|
||||
|
||||
### Search default
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId":"abc123","query":"","page":1,"pageSize":20}'
|
||||
```
|
||||
|
||||
### Search with query
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId":"abc123","query":"chuyển tiền","page":1,"pageSize":20}'
|
||||
```
|
||||
|
||||
### Reward - page 1 (có banners)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/reward/getList \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId":"abc123","page":1,"pageSize":5}'
|
||||
```
|
||||
|
||||
**Response structure:**
|
||||
- `banners[]` - Danh sách banner (chỉ trả về ở page 1)
|
||||
- `vouchers[]` - Danh sách voucher (phân trang)
|
||||
- `paging` - Thông tin phân trang
|
||||
|
||||
### Reward - load more (page 2+, không có banners)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3100/charging-station/reward/getList \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"sessionId":"abc123","page":2,"pageSize":5}'
|
||||
```
|
||||
|
||||
**Voucher fields:**
|
||||
- `imageUrl` - URL ảnh voucher
|
||||
- `content` - Nội dung/mô tả ưu đãi
|
||||
- `expiry` - Thời hạn (rỗng = không giới hạn)
|
||||
- `requireCardSpending` - `true` = yêu cầu chi tiêu thẻ
|
||||
|
||||
---
|
||||
|
||||
## 7. BASE_URL Configuration
|
||||
|
||||
| Environment | BASE_URL |
|
||||
|-------------|----------|
|
||||
| Local | `http://localhost:3100` |
|
||||
| VPS Production | `https://api-mock.example.com` |
|
||||
|
||||
> **TODO:** Cập nhật VPS Production URL sau khi deploy.
|
||||
|
||||
---
|
||||
|
||||
## 8. Deploy lên VPS với PM2
|
||||
|
||||
### 8.1 Cài đặt môi trường trên VPS
|
||||
|
||||
```bash
|
||||
# 1. Cài Bun (JavaScript runtime)
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
source ~/.bashrc
|
||||
|
||||
# 2. Cài Node.js (cần cho PM2)
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# 3. Cài PM2 globally
|
||||
sudo npm install -g pm2
|
||||
|
||||
# 4. Verify installations
|
||||
bun --version
|
||||
node --version
|
||||
pm2 --version
|
||||
```
|
||||
|
||||
### 8.2 Clone và setup project
|
||||
|
||||
```bash
|
||||
# Clone repo (hoặc copy folder mock/charging-station)
|
||||
cd /var/www
|
||||
git clone <repo-url> mbbank-mock
|
||||
cd mbbank-mock/mock/charging-station
|
||||
|
||||
# Cài dependencies
|
||||
bun install
|
||||
```
|
||||
|
||||
### 8.3 Chạy với PM2
|
||||
|
||||
```bash
|
||||
# Start server với PM2
|
||||
pm2 start ecosystem.config.js --env production
|
||||
|
||||
# Hoặc start trực tiếp
|
||||
pm2 start server.js --name "charging-station-mock" --interpreter bun
|
||||
|
||||
# Xem logs
|
||||
pm2 logs charging-station-mock
|
||||
|
||||
# Xem status
|
||||
pm2 status
|
||||
|
||||
# Restart
|
||||
pm2 restart charging-station-mock
|
||||
|
||||
# Stop
|
||||
pm2 stop charging-station-mock
|
||||
|
||||
# Delete
|
||||
pm2 delete charging-station-mock
|
||||
```
|
||||
|
||||
### 8.4 Auto-start khi reboot VPS
|
||||
|
||||
```bash
|
||||
# Generate startup script
|
||||
pm2 startup
|
||||
|
||||
# Save current process list
|
||||
pm2 save
|
||||
```
|
||||
|
||||
### 8.5 Cấu hình Nginx (reverse proxy)
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/charging-station-mock
|
||||
server {
|
||||
listen 80;
|
||||
server_name api-mock.example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3100;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Enable site
|
||||
sudo ln -s /etc/nginx/sites-available/charging-station-mock /etc/nginx/sites-enabled/
|
||||
|
||||
# Test config
|
||||
sudo nginx -t
|
||||
|
||||
# Reload nginx
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 8.6 Cài SSL với Certbot (HTTPS)
|
||||
|
||||
```bash
|
||||
# Cài Certbot
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
|
||||
# Lấy SSL certificate
|
||||
sudo certbot --nginx -d api-mock.example.com
|
||||
|
||||
# Auto-renew (đã tự động setup)
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
### 8.7 PM2 Commands Reference
|
||||
|
||||
| Command | Mô tả |
|
||||
|---------|-------|
|
||||
| `pm2 start ecosystem.config.js` | Start với config file |
|
||||
| `pm2 start server.js --name app` | Start với tên custom |
|
||||
| `pm2 list` | Xem danh sách processes |
|
||||
| `pm2 logs [name]` | Xem logs |
|
||||
| `pm2 monit` | Monitor realtime |
|
||||
| `pm2 restart [name]` | Restart process |
|
||||
| `pm2 reload [name]` | Zero-downtime reload |
|
||||
| `pm2 stop [name]` | Stop process |
|
||||
| `pm2 delete [name]` | Xóa process |
|
||||
| `pm2 save` | Lưu process list |
|
||||
| `pm2 startup` | Setup auto-start |
|
||||
|
||||
### 8.8 Đổi Port
|
||||
|
||||
```bash
|
||||
# Cách 1: Environment variable
|
||||
PORT=3200 pm2 start server.js --name "charging-station-mock" --interpreter bun
|
||||
|
||||
# Cách 2: Sửa ecosystem.config.js
|
||||
# env_production: { PORT: 3200 }
|
||||
pm2 start ecosystem.config.js --env production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Troubleshooting
|
||||
|
||||
### PM2 không nhận Bun
|
||||
|
||||
```bash
|
||||
# Kiểm tra path của bun
|
||||
which bun
|
||||
# Output: /home/user/.bun/bin/bun
|
||||
|
||||
# Sử dụng full path
|
||||
pm2 start server.js --interpreter /home/user/.bun/bin/bun
|
||||
```
|
||||
|
||||
### Port đã được sử dụng
|
||||
|
||||
```bash
|
||||
# Tìm process đang dùng port
|
||||
sudo lsof -i :3100
|
||||
|
||||
# Kill process
|
||||
sudo kill -9 <PID>
|
||||
```
|
||||
|
||||
### Logs quá lớn
|
||||
|
||||
```bash
|
||||
# Rotate logs
|
||||
pm2 install pm2-logrotate
|
||||
|
||||
# Config logrotate
|
||||
pm2 set pm2-logrotate:max_size 10M
|
||||
pm2 set pm2-logrotate:retain 7
|
||||
```
|
||||
332
bun.lock
Normal file
332
bun.lock
Normal file
@@ -0,0 +1,332 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "charging-station-json-server",
|
||||
"dependencies": {
|
||||
"json-server": "^0.17.4",
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="],
|
||||
|
||||
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
|
||||
|
||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||
|
||||
"body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="],
|
||||
|
||||
"compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="],
|
||||
|
||||
"connect-pause": ["connect-pause@0.1.1", "", {}, "sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w=="],
|
||||
|
||||
"content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
|
||||
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
|
||||
|
||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
||||
"errorhandler": ["errorhandler@1.5.2", "", { "dependencies": { "accepts": "~1.3.8", "escape-html": "~1.0.3" } }, "sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="],
|
||||
|
||||
"express-urlrewrite": ["express-urlrewrite@1.4.0", "", { "dependencies": { "debug": "*", "path-to-regexp": "^1.0.3" } }, "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"ignore-by-default": ["ignore-by-default@1.0.1", "", {}, "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="],
|
||||
|
||||
"isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="],
|
||||
|
||||
"jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="],
|
||||
|
||||
"json-parse-helpfulerror": ["json-parse-helpfulerror@1.0.3", "", { "dependencies": { "jju": "^1.1.0" } }, "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg=="],
|
||||
|
||||
"json-server": ["json-server@0.17.4", "", { "dependencies": { "body-parser": "^1.19.0", "chalk": "^4.1.2", "compression": "^1.7.4", "connect-pause": "^0.1.1", "cors": "^2.8.5", "errorhandler": "^1.5.1", "express": "^4.17.1", "express-urlrewrite": "^1.4.0", "json-parse-helpfulerror": "^1.0.3", "lodash": "^4.17.21", "lodash-id": "^0.14.1", "lowdb": "^1.0.0", "method-override": "^3.0.0", "morgan": "^1.10.0", "nanoid": "^3.1.23", "please-upgrade-node": "^3.2.0", "pluralize": "^8.0.0", "server-destroy": "^1.0.1", "yargs": "^17.0.1" }, "bin": { "json-server": "lib/cli/bin.js" } }, "sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ=="],
|
||||
|
||||
"lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
|
||||
|
||||
"lodash-id": ["lodash-id@0.14.1", "", {}, "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg=="],
|
||||
|
||||
"lowdb": ["lowdb@1.0.0", "", { "dependencies": { "graceful-fs": "^4.1.3", "is-promise": "^2.1.0", "lodash": "4", "pify": "^3.0.0", "steno": "^0.4.1" } }, "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
|
||||
|
||||
"method-override": ["method-override@3.0.0", "", { "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", "parseurl": "~1.3.2", "vary": "~1.1.2" } }, "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA=="],
|
||||
|
||||
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
|
||||
|
||||
"mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||
|
||||
"morgan": ["morgan@1.10.1", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
|
||||
|
||||
"nodemon": ["nodemon@3.1.14", "", { "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^10.2.1", "pstree.remy": "^1.1.8", "semver": "^7.5.3", "simple-update-notifier": "^2.0.0", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" } }, "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
|
||||
|
||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
|
||||
|
||||
"please-upgrade-node": ["please-upgrade-node@3.2.0", "", { "dependencies": { "semver-compare": "^1.0.0" } }, "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg=="],
|
||||
|
||||
"pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"pstree.remy": ["pstree.remy@1.1.8", "", {}, "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="],
|
||||
|
||||
"qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
|
||||
|
||||
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
|
||||
|
||||
"send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="],
|
||||
|
||||
"serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
|
||||
|
||||
"server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="],
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||
|
||||
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
||||
|
||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"steno": ["steno@0.4.4", "", { "dependencies": { "graceful-fs": "^4.1.3" } }, "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"touch": ["touch@3.1.1", "", { "bin": { "nodetouch": "bin/nodetouch.js" } }, "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA=="],
|
||||
|
||||
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
|
||||
|
||||
"undefsafe": ["undefsafe@2.0.5", "", {}, "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"express-urlrewrite/path-to-regexp": ["path-to-regexp@1.9.0", "", { "dependencies": { "isarray": "0.0.1" } }, "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g=="],
|
||||
|
||||
"finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"method-override/debug": ["debug@3.1.0", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g=="],
|
||||
|
||||
"morgan/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"morgan/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="],
|
||||
|
||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"method-override/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"morgan/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
}
|
||||
}
|
||||
246
db.json
Normal file
246
db.json
Normal file
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"homeData": {
|
||||
"result": {
|
||||
"ok": true,
|
||||
"responseCode": "00",
|
||||
"message": "Thành công"
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"accountNumber": "012345678",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "DEFAULT",
|
||||
"balance": "151,000,000",
|
||||
"currency": "VND",
|
||||
"isDefault": true
|
||||
},
|
||||
{
|
||||
"accountNumber": "012345679",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "APPLE_PAY",
|
||||
"balance": "25,500,000",
|
||||
"currency": "VND",
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"accountNumber": "012345680",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "OVERDRAFT",
|
||||
"balance": "50,000,000",
|
||||
"currency": "VND",
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"accountNumber": "012345681",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "SALARY",
|
||||
"balance": "88,200,000",
|
||||
"currency": "VND",
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"accountNumber": "012345682",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "DEFAULT",
|
||||
"balance": "5,000",
|
||||
"currency": "USD",
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"accountNumber": "012345683",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"type": "DEFAULT",
|
||||
"balance": "2,800",
|
||||
"currency": "EUR",
|
||||
"isDefault": false
|
||||
}
|
||||
],
|
||||
"businessAccounts": [
|
||||
{
|
||||
"accountNumber": "198765432",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"businessName": "Cửa hàng Tạp hóa Minh Anh",
|
||||
"balance": "320,500,000",
|
||||
"currency": "VND"
|
||||
},
|
||||
{
|
||||
"accountNumber": "198765433",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"businessName": "Quán Cafe Sunrise",
|
||||
"balance": "85,200,000",
|
||||
"currency": "VND"
|
||||
},
|
||||
{
|
||||
"accountNumber": "198765434",
|
||||
"accountName": "NGUYEN VAN A",
|
||||
"businessName": "Shop Online ABC",
|
||||
"balance": "12,500",
|
||||
"currency": "USD"
|
||||
}
|
||||
],
|
||||
"loyaltyPoints": "2,450",
|
||||
"cashbackAmount": "888,000",
|
||||
"memberLevel": "GOLD",
|
||||
"recentTransactions": [
|
||||
{
|
||||
"transactionId": "TXN20260325001234",
|
||||
"amount": "1,000,000",
|
||||
"status": "SUCCESS",
|
||||
"executedAt": "2026-03-25T10:30:00Z",
|
||||
"description": "Nạp tiền tài khoản",
|
||||
"accountNumber": "012345678"
|
||||
},
|
||||
{
|
||||
"transactionId": "TXN20260325001235",
|
||||
"amount": "500,000",
|
||||
"status": "SUCCESS",
|
||||
"executedAt": "2026-03-24T08:20:00Z",
|
||||
"description": "Nạp tiền tài khoản",
|
||||
"accountNumber": "012345678"
|
||||
},
|
||||
{
|
||||
"transactionId": "TXN20260325001236",
|
||||
"amount": "2,200,000",
|
||||
"status": "PENDING",
|
||||
"executedAt": "2026-03-22T12:00:00Z",
|
||||
"description": "Nạp tiền tài khoản",
|
||||
"accountNumber": "012345678"
|
||||
}
|
||||
]
|
||||
},
|
||||
"searchSeed": {
|
||||
"recentSearches": [
|
||||
"Chuyển tiền nhanh",
|
||||
"Nạp điện thoại",
|
||||
"Thanh toán hóa đơn"
|
||||
],
|
||||
"recommendedFeatures": [
|
||||
{
|
||||
"code": "MOVE_MONEY",
|
||||
"name": "Chuyển tiền",
|
||||
"iconCode": "moveMoney"
|
||||
},
|
||||
{
|
||||
"code": "TOPUP",
|
||||
"name": "Nạp tiền ĐT",
|
||||
"iconCode": "topup"
|
||||
},
|
||||
{
|
||||
"code": "DEPOSIT",
|
||||
"name": "Tiết kiệm",
|
||||
"iconCode": "deposit"
|
||||
},
|
||||
{
|
||||
"code": "LOAN",
|
||||
"name": "Vay vốn",
|
||||
"iconCode": "loan"
|
||||
}
|
||||
],
|
||||
"recommendedChips": [
|
||||
{
|
||||
"id": "C1",
|
||||
"label": "Dán chuyển tiền",
|
||||
"isNew": true,
|
||||
"actionCode": "TRANSFER_LABEL"
|
||||
},
|
||||
{
|
||||
"id": "C2",
|
||||
"label": "Vay tiêu dùng",
|
||||
"isNew": false,
|
||||
"actionCode": "CONSUMER_LOAN"
|
||||
},
|
||||
{
|
||||
"id": "C3",
|
||||
"label": "Nạp điện thoại tự động",
|
||||
"isNew": true,
|
||||
"actionCode": "AUTO_TOPUP"
|
||||
}
|
||||
],
|
||||
"suggestions": [
|
||||
{
|
||||
"id": "SUG001",
|
||||
"title": "Chuyển tiền nhanh 24/7",
|
||||
"description": "Chuyển tiền tức thì đến mọi ngân hàng, không mất phí",
|
||||
"type": "FEATURE",
|
||||
"actionCode": "TRANSFER"
|
||||
},
|
||||
{
|
||||
"id": "SUG002",
|
||||
"title": "Ưu đãi hoàn tiền 10% khi nạp điện thoại",
|
||||
"description": "Áp dụng từ 01/03 - 31/03/2026 cho thẻ MB",
|
||||
"type": "PROMOTION",
|
||||
"actionCode": "TOPUP_PHONE"
|
||||
},
|
||||
{
|
||||
"id": "SUG003",
|
||||
"title": "Mẹo quản lý chi tiêu thông minh",
|
||||
"description": "Thiết lập ngân sách theo tuần để tối ưu tiền nhàn rỗi",
|
||||
"type": "TIP",
|
||||
"actionCode": "SPENDING_TIP"
|
||||
},
|
||||
{
|
||||
"id": "SUG004",
|
||||
"title": "Mở sổ tiết kiệm online",
|
||||
"description": "Lãi suất cao hơn quầy, gửi tiền chỉ 30 giây",
|
||||
"type": "FEATURE",
|
||||
"actionCode": "OPEN_DEPOSIT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rewardSeed": {
|
||||
"banners": [
|
||||
{
|
||||
"id": "BN001",
|
||||
"imageUrl": "/images/banners/banner_01.png",
|
||||
"redirectUrl": "https://www.mbbank.com.vn/promotion/summer-sale"
|
||||
},
|
||||
{
|
||||
"id": "BN002",
|
||||
"imageUrl": "/images/banners/banner_01.png",
|
||||
"redirectUrl": "https://www.mbbank.com.vn/promotion/cashback-50"
|
||||
},
|
||||
{
|
||||
"id": "BN003",
|
||||
"imageUrl": "/images/banners/banner_01.png",
|
||||
"redirectUrl": "https://www.mbbank.com.vn/promotion/new-year-2026"
|
||||
}
|
||||
],
|
||||
"vouchers": [
|
||||
{
|
||||
"id": "V001",
|
||||
"imageUrl": "/images/vouchers/starbucks.png",
|
||||
"content": "Giảm 40% tại Starbucks cho đơn từ 100K",
|
||||
"expiry": "Hạn: 25/06/2026",
|
||||
"requireCardSpending": false
|
||||
},
|
||||
{
|
||||
"id": "V002",
|
||||
"imageUrl": "/images/vouchers/kfc.png",
|
||||
"content": "Giảm 68K cho combo gà rán KFC",
|
||||
"expiry": "",
|
||||
"requireCardSpending": true
|
||||
},
|
||||
{
|
||||
"id": "V003",
|
||||
"imageUrl": "/images/vouchers/bamboo.png",
|
||||
"content": "Đồng giá 39K",
|
||||
"expiry": "Hạn: 15/05/2026",
|
||||
"requireCardSpending": false
|
||||
},
|
||||
{
|
||||
"id": "V004",
|
||||
"imageUrl": "/images/vouchers/kfc_2.png",
|
||||
"content": "Giảm 68K",
|
||||
"expiry": "Hạn: 30/04/2026",
|
||||
"requireCardSpending": true
|
||||
},
|
||||
{
|
||||
"id": "V005",
|
||||
"imageUrl": "/images/vouchers/thai_air.png",
|
||||
"content": "Vé giảm 50%",
|
||||
"expiry": "Hạn: 30/09/2026",
|
||||
"requireCardSpending": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
21
ecosystem.config.js
Normal file
21
ecosystem.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'charging-station-mock',
|
||||
script: 'server.js',
|
||||
interpreter: 'bun',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '500M',
|
||||
env: {
|
||||
NODE_ENV: 'development',
|
||||
PORT: 3100,
|
||||
},
|
||||
env_production: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3100,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "charging-station-json-server",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Mock API server for Charging Station feature",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "bun server.js",
|
||||
"start:watch": "nodemon --watch db.json --watch server.js --exec \"bun server.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"json-server": "^0.17.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
0
public/images/banners/.gitkeep
Normal file
0
public/images/banners/.gitkeep
Normal file
BIN
public/images/banners/banner_01.png
Normal file
BIN
public/images/banners/banner_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 324 KiB |
0
public/images/vouchers/.gitkeep
Normal file
0
public/images/vouchers/.gitkeep
Normal file
BIN
public/images/vouchers/bamboo.png
Normal file
BIN
public/images/vouchers/bamboo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
public/images/vouchers/kfc.png
Normal file
BIN
public/images/vouchers/kfc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
public/images/vouchers/kfc_2.png
Normal file
BIN
public/images/vouchers/kfc_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
BIN
public/images/vouchers/starbucks.png
Normal file
BIN
public/images/vouchers/starbucks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
public/images/vouchers/thai_air.png
Normal file
BIN
public/images/vouchers/thai_air.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
237
server.js
Normal file
237
server.js
Normal file
@@ -0,0 +1,237 @@
|
||||
import path from 'node:path';
|
||||
import jsonServer from 'json-server';
|
||||
|
||||
const server = jsonServer.create();
|
||||
const router = jsonServer.router(path.join(import.meta.dir, 'db.json'));
|
||||
|
||||
// Serve static files từ folder public (images, etc.)
|
||||
const middlewares = jsonServer.defaults({
|
||||
logger: true,
|
||||
static: path.join(import.meta.dir, 'public'),
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3100;
|
||||
// BASE_URL có thể set từ env khi deploy lên VPS (vd: https://api-mock.example.com)
|
||||
const baseUrl = process.env.BASE_URL || `http://localhost:${port}`;
|
||||
|
||||
server.use(middlewares);
|
||||
server.use(jsonServer.bodyParser);
|
||||
|
||||
server.use((req, _res, next) => {
|
||||
req.body = req.body || {};
|
||||
next();
|
||||
});
|
||||
|
||||
const formatCurrencyVnd = (value) => {
|
||||
const number = Number(String(value || '').replace(/[^0-9]/g, ''));
|
||||
if (Number.isNaN(number) || number <= 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat('en-US').format(number);
|
||||
};
|
||||
|
||||
const buildResult = ({ ok = true, responseCode = '00', message = 'Thành công' } = {}) => ({
|
||||
ok,
|
||||
responseCode,
|
||||
message,
|
||||
});
|
||||
|
||||
const normalizeText = (value) => String(value || '').toLowerCase().trim();
|
||||
|
||||
// Validation constants theo spec
|
||||
const ACCOUNT_NUMBER_LENGTH = 9;
|
||||
const MAX_AMOUNT = 1000000000; // 1 tỷ VND
|
||||
const ACCOUNT_NUMBER_REGEX = /^[0-9]{9}$/;
|
||||
const ACCOUNT_NAME_REGEX = /^[a-zA-ZÀ-ỹ\s]*$/;
|
||||
|
||||
server.post('/charging-station/execute', (req, res) => {
|
||||
const payload = req.body;
|
||||
const accountNumber = String(payload.accountNumber || '').trim();
|
||||
const accountName = String(payload.accountName || '').trim();
|
||||
const amountRaw = String(payload.amount || '').trim();
|
||||
const amountNumber = Number(amountRaw.replace(/[^0-9]/g, ''));
|
||||
|
||||
// V1, V4: Kiểm tra rỗng
|
||||
if (!accountNumber || !amountRaw || Number.isNaN(amountNumber) || amountNumber <= 0) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '01',
|
||||
message: 'Dữ liệu không hợp lệ',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// V2, V3: Kiểm tra accountNumber đúng 9 ký tự và chỉ chứa số
|
||||
if (!ACCOUNT_NUMBER_REGEX.test(accountNumber)) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '01',
|
||||
message: accountNumber.length !== ACCOUNT_NUMBER_LENGTH
|
||||
? `Số tài khoản phải có ${ACCOUNT_NUMBER_LENGTH} ký tự`
|
||||
: 'Số tài khoản chỉ được chứa số',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// V6: Kiểm tra accountName không chứa ký tự đặc biệt hoặc số
|
||||
if (accountName && !ACCOUNT_NAME_REGEX.test(accountName)) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '01',
|
||||
message: 'Tên chỉ được chứa chữ cái và khoảng trắng',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// V5: Kiểm tra amount không vượt quá 1 tỷ
|
||||
if (amountNumber > MAX_AMOUNT) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '03',
|
||||
message: `Số tiền tối đa là ${formatCurrencyVnd(MAX_AMOUNT)} VND`,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// Simulate: Số tài khoản bắt đầu bằng 999 không tồn tại
|
||||
if (accountNumber.startsWith('999')) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '01',
|
||||
message: 'Số tài khoản không tồn tại',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// Simulate: Số tiền > 500 triệu và TK bắt đầu bằng 111 → số dư không đủ
|
||||
if (amountNumber > 500000000 && accountNumber.startsWith('111')) {
|
||||
return res.status(200).json({
|
||||
result: buildResult({
|
||||
ok: false,
|
||||
responseCode: '02',
|
||||
message: 'Số dư không đủ',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const transactionSuffix = String(now.getTime()).slice(-10);
|
||||
|
||||
return res.status(200).json({
|
||||
result: buildResult(),
|
||||
transactionId: `TXN${transactionSuffix}`,
|
||||
status: 'SUCCESS',
|
||||
message: 'Nạp tiền thành công',
|
||||
accountNumber,
|
||||
accountName,
|
||||
amount: formatCurrencyVnd(amountNumber),
|
||||
executedAt: now.toISOString(),
|
||||
refNo: `REF${String(now.getTime()).slice(-8)}`,
|
||||
});
|
||||
});
|
||||
|
||||
server.post('/charging-station/getHomeData', (_req, res) => {
|
||||
const db = router.db;
|
||||
const homeData = db.get('homeData').value();
|
||||
return res.status(200).json(homeData);
|
||||
});
|
||||
|
||||
server.post('/charging-station/search', (req, res) => {
|
||||
const db = router.db;
|
||||
const searchSeed = db.get('searchSeed').value();
|
||||
const query = normalizeText(req.body.query);
|
||||
|
||||
const baseResponse = {
|
||||
result: buildResult(),
|
||||
recentSearches: searchSeed.recentSearches,
|
||||
recommendedFeatures: searchSeed.recommendedFeatures,
|
||||
recommendedChips: searchSeed.recommendedChips,
|
||||
};
|
||||
|
||||
if (!query) {
|
||||
return res.status(200).json({
|
||||
...baseResponse,
|
||||
suggestions: searchSeed.suggestions,
|
||||
});
|
||||
}
|
||||
|
||||
const suggestions = searchSeed.suggestions.filter((item) => {
|
||||
const title = normalizeText(item.title);
|
||||
const description = normalizeText(item.description);
|
||||
return title.includes(query) || description.includes(query);
|
||||
});
|
||||
|
||||
const recentSearches = [req.body.query, ...searchSeed.recentSearches]
|
||||
.filter(Boolean)
|
||||
.filter((item, index, array) => array.indexOf(item) === index)
|
||||
.slice(0, 10);
|
||||
|
||||
return res.status(200).json({
|
||||
...baseResponse,
|
||||
suggestions,
|
||||
recentSearches,
|
||||
});
|
||||
});
|
||||
|
||||
// Helper: build full image URL từ relative path
|
||||
const buildImageUrl = (relativePath) => {
|
||||
if (!relativePath) return null;
|
||||
// Nếu đã là URL đầy đủ thì giữ nguyên
|
||||
if (relativePath.startsWith('http://') || relativePath.startsWith('https://')) {
|
||||
return relativePath;
|
||||
}
|
||||
return `${baseUrl}${relativePath.startsWith('/') ? '' : '/'}${relativePath}`;
|
||||
};
|
||||
|
||||
server.post('/charging-station/reward/getList', (req, res) => {
|
||||
const db = router.db;
|
||||
const rewardSeed = db.get('rewardSeed').value();
|
||||
const page = Number(req.body.page || 1);
|
||||
const pageSize = Number(req.body.pageSize || 10);
|
||||
|
||||
const safePage = Number.isNaN(page) || page < 1 ? 1 : page;
|
||||
const safePageSize = Number.isNaN(pageSize) || pageSize < 1 ? 10 : pageSize;
|
||||
|
||||
const start = (safePage - 1) * safePageSize;
|
||||
const end = start + safePageSize;
|
||||
|
||||
// Map vouchers với full image URL
|
||||
const vouchers = rewardSeed.vouchers.slice(start, end).map((v) => ({
|
||||
...v,
|
||||
imageUrl: buildImageUrl(v.imageUrl),
|
||||
}));
|
||||
|
||||
// Banners chỉ trả về ở page 1, với full image URL
|
||||
const banners =
|
||||
safePage === 1
|
||||
? rewardSeed.banners.map((b) => ({
|
||||
...b,
|
||||
imageUrl: buildImageUrl(b.imageUrl),
|
||||
}))
|
||||
: [];
|
||||
|
||||
return res.status(200).json({
|
||||
result: buildResult(),
|
||||
banners,
|
||||
vouchers,
|
||||
paging: {
|
||||
page: safePage,
|
||||
pageSize: safePageSize,
|
||||
total: rewardSeed.vouchers.length,
|
||||
hasMore: end < rewardSeed.vouchers.length,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
server.use(router);
|
||||
|
||||
server.listen(port, () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Charging Station mock API is running at http://localhost:${port}`);
|
||||
});
|
||||
Reference in New Issue
Block a user