# AutoShopee Internal API

Tài liệu chi tiết tất cả API nội bộ AutoShopee được sử dụng trong extension.

## Tổng quan kiến truc

### Base URLs

| Service | URL | Mô tả |
|---------|-----|--------|
| Main API | `https://autoshopee.com/api/v1` | API chính (xác thực, user, shop, extension) |
| CMS API | `https://content.autoshopee.com` | Nội dung CMS (ecosystem, extension info) |
| PDF Service | `https://service-3rd.autoshopee.com` | Xử lý file PDF (parse, extract) |
| S3 Storage | `https://s3-store-api.autoshopee.com` | Lưu trữ file S3 (PDF đơn hàng) |
| CouchDB | `https://store-extension-api.autoshopee.com` | Đồng bộ PouchDB <-> CouchDB |
| KV Store | `https://store-kv-api.autoshopee.com` | Key-Value storage |
| Socket.io | `wss://ws.autoshopee.com` | WebSocket real-time events |
| Worker Socket | `wss://worker-shopee-ws.autoshopee.com` | Worker WebSocket proxy |

### Axios Instances

| Instance | Base URL | Auth | Mô tả |
|----------|----------|------|--------|
| `apiApp` | Main API | Bearer token (tự động gắn qua interceptor) | API chính, yêu cầu đăng nhập |
| `apiPublic` | Main API | Không | API công khai, không cần đăng nhập |
| `apiCms` | CMS URL | Không | API nội dung CMS |
| `apiBridge` | Main API | Bearer token (thủ công qua header) | Gọi API qua bridge (content script -> background) |
| `autoshopeeS3Client` | S3 Storage | Không | Upload/download file S3 |

Tất cả Axios instance đều sử dụng `fetchAdapter` để tương thích với Service Worker (Chrome MV3). Timeout mặc định: **60 giây**.

---

## REST API

### Xac thuc (Auth)

**File:** `src/app/api/autoshopee/user.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `loginUser` | POST | `/extension/login` | `username`, `password`, `profileId`, `version` | Đăng nhập người dùng. Gửi kèm version extension hiện tại |
| `getUserInfo` | GET | `/extension/user/get/me` | -- | Lấy thông tin người dùng đang đăng nhập (profile, subscription, settings) |
| `checkProfileId` | GET | `/extension/user/profile/check` | `profileId` (query) | Kiểm tra profile ID đã tồn tại hay chưa |

---

### CMS (Noi dung)

**File:** `src/app/api/autoshopee/cms.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `fetchListEcosystem` | GET | `/ecosystems` | `isCached` (local) | Lấy danh sách hệ sinh thái AutoShopee. Hỗ trợ cache local (`CacheCmsContent`) |
| `fetchExtensionInfo` | GET | `/extension` | `isCached` (local) | Lấy thông tin extension (version, logo, changelog). Hỗ trợ cache local |
| `fetchPlatformConfig` | GET | `/platform-config` | `isCached` (local) | Lấy cấu hình platform (Shopee, Lazada, TikTok). Hỗ trợ cache local |

> **Ghi chú:** CMS API sử dụng instance `apiCms` với base URL riêng (`cmsUrl`). Các hàm đều hỗ trợ caching thông qua `CacheCmsContent` -- nếu `isCached = true` (mặc định), dữ liệu được lấy từ cache trước, chỉ gọi API khi cache trống.

---

### Public API (Cong khai)

**File:** `src/app/api/autoshopee/public.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `getAffLink` | GET | `/public/tracking/get` | `platform` (query) | Lấy link affiliate tracking theo platform |
| `downloadVideoShopee` | POST | `/public/shopee/product/downloadVideo` | `videoId` (body) | Tải video sản phẩm Shopee theo video ID |
| `getShopeeProductExtraInfo` | GET | `/public/shopee/productSync/data` | `shopId`, `productId` (query) | Lấy thông tin bổ sung sản phẩm Shopee (dữ liệu đồng bộ) |

> **Ghi chú:** Public API sử dụng instance `apiPublic` -- không cần xác thực Bearer token.

---

### Database (CouchDB)

**File:** `src/app/api/autoshopee/db.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `getCouchDatabase` | POST | `/extension/db/get` | `databaseName` (body) | Lấy thông tin kết nối CouchDB database. Trả về `{ connect, db, url }` |
| `createCouchDatabase` | POST | `/extension/db/create` | `databaseName` (body) | Tạo mới CouchDB database cho user. Trả về `{ connect, db, url }` |
| `fetchCouchDatabase` | -- | -- | `databaseName` | Hàm wrapper: thử `getCouchDatabase` trước, nếu chưa có thì gọi `createCouchDatabase` |

