# Triển khai StockFlow trên cPanel

Tài liệu này giả định hosting có **Application Manager/Setup Node.js App**
và cho phép chạy Node.js 20+. Nếu gói cPanel chỉ hỗ trợ PHP/Apache mà không có
Node.js Application Manager, chỉ có thể host frontend tĩnh trên gói đó; API
Express phải chạy trên VPS hoặc một dịch vụ Node.js khác.

## Kiến trúc nên dùng

- Frontend React được build thành `frontend/dist` và đặt tại `public_html` hoặc
  một subdomain frontend.
- Backend Express chạy thành một Node.js application riêng, tốt nhất ở một
  subdomain như `api.example.com` và nằm ngoài `public_html`.
- MySQL dùng database/user được tạo trong cPanel, thường có tiền tố tài khoản,
  ví dụ `acct_stockflow` và `acct_stockflow_user`.
- HTTPS phải được bật cho cả frontend và API. `VITE_API_URL` được đóng gói vào
  frontend lúc build, nên phải đặt đúng URL production trước khi chạy Vite.

## Điền form Deploy a Node.js app khi deploy bằng Git

Repository có hai package độc lập. Form Node.js nên tạo cho **backend**; không
chạy `frontend` như một Node server production. Với source đã clone ở root của
repository và đang ở branch `ver2`, điền như sau:

| Trường trên form | Giá trị cho backend |
|---|---|
| Application path from root | `backend` |
| Framework | `Other`/`Express` nếu có; nếu vẫn hiện `Not detected` thì có thể giữ nguyên |
| Build command | `npm run build` |
| Startup command | `node dist/server.js` (hoặc `npm start` nếu form nhận script npm) |
| Output directory | `dist` nếu bắt buộc; nếu cho phép bỏ trống thì bỏ trống vì đây là Node app |

Chọn Node.js 20 trở lên. Build command chạy trong thư mục `backend`, nơi có
`package.json` và `tsconfig.json`. Không dùng `npm ci --omit=dev` trước bước
build vì TypeScript nằm trong `devDependencies`; nếu cPanel tự cài package
trước khi build thì chỉ cần để build command là `npm run build`.

Nếu form yêu cầu startup là một file thay vì một câu lệnh, nhập
`dist/server.js`. Backend đã đọc `process.env.PORT`, vì vậy không đổi code để
dùng một port cố định.

Frontend không cần một Node process riêng. Build frontend ở package
`frontend`, sau đó upload nội dung `frontend/dist` vào document root của domain
frontend (`public_html` hoặc subdomain riêng):

```bash
cd frontend
printf 'VITE_API_URL=https://api.example.com/api\n' > .env.production.local
npm ci
npm run build
```

Nếu giao diện cPanel cũng có form build cho frontend, điền `frontend` vào
Application path, `npm run build` vào Build command, `dist` vào Output
directory và để trống Startup command. Cách an toàn hơn vẫn là upload `dist`
như static site, vì frontend Vite không phải backend Node cần giữ một process.

Trong phần Environment Variables của app backend, nhập các biến production
ở mục bên dưới. `VITE_API_URL` phải được đặt trong môi trường build frontend,
không đặt nhầm vào runtime environment của backend.

## Kiểm tra trước khi triển khai

1. Xác nhận host có Node.js 20+ và Application Manager. Node version trong
   cPanel phải khớp với trường `engines` của ứng dụng.
2. Xác nhận database là MySQL 8+ hoặc kiểm thử đầy đủ trên phiên bản MariaDB
   mà host cung cấp. Schema sử dụng `CHECK`, generated columns, CTE và các kiểu
   dữ liệu MySQL; không nên đoán rằng mọi MariaDB version tương thích.
3. Tạo backup database hiện tại và lưu ngoài `public_html`.
4. Không upload `backend/.env` hoặc `frontend/.env` hiện tại. Hai file đó chỉ
   dùng cho máy local; repository đã bỏ theo dõi các file secret và giữ lại
   `.env.example` làm mẫu. Nếu secret local đã từng commit, vẫn phải rotate
   chúng vì lịch sử Git có thể còn chứa giá trị cũ.

## Build và upload

Build frontend với URL API thật:

```bash
cd frontend
printf 'VITE_API_URL=https://api.example.com/api\n' > .env.production.local
npm ci
npm run build
```

