# Deploy lên cPanel

Dành cho hosting cPanel có **Setup Node.js App**. Nếu cPanel của bạn không có mục đó
thì dừng ở đây — app Node không chạy được trên hosting đó, xem phần cuối trang.

Domain dùng trong hướng dẫn: `thugiancuoituan.com`

---

## 0. Kiểm tra trước: hosting có chạy được Node không

Đăng nhập cPanel, ở ô tìm kiếm gõ `node`.

- **Hiện "Setup Node.js App"** → làm tiếp bước 1.
- **Không hiện gì** → hosting không hỗ trợ Node. Nhảy xuống mục *"Nếu không có Node.js"*.

Bấm vào đó, mở ô **Node.js version** xem có những bản nào. Chọn **bản cao nhất** —
code này chạy được từ Node 18 trở lên, nên bản nào trong danh sách cũng được.

---

## 1. Tạo database MySQL

cPanel → **MySQL® Databases**.

**Tạo database:** ô *New Database* gõ `tiktok` → Create Database.
cPanel tự thêm tiền tố tên tài khoản, thành `alextuan_tiktok`. **Ghi lại tên đầy đủ này.**

**Tạo user:** phần *MySQL Users* → *Add New User*.
Tên `tk`, cPanel biến thành `alextuan_tk`. Bấm *Password Generator* lấy mật khẩu mạnh,
**lưu lại ngay** vì nó không hiện lại lần thứ hai.

**Gán quyền:** phần *Add User To Database* → chọn user vừa tạo và database vừa tạo →
Add → tick **ALL PRIVILEGES** → Make Changes.

> Không cần tự tạo bảng. App tự tạo 3 bảng `users`, `social_accounts`, `sessions`
> ở lần chạy đầu tiên.

---

## 2. Tải code lên

cPanel → **File Manager** → vào thư mục gốc tài khoản (`/home/alextuan`).

Tạo thư mục `tiktok-auth`. Đừng đặt trong `public_html` — Passenger sẽ tự nối domain
vào app, còn để code trong `public_html` là người ngoài đọc được cả file `.env` chứa
secret của bạn.

Nén những thứ này ở máy bạn thành một file zip:

```
app.cjs          <-- file khởi động cho Passenger, THIẾU LÀ KHÔNG CHẠY
server.js
db.js
session-store.js
package.json
package-lock.json
public/          (cả thư mục)
```

**Không** nén `node_modules` (cPanel tự cài ở bước 5), `.env`, hay `deploy/`.

Upload zip vào `/home/alextuan/tiktok-auth` rồi bấm chuột phải → **Extract**.

---

## 3. Tạo ứng dụng Node

cPanel → **Setup Node.js App** → **Create Application**:

| Ô | Điền |
|---|---|
| Node.js version | bản cao nhất có trong danh sách |
| Application mode | **Production** |
| Application root | `tiktok-auth` |
| Application URL | `thugiancuoituan.com` (chọn domain, để trống phần thư mục con) |
| Application startup file | **`app.cjs`** |

Bấm **Create**.

> **Startup file phải là `app.cjs`, KHÔNG phải `server.js`.** cPanel chạy app bằng
> Phusion Passenger, mà Passenger nạp file khởi động bằng `require()` của CommonJS.
> `require()` không nạp được module ESM — mà `server.js` viết theo ESM (dùng `import`)
> và có top-level await. Trỏ thẳng vào `server.js` là tiến trình chết ngay khi khởi
> động với lỗi `ERR_REQUIRE_ESM` (Node 18/20) hoặc `ERR_REQUIRE_ASYNC_MODULE`
> (Node 22+), còn ngoài trình duyệt bạn chỉ thấy lỗi 503 không giải thích gì.
>
> `app.cjs` là cầu nối: đuôi `.cjs` buộc Node coi nó là CommonJS nên Passenger nạp
> được, rồi bên trong nó dùng `import()` động để gọi `server.js`.

> *Application mode: Production* chính là thứ đặt `NODE_ENV=production`, nhờ đó cookie
> phiên mới có cờ `Secure`. Để *Development* là đăng nhập sẽ lỗi trên HTTPS.

---

## 4. Khai biến môi trường

Vẫn trong màn hình app vừa tạo, kéo xuống phần **Environment variables**, thêm từng biến:

| Tên | Giá trị |
|---|---|
| `TIKTOK_CLIENT_KEY` | `awxbp03cp3zs1wnm` |
| `TIKTOK_CLIENT_SECRET` | secret của bạn |
| `TIKTOK_REDIRECT_URI` | `https://thugiancuoituan.com/auth/tiktok/callback` |
| `SESSION_SECRET` | chuỗi ngẫu nhiên ≥ 32 ký tự |
| `DB_HOST` | `localhost` |
| `DB_USER` | `alextuan_tk` (tên đầy đủ có tiền tố) |
| `DB_PASSWORD` | mật khẩu đã lưu ở bước 1 |
| `DB_NAME` | `alextuan_tiktok` (tên đầy đủ có tiền tố) |

Bấm **Save**.