> **Ghi chú:** Đây là API quản lý CouchDB database phía server. Mỗi user có database riêng. Kết quả trả về chứa URL kết nối dùng cho PouchDB sync.

---

### Shopee - Quan ly Shop

**File:** `src/app/api/autoshopee/shopee/shop.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `shopeeShopSyncSellerCreate` | POST | `/shopee/shop/create` | `payload` (body) | Tạo shop Shopee từ seller (qua server-side API) |
| `shopeeShopSync` | POST | `/extension/shopee/shop/sync` | `payload` (body) | Đồng bộ thông tin shop Shopee từ extension |
| `shopeeShopCreate` | POST | `/extension/shopee/shop/create` | `payload` (body) | Tạo mới shop Shopee từ extension |
| `shopeeShopGetShopId` | GET | `/extension/shopee/shop/get` | `shopId` (query) | Lấy thông tin shop theo shop ID |
| `shopeeShopUpdateShopId` | POST | `/extension/shopee/shop/update/:shopId` | `shopId` (path), `payload` (body) | Cập nhật thông tin shop |
| `shopeeShopRemoveShopId` | POST | `/extension/shopee/shop/remove/:shopId` | `shopId` (path) | Xóa shop khỏi hệ thống |
| `shopeeShopSyncFromBackground` | POST | `/extension/shopee/syncData` | `payload` (body) | Đồng bộ dữ liệu shop từ background service worker |

---

### Shopee - Tu khoa Quang cao (Ads Keywords)

**File:** `src/app/api/autoshopee/shopee/keyword.js`

#### Bo suu tap tu khoa (Keyword Collection)

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `apiCollectionCreate` | POST | `/extension/shopee/ads/keyword/create` | `payload` (body) | Tạo bộ sưu tập từ khóa quảng cáo mới |
| `apiCollectionList` | GET | `/extension/shopee/ads/keyword/list` | -- | Lấy danh sách tất cả bộ sưu tập từ khóa |
| `apiCollectionRemove` | POST | `/extension/shopee/ads/keyword/remove/:id` | `id` (path) | Xóa bộ sưu tập từ khóa |
| `apiCollectionUpdate` | POST | `/extension/shopee/ads/keyword/update/:id` | `id` (path), `payload` (body) | Cập nhật bộ sưu tập từ khóa |

#### Tu khoa trong bo suu tap (Keyword Items)

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `apiKeywordList` | GET | `/extension/shopee/ads/keyword/item/list/:collectionId` | `collectionId` (path) | Lấy danh sách từ khóa trong bộ sưu tập |
| `apiKeywordAdd` | POST | `/extension/shopee/ads/keyword/item/add/:collectionId` | `collectionId` (path), `payload` (body) | Thêm từ khóa vào bộ sưu tập |
| `apiKeywordRemove` | POST | `/extension/shopee/ads/keyword/item/remove/:id` | `id` (path) | Xóa từ khóa khỏi bộ sưu tập |
| `apiKeywordUpdate` | POST | `/extension/shopee/ads/keyword/item/update/:id` | `id` (path), `payload` (body) | Cập nhật từ khóa trong bộ sưu tập |

---

### Shopee - Flash Sale

**File:** `src/app/api/autoshopee/shopee/flashsale.js`

#### Flash Sale Scheduler

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `apiFlashsaleSchedulerList` | GET | `/extension/shopee/flashsale/scheduler/list` | `shopId` (query) | Lấy danh sách lịch flash sale theo shop |
| `apiFlashsaleSchedulerSlot` | GET | `/extension/shopee/flashsale/scheduler/slot` | `shopId` (query) | Lấy danh sách slot flash sale khả dụng theo shop |
| `apiFlashsaleSchedulerGetId` | GET | `/extension/shopee/flashsale/scheduler/get/:refId` | `refId` (path) | Lấy chi tiết một lịch flash sale |
| `apiFlashsaleSchedulerCreate` | POST | `/extension/shopee/flashsale/scheduler/create` | `payload` (body) | Tạo lịch flash sale mới |
| `apiFlashsaleSchedulerRemove` | POST | `/extension/shopee/flashsale/scheduler/remove/:removeId` | `removeId` (path) | Xóa lịch flash sale |
| `apiFlashsaleSchedulerSetId` | POST | `/extension/shopee/flashsale/scheduler/set/:setId` | `setId` (path), `flashsaleId` (body) | Gắn flash sale ID vào scheduler |

#### Flash Sale Campaign

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `apiFlashsaleCampaignList` | GET | `/extension/shopee/flashsale/campaign/list` | `shopId` (query) | Lấy danh sách chiến dịch flash sale theo shop |
| `apiFlashsaleCampaignCreate` | POST | `/extension/shopee/flashsale/campaign/create` | `payload` (body) | Tạo chiến dịch flash sale mới |
| `apiFlashsaleCampaignUpdate` | POST | `/extension/shopee/flashsale/campaign/update/:updateId` | `updateId` (path), `payload` (body) | Cập nhật chiến dịch flash sale |
| `apiFlashsaleCampaignRemove` | POST | `/extension/shopee/flashsale/campaign/remove/:removeId` | `removeId` (path) | Xóa chiến dịch flash sale |
| `apiFlashsaleCampaignGetId` | GET | `/extension/shopee/flashsale/campaign/get/:campaignId` | `campaignId` (path) | Lấy chi tiết chiến dịch flash sale |

---

### Lazada

**File:** `src/app/api/autoshopee/lazada/index.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `getListShop` | GET | `/lazada/shop/list` | -- | Lấy danh sách shop Lazada đã kết nối |
| `refreshToken` | GET | `/lazada/shop/refresh/:id` | `id` (path) | Làm mới token xác thực shop Lazada |
| `importProduct` | POST | `/lazada/product/import` | `data` (product), `option` (body) | Import sản phẩm vào Lazada |
| `saveUpload` | POST | `/lazada/upload/single/save` | `shopRef`, `warehouseId`, `product` (body) | Lưu sản phẩm chuẩn bị upload lên Lazada |
| `handlerUpload` | POST | `/lazada/upload/product/run/:id` | `id` (path) | Thực thi upload sản phẩm đã lưu lên Lazada |

