diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..7d1bdad --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,209 @@ +# คู่มือการติดตั้งโปรเจกต์ใหม่ (New Project Deployment Guide) + +เอกสารนี้รวบรวมขั้นตอนการสร้างสภาพแวดล้อม (Environment) และการตั้งค่าระบบสำหรับลูกค้ารายใหม่ (เช่น โปรเจกต์ `pkh`) โดยอ้างอิงจากโครงสร้างระบบปัจจุบันบนเซิร์ฟเวอร์ `kitizone.com` + +> [!NOTE] +> ระบบปัจจุบันใช้ **Bare Git Repository** ในการรับโค้ดและรัน Deployment Hook (post-receive) +> โปรเจกต์มี 4 โดเมนย่อย: เว็บไซต์หลัก (Next.js), SPA (React), Admin Panel (React), และ API (Elysia.js) + +--- + +## 1. เตรียม Git Repository (Bare Repo) + +บน Server ให้สร้างโฟลเดอร์สำหรับรับโค้ด: + +```bash +# กำหนดชื่อโปรเจกต์ +PROJECT_NAME="pkh" + +# สร้าง Bare Repo +mkdir -p /home/ti/git/${PROJECT_NAME}.git +cd /home/ti/git/${PROJECT_NAME}.git +git init --bare +``` + +--- + +## 2. ขอ SSL Certificate & ตั้งค่า Nginx + +เนื่องจากใช้ Nginx ผ่าน Docker เราจะต้องรัน Certbot แบบ Standalone เพื่อขอ Certificate + +### 2.1 ปิด Nginx ชั่วคราว +```bash +cd /home/ti/projects/MyNginxMain +docker compose stop nginx +``` + +### 2.2 ขอ SSL ด้วย Certbot +เปลี่ยนชื่อโดเมนให้ตรงกับลูกค้า: +```bash +docker compose run --rm certbot certonly --standalone \ + -d ${PROJECT_NAME}.kitizone.com \ + -d spa.${PROJECT_NAME}.kitizone.com \ + -d admin.${PROJECT_NAME}.kitizone.com \ + -d api.${PROJECT_NAME}.kitizone.com +``` + +### 2.3 สร้าง Nginx Config Files +เข้าไปที่ `/home/ti/projects/MyNginxMain/nginx/conf.d/` และสร้างไฟล์ `.conf` สำหรับ 4 โดเมน โดยใช้ `admin.pkh.kitizone.com.conf` เป็นต้นแบบ: + +> [!IMPORTANT] +> - สำหรับเว็บไซต์ (Next.js) และ API: ใช้ `proxy_pass http://192.168.1.167:;` +> - สำหรับ SPA และ Admin: ตั้งค่า `root /usr/share/nginx/html/${PROJECT_NAME}/spa;` ไว้ที่ **ระดับ server block** (ห้ามใส่ใน location / เพื่อไม่ให้ติดปัญหา CORS หรือ 404 สำหรับไฟล์ static) + +**ตัวอย่างโครงสร้าง Nginx สำหรับ Admin/SPA:** +```nginx +server { + listen 443 ssl; + server_name admin.pkh.kitizone.com; + ssl_certificate /etc/letsencrypt/live/admin.pkh.kitizone.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/admin.pkh.kitizone.com/privkey.pem; + + root /usr/share/nginx/html/pkh/admin; + + location / { + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires max; + log_not_found off; + } +} +``` + +### 2.4 สตาร์ท Nginx +```bash +docker compose up -d nginx +``` + +--- + +## 3. สร้าง Database + +เข้าไปใน Container ของ Postgres เพื่อสร้างฐานข้อมูลใหม่: + +```bash +docker exec -it arokro_postgres bash +psql -U postgres -c "CREATE DATABASE ${PROJECT_NAME}db;" +exit +``` + +--- + +## 4. เปิด Firewall (UFW) ให้พอร์ตใหม่ + +กำหนดพอร์ตใหม่สำหรับ Next.js (เช่น `3003`) และ Backend API (เช่น `3052`) +> [!WARNING] +> หากลืมเปิด Firewall ขั้นตอนนี้ Nginx จะตอบกลับด้วย 504 Gateway Timeout ทันที! + +```bash +sudo ufw allow 3003/tcp +sudo ufw allow 3052/tcp +sudo ufw reload +``` + +--- + +## 5. กำหนดค่า Environment (.env) + +สร้างโฟลเดอร์สำหรับเก็บโค้ด (ซึ่งจะถูกโคลนมาจาก Git): +```bash +mkdir -p /home/ti/projects/${PROJECT_NAME}-api +mkdir -p /home/ti/projects/${PROJECT_NAME}-website +``` + +สร้างไฟล์ `.env` สำหรับ API ที่ `/home/ti/projects/${PROJECT_NAME}-api/apps/backend/.env`: +```ini +PORT=3052 +DATABASE_URL="postgresql://postgres:password@localhost:5132/pkhdb?schema=public" +NODE_ENV=production +APP_URL="https://api.pkh.kitizone.com" +BACKEND_PUBLIC_URL="https://api.pkh.kitizone.com" +CORS_ORIGIN="https://admin.pkh.kitizone.com,https://spa.pkh.kitizone.com,https://pkh.kitizone.com" +``` +> [!TIP] +> **สำคัญมาก:** Backend ของระบบ (Bun) จะอ่านไฟล์ `.env` ในโฟลเดอร์ `apps/backend/` เป็นหลัก ดังนั้นต้องระบุ `CORS_ORIGIN` ให้ครบถ้วนเพื่อไม่ให้หน้าบ้าน (React) ติดปัญหา CORS (Error 404/Failed to load resource) + +--- + +## 6. ตั้งค่า Git Hook (post-receive) + +นำสคริปต์ `post-receive` ไปวางที่ `/home/ti/git/${PROJECT_NAME}.git/hooks/post-receive` + +**จุดที่ต้องแก้ไขใน Script:** +1. แก้ตัวแปรด้านบนสุด: + ```bash + TARGET_BACKEND="/home/ti/projects/pkh-api" + TARGET_WEBSITE="/home/ti/projects/pkh-website" + TARGET_SPA="/home/ti/projects/MyNginxMain/nginx/html/pkh/spa" + TARGET_ADMIN="/home/ti/projects/MyNginxMain/nginx/html/pkh/admin" + + PM2_NAME_BACKEND="pkh-backend" + PM2_NAME_WEBSITE="pkh-website" + ``` +2. แก้ไข Port ชั่วคราว (ถ้ามีการ copy ecosystem.config.js จาก homaroma): + ```bash + sed -i 's/homaroma/pkh/g' $TARGET_BACKEND/ecosystem.config.js + sed -i 's/PORT: 3002/PORT: 3003/g' $TARGET_WEBSITE/ecosystem.config.js + ``` +3. **(สำคัญมาก)** เพิ่มคำสั่ง `sleep 10` หลังการสตาร์ท Backend: + ```bash + echo ">>> Managing PM2 Process: $PM2_NAME_BACKEND..." + $PM2 startOrReload $TARGET_BACKEND/ecosystem.config.js --only $PM2_NAME_BACKEND --env production --update-env + echo "✅ PM2 process '$PM2_NAME_BACKEND' is running. Waiting 10s for it to fully start..." + sleep 10 + ``` + *(ป้องกันปัญหาที่ Next.js Build เริ่มทำงานทันทีและ fetch API พังเพราะ Backend เพิ่งถูก restart ไปหมาดๆ)* + +ตั้งสิทธิ์การรัน: +```bash +chmod +x /home/ti/git/${PROJECT_NAME}.git/hooks/post-receive +``` + +--- + +## 7. Deploy โค้ด (จากเครื่องนักพัฒนา) + +ที่เครื่องของคุณ (Developer): +```bash +# แอดเซิร์ฟเวอร์ใหม่ +git remote add pkh ti@kitizone.com:/home/ti/git/pkh.git + +# สั่ง Deploy +git push pkh master +``` + +รอจนกว่าสคริปต์ post-receive จะจัดการ Build ทั้ง Backend, Website (SSR), Admin, และ SPA จนเสร็จ + +--- + +## 8. Seed ฐานข้อมูล (ทำครั้งแรกครั้งเดียว) + +หลังจาก Push สำเร็จแล้ว Database จะมีตารางต่างๆ สร้างขึ้นมา แต่ยังไม่มีข้อมูลพื้นฐาน ให้รัน Seed Script: + +```bash +ssh ti@kitizone.com + +# รันคำสั่ง Seed +cd /home/ti/projects/${PROJECT_NAME}-api/apps/backend +export PATH=$PATH:/home/ti/.bun/bin:/home/ti/.nvm/versions/node/v22.17.1/bin +bun run prisma/seed.cjs +``` + +ระบบจะสร้าง: +- เมนูพื้นฐาน +- ข้อมูลจำลอง (แบรนด์, สินค้า, กลิ่น) +- บัญชี Admin (`admin` / `homaroma123`) + +--- + +## สรุปปัญหาที่มักพบ (Troubleshooting) + +1. **Next.js Build พัง (504 Gateway Timeout ตอนดึง Sitemap):** + - สาเหตุ: API หรือ Database ดาวน์, หรือลืมเปิดพอร์ต Backend ด้วยคำสั่ง `ufw allow` + - แก้ไข: ตรวจสอบพอร์ต UFW ว่าเปิดแล้ว และเช็คสคริปต์ว่ามี `sleep 10` ให้ API บูทเสร็จหรือไม่ +2. **Admin/SPA พัง (CORS Policy หรือ 404 Assets):** + - สาเหตุ: ลืมระบุ `CORS_ORIGIN` หรือตั้งค่า `root` ของ Nginx ไว้ผิดที่ (ผิด Block) + - แก้ไข: เติม CORS ใน `apps/backend/.env` แล้ว Restart PM2, และนำ `root` ออกมาไว้ระดับ `server { ... }` ใน Nginx