Dùng ô này thay cho file `.env` thì an toàn hơn: secret không nằm dưới dạng file trên
đĩa. Nếu bạn thích dùng `.env` thì upload nó vào `tiktok-auth/`, app đọc được cả hai
— biến khai trong cPanel luôn thắng.

Sinh `SESSION_SECRET` bằng lệnh này ở máy bạn:

```powershell
node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"
```

---

## 5. Cài thư viện

Vẫn màn hình đó, bấm nút **Run NPM Install**.

Nếu nó báo lỗi hoặc treo, dùng Terminal của cPanel (nếu hosting cho phép):

```bash
cd ~/tiktok-auth
source /home/alextuan/nodevenv/tiktok-auth/22/bin/activate   # số 22 = phiên bản Node bạn chọn
npm ci --omit=dev
```

Không có package nào cần biên dịch native, nên bước này nhẹ, không lo bị OOM.

---

## 6. Bật HTTPS

cPanel → **SSL/TLS Status** → chọn `thugiancuoituan.com` và `www` → **Run AutoSSL**.

Đợi vài phút, mở `https://thugiancuoituan.com` phải thấy ổ khoá. TikTok bắt buộc redirect
URI là `https`, bước này không bỏ được.

---

## 7. Khởi động

Quay lại **Setup Node.js App**, bấm **Restart**.

Mở `https://thugiancuoituan.com` — phải thấy trang có nút "Tiếp tục với TikTok".

Kiểm tra nhanh 3 đường dẫn:

- `https://thugiancuoituan.com/` → trang chủ
- `https://thugiancuoituan.com/terms` → điều khoản
- `https://thugiancuoituan.com/privacy` → chính sách bảo mật

---

## 8. Khai báo bên TikTok

developers.tiktok.com → app `alextuan`:

| Ô | Giá trị |
|---|---|
| Redirect URI (Login Kit) | `https://thugiancuoituan.com/auth/tiktok/callback` |
| Web/Desktop URL | `https://thugiancuoituan.com` |
| Terms of Service URL | `https://thugiancuoituan.com/terms` |
| Privacy Policy URL | `https://thugiancuoituan.com/privacy` |

Redirect URI phải **trùng từng ký tự** với biến `TIKTOK_REDIRECT_URI`, kể cả dấu `/`.

Rồi bấm **URL properties** (góc phải trên) để xác minh quyền sở hữu domain. Nếu chọn
cách tải file chữ ký, bỏ file đó vào `tiktok-auth/public/` rồi kiểm tra:

```
https://thugiancuoituan.com/tiktok<mã>.txt
```

Mở được mới bấm Verify.

---

## 9. Cập nhật code sau này

1. Upload file mới đè lên trong File Manager.
2. **Setup Node.js App** → **Restart**.

Dữ liệu nằm trong MySQL nên không bị ảnh hưởng.

---

## Sao lưu

cPanel → **phpMyAdmin** → chọn `alextuan_tiktok` → tab **Export** → Go.
Nên làm trước mỗi lần cập nhật code.

---

## Gặp lỗi thì xem đâu

Log nằm ở `~/tiktok-auth/stderr.log`, hoặc nút **Open log** trong Setup Node.js App.

| Triệu chứng | Nguyên nhân hay gặp |
|---|---|
| Trang trắng / 503 | App chết lúc khởi động. Mở `stderr.log` đọc dòng cuối |
| `Không kết nối được MySQL` | Sai `DB_USER`/`DB_NAME` — nhớ phải có tiền tố `alextuan_` |
| `Thiếu biến môi trường` | Chưa Save phần Environment variables, hoặc chưa Restart |
| `Sai cấu hình production` | `SESSION_SECRET` ngắn dưới 32 ký tự, hoặc redirect URI không phải https |
| Bấm nút → TikTok báo redirect_uri sai | `.env`/biến môi trường và portal lệch nhau |
| Đăng nhập xong bị đá về trang chưa đăng nhập | Application mode đang để *Development*, đổi sang *Production* rồi Restart |
| `ERR_REQUIRE_ESM` / `ERR_REQUIRE_ASYNC_MODULE` trong log | Startup file đang trỏ `server.js`. Đổi thành `app.cjs` rồi Restart |
| 503 mà log trống trơn | Cũng thường là lỗi trên — kiểm tra ô Application startup file |
| Hiện mã nguồn thay vì chạy | Chưa tạo app trong Setup Node.js App, Apache đang coi đây là web tĩnh |

---

## Nếu không có Node.js

Hosting cPanel giá rẻ thường chỉ có PHP. Lúc đó ba lựa chọn:

1. **Hỏi nhà cung cấp** xem có bật được CloudLinux Node.js Selector không — nhiều nơi bật
   miễn phí khi khách yêu cầu.
2. **Thuê VPS** — mọi thứ đã chuẩn bị sẵn trong [DEPLOY.md](DEPLOY.md), khoảng 100–150k/tháng.
3. **Viết lại bằng PHP** — chạy được trên mọi hosting, nhưng phải làm lại từ đầu.