---

### TikTok Shop

**File:** `src/app/api/autoshopee/tiktok/index.js`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `getListShop` | GET | `/tiktok/shop/list` | -- | Lấy danh sách shop TikTok đã kết nối |
| `refreshToken` | GET | `/tiktok/shop/refresh/:id` | `id` (path) | Làm mới token xác thực shop TikTok |
| `getShopWarehouse` | GET | `/tiktok/handler/warehouse/:id` | `id` (path) | Lấy danh sách kho hàng của shop TikTok |
| `importProduct` | POST | `/tiktok/product/import` | `data` (product), `option` (body) | Import sản phẩm vào TikTok Shop |
| `saveUpload` | POST | `/tiktok/upload/single/save` | `shopRef`, `warehouseId`, `product` (body) | Lưu sản phẩm chuẩn bị upload lên TikTok |
| `handlerUpload` | POST | `/tiktok/upload/product/run/:id` | `id` (path) | Thực thi upload sản phẩm đã lưu lên TikTok |

---

### Dich vu PDF

**File:** `src/app/api/autoshopee/service/pdf.js`

**Base URL:** `https://service-3rd.autoshopee.com`

Tất cả endpoint đều nhận file PDF qua `FormData` (field `file`, content-type `application/pdf`).

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `parsePdfToHtml` | POST | `/pdftohtml` | PDF buffer (FormData) | Chuyển đổi file PDF sang HTML |
| `parsePdfToText` | POST | `/pdftotext` | PDF buffer (FormData) | Chuyển đổi file PDF sang plain text |
| `parsePdfInfo` | POST | `/pdfinfo` | PDF buffer (FormData) | Trích xuất metadata PDF (số trang, kích thước, ngày tạo...) |
| `parsePdfData` | -- | -- | PDF buffer | Hàm tổng hợp: gọi `parsePdfToHtml`, sau đó extract text bằng `html-to-text` và đếm số trang. Trả về `{ html, text, page }` |

> **Ghi chú:** Service PDF dùng `fetch()` trực tiếp thay vì Axios. Kết quả `parsePdfData` đã được clean (loại bỏ base64 images, whitespace thừa).

---

### Dich vu S3 Storage

**File:** `src/app/api/autoshopee/service/s3.js`

**Base URL:** `https://s3-store-api.autoshopee.com`

| Ham | Method | Endpoint | Tham so | Mo ta |
|-----|--------|----------|---------|-------|
| `uploadS3OrderPrintPdf` | PUT | `/order-print/:fileName` | `buffer` (ArrayBuffer), `fileName`, `metadataPayload` (headers x-amz-meta-*) | Upload file PDF in đơn hàng lên S3. Trả về `{ fileId, fileUrl }` |
| `getS3FileUrl` | -- | -- | `filePath` | Hàm helper tạo URL truy cập file S3: `{S3_API_URL}/{filePath}` |