Upload nội dung `frontend/dist` vào document root frontend. File
`frontend/public/.htaccess` được copy vào dist để các route `/reports`, `/debt`
và các route React khác không trả 404 khi refresh.
Nếu đặt ứng dụng trong thư mục con như `public_html/stockflow/` thay vì
document root/subdomain, phải cấu hình thêm `base: '/stockflow/'` trong
`vite.config.ts` và điều chỉnh `RewriteBase` trước khi build.

Build backend:

```bash
cd backend
npm ci
npm run build
# Chỉ dùng bước này nếu chạy backend từ source trên máy chủ; dist đã được build ở trên.
npm ci --omit=dev
```

Nếu build ở máy phát triển/CI, upload `dist`, `package.json` và
`package-lock.json`, sau đó chỉ chạy `npm ci --omit=dev` trên cPanel. Không thể
chạy `npm run build` sau `npm ci --omit=dev` vì TypeScript nằm trong
`devDependencies`.

Trong cPanel Setup Node.js App, chọn:

- Application root: thư mục backend, ngoài `public_html`.
- Startup file: `dist/server.js`.
- Node.js version: 20 trở lên.
- Environment variables: nhập các biến production ở dưới.

Backend đã đọc `process.env.PORT`, vì vậy không gán cứng port. Hãy dùng port
do cPanel cấp và restart application sau khi đổi biến môi trường.

## Biến môi trường production

Đặt trong phần Environment Variables của Node.js App hoặc file riêng không nằm
trong document root:

```text
NODE_ENV=production
PORT=<port-do-cpanel-cap>
DB_HOST=localhost
DB_PORT=3306
DB_USER=acct_stockflow_user
DB_PASSWORD=<mat-khau-db-rat-manh>
DB_NAME=acct_stockflow
JWT_SECRET=<chuoi-ngau-nhien-dai-it-nhat-32-ky-tu>
JWT_EXPIRES_IN=1d
CORS_ORIGIN=https://example.com
```

Không dùng `root`, mật khẩu `0000`, JWT secret mẫu hoặc tài khoản demo trong
production. Nếu các secret local đã từng commit lên Git, phải rotate chúng dù
repository đang private.

## Database và migration

`database/schema.sql` chỉ dành cho database mới và có lệnh drop/recreate; tuyệt
đối không chạy file này trên database production đang có dữ liệu.

Với database hiện hữu, chạy lần lượt migrations `001` đến `005` bằng cPanel
Terminal/SSH và chỉ rõ database thật:

```bash
mysql -h localhost -u acct_stockflow_user -p acct_stockflow < database/migrations/001_add_inventory_conditions.sql
mysql -h localhost -u acct_stockflow_user -p acct_stockflow < database/migrations/002_align_full_stockflow_schema.sql
mysql -h localhost -u acct_stockflow_user -p acct_stockflow < database/migrations/003_add_audit_logs.sql
mysql -h localhost -u acct_stockflow_user -p acct_stockflow < database/migrations/004_add_debt_reporting.sql
mysql -h localhost -u acct_stockflow_user -p acct_stockflow < database/migrations/005_add_debt_opening_dimensions.sql
```

Migrations có stored procedure/delimiter nên Terminal với `mysql` client đáng
tin cậy hơn phpMyAdmin. Kiểm tra `SELECT VERSION()` trước khi chạy và kiểm tra
số bảng, foreign key, view sau khi chạy. Không chạy `seed.sql` trên production;
file này tạo user demo và dữ liệu mẫu.

## Kiểm tra sau khi bật app

1. Mở `https://api.example.com/health`; kết quả phải có `database: "up"`.
2. Đăng nhập admin bằng tài khoản production đã đổi mật khẩu.
3. Kiểm tra `/reports`, `/debt`, refresh trực tiếp các URL này và thử in báo cáo.
4. Kiểm tra log Node.js/Passenger, memory limit, timeout và restart behavior.
5. Chạy smoke test từ máy có quyền truy cập:

```bash
API_BASE=https://api.example.com/api bash scripts/api-smoke-test.sh
```

Backend sẽ từ chối khởi động production khi thiếu `DB_HOST`, `DB_USER`,
`DB_PASSWORD`, `DB_NAME`, `JWT_SECRET` đủ mạnh hoặc `CORS_ORIGIN`; CORS cũng
chỉ phản hồi cho các origin đã cấu hình. Trước khi mở traffic, vẫn phải bật
HTTPS, đổi secret, dùng database user tối thiểu quyền, thêm rate
limiting/security headers ở lớp Apache hoặc backend, và thiết lập backup định
kỳ ngoài `public_html`.