#### S3 Buckets

| Bucket | Mô tả |
|--------|--------|
| `order-print` | Lưu trữ file PDF in đơn hàng |

---

## WebSocket (Socket.io)

### App Socket

**File:** `src/app/socket/app.js`

**URL:** `wss://ws.autoshopee.com` (env: `VITE_SOCKET_URL`)

#### Cau hinh ket noi

```javascript
{
  auth: {
    ref: 'extension',
    version: configApp.verText,
    manifest: configApp.manifest,
    token: userSetting.userToken.get(),
    username: userSetting.userName.get(),
    id: userSetting.userId.get(),
  },
  transports: ['websocket'],
  reconnectionAttempts: 5,
  reconnectionDelay: 2000,
}
```

#### Events

| Event | Hướng | Mô tả |
|-------|-------|--------|
| `connect` | Client -> Server | Kết nối thành công. Log socket ID |
| `disconnected` | Server -> Client | Ngắt kết nối |
| `extension:auth` | Client -> Server | Xác thực extension với server |

**File khởi tạo:** `src/app/socket/index.js` -- Hàm `socketService()` tạo kết nối socket và lắng nghe event `connect`.

---

### Worker Socket

**File:** `src/app/socket/worker.js`

**URL:** `wss://worker-shopee-ws.autoshopee.com` (env: `VITE_WORKER_SOCKET`)

#### Cau hinh ket noi

```javascript
{
  transports: ['websocket'],
  reconnectionAttempts: 5,
  reconnectionDelay: 2000,
}
```

> **Ghi chú:** Worker Socket **không gửi auth payload** khi kết nối -- xác thực được thực hiện qua event `workerSetIndentify`.

#### Events

| Event | Hướng | Mô tả |
|-------|-------|--------|
| `connect` | Client -> Server | Kết nối thành công |
| `disconnected` | Server -> Client | Ngắt kết nối |
| `fetch-url-worker-id` | Server -> Client | Nhận worker ID từ server |
| `fetch-url-job-request` | Client -> Server | Gửi yêu cầu fetch URL đến worker |
| `fetch-url-job-recived` | Server -> Client | Nhận kết quả fetch URL từ worker |
| `worker-set-indentify` | Client -> Server | Đăng ký identity của worker (xác thực) |
| `worker-ready-up` | Client -> Server | Thông báo worker sẵn sàng nhận việc |
| `worker-ready-down` | Client -> Server | Thông báo worker ngừng nhận việc |

---

## Database Sync (PouchDB / CouchDB)

### ModelApiLoader

**File:** `src/app/api/model.js`

`ModelApiLoader` kế thừa từ `PouchDB`, cung cấp lớp base cho tất cả model dữ liệu local. Mỗi model tương ứng với một PouchDB database (lưu trên IndexedDB) và có thể đồng bộ hai chiều với CouchDB remote.

#### Plugins PouchDB

| Plugin | Mô tả |
|--------|--------|
| `pouchdb-find` | Mango query selectors (tìm kiếm nâng cao) |
| `pouchdb-authentication` | Xác thực CouchDB (username/password, session) |
| `relational-pouch` | Quan hệ giữa các document |
| `pouchdb-upsert` | Insert hoặc update document (upsert) |

#### Cac phuong thuc CRUD

| Phương thức | Tham số | Mô tả |
|-------------|---------|--------|
| `findAll()` | -- | Lấy tất cả documents (bao gồm attachments) |
| `findMany(where, limit, skip, sort, projection)` | `where`: selector, `limit`: max 1000, `skip`: offset, `sort`: sắp xếp, `projection`: fields cần lấy | Tìm nhiều documents theo điều kiện. Limit giới hạn 1-1000, mặc định 100 |
| `findOne(where)` | `where`: selector | Tìm một document theo điều kiện |
| `countAll()` | -- | Đếm tổng số documents |
| `count(where)` | `where`: selector | Đếm documents theo điều kiện. Giới hạn 10,000 |
| `insert(payload, _id)` | `payload`: dữ liệu, `_id`: ID tùy chọn | Thêm mới hoặc upsert document. Tự động gắn `createdAt`, `updatedAt` |
| `getId(id)` | `id`: document ID | Lấy document theo ID. Trả về `null` nếu không tìm thấy |
| `updateId(id, payload, isValidate)` | `id`, `payload`, `isValidate`: có validate schema không (mặc định `true`) | Cập nhật document theo ID. Tự động gắn `updatedAt` |
| `setId(id, payload)` | `id`, `payload` | Ghi đè toàn bộ document (không merge, không validate) |
| `removeId(id)` | `id` | Xóa document theo ID |
| `removeAll()` | -- | Xóa tất cả documents trong database |
| `removeQuery(query)` | `query`: selector | Xóa documents theo điều kiện |
| `validate(payload)` | `payload` | Validate dữ liệu theo Joi schema (nếu có) |

#### Cac phuong thuc dong bo (Sync)

| Phương thức | Mô tả |
|-------------|--------|
| `getRemoteUrl()` | Lấy URL CouchDB remote. Sử dụng cache (`CacheCouchDbConnect`), nếu cache trống thì gọi `fetchCouchDatabase` API |
| `pushRemote()` | Đẩy dữ liệu từ PouchDB local lên CouchDB remote (one-way) |
| `pullRemote()` | Kéo dữ liệu từ CouchDB remote về PouchDB local (one-way) |
| `syncRemote()` | Đồng bộ hai chiều (live replication) với auto-retry. Mỗi database chỉ sync một lần (cache bằng `Map`) |
| `changesListener(callback, options)` | Lắng nghe thay đổi realtime trên database. Mặc định: `since: 'now'`, `live: true`, `include_docs: true` |

#### Cau hinh dong bo

```javascript
// Sync options
{
  live: true,     // Đồng bộ liên tục (không dừng)
  retry: true,    // Tự động retry khi mất kết nối
}

// Changes listener options
{
  since: 'now',         // Chỉ lắng nghe thay đổi mới
  live: true,           // Lắng nghe liên tục
  include_docs: true,   // Bao gồm nội dung document
}
```

#### Quan ly Session

| Phương thức | Mô tả |
|-------------|--------|
| `sessionRemote(session, url)` | Thiết lập cookie session CouchDB (set cookie cho domain CouchDB) |
| `sessionLogout(url)` | Xóa cookie `AuthSession` -- đăng xuất CouchDB session |

> **Ghi chú:** Session auth hiện đang **tắt** (`USE_SESSION = false`). Hệ thống sử dụng URL-based auth (username:password trong URL) thay vì cookie session.

#### Luong dong bo du lieu

```
1. Extension khởi động
2. Gọi getRemoteUrl()
   ├── Kiểm tra cache (CacheCouchDbConnect)
   ├── Nếu cache trống -> Gọi API: POST /extension/db/get
   │   └── Nếu DB chưa tồn tại -> POST /extension/db/create
   └── Trả về URL kết nối CouchDB
3. Gọi syncRemote()
   └── PouchDB.sync(localDB, remoteURL, { live: true, retry: true })
       ├── on('change') -> Log thay đổi
       ├── on('paused') -> Log tạm dừng
       ├── on('active') -> Log tiếp tục
       └── on('error')  -> Log lỗi
```

---

## Bridge API

**File:** `src/app/api/autoshopee/api.js`

Hàm `apiBridge` cho phép content script gọi API AutoShopee thông qua background service worker (via `webext-bridge`).

```javascript
apiBridge({ url, method, body, params })
```

| Tham số | Kiểu | Mô tả |
|---------|------|--------|
| `url` | string | Đường dẫn API (ví dụ: `/extension/user/get/me`) |
| `method` | string | HTTP method (`GET`, `POST`, `PUT`, `DELETE`) |
| `body` | object | Request body (cho POST/PUT) |
| `params` | object | Query parameters (cho GET) |

> **Ghi chú:** Bridge API tự động gắn Bearer token và base URL. Dùng khi content script cần gọi API mà không thể gọi trực tiếp (do CORS hoặc chạy trong context trang web).

---

## Tong hop Endpoints

### Thong ke

| Nhóm | Số endpoint | File |
|------|-------------|------|
| Auth / User | 3 | `user.js` |
| CMS | 3 | `cms.js` |
| Public | 3 | `public.js` |
| Database (CouchDB) | 2 | `db.js` |
| Shopee - Shop | 7 | `shopee/shop.js` |
| Shopee - Ads Keywords | 8 | `shopee/keyword.js` |
| Shopee - Flash Sale | 11 | `shopee/flashsale.js` |
| Lazada | 5 | `lazada/index.js` |
| TikTok | 6 | `tiktok/index.js` |
| PDF Service | 3 | `service/pdf.js` |
| S3 Storage | 1 | `service/s3.js` |
| **Tong cong** | **52** | |

### Socket Events

| Nhóm | Số events | File |
|------|-----------|------|
| App Socket | 3 | `socket/app.js` |
| Worker Socket | 7 | `socket/worker.js` |
| **Tong cong** | **10** | |
