chore: add deployment runbook and server configs for kittisak.com

This commit is contained in:
Your Name committed 2026-09-23 18:49:34 +07:00
1 parent 14d3282a81
commit 486c8d31f1
7 files changed
+478

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
# Kittisak Deployment Runbook
เอกสารนี้เป็น runbook เฉพาะลูกค้า `kittisak` โดยอิงจาก `DEPLOYMENT_GUIDE.md` และไฟล์ตัวอย่างใน `serverinfo/ddc-*` / `serverinfo/huahin-*`
## Target
- Project name: `kittisak`
- Website: `https://kittisak.com`
- SPA: `https://spa.kittisak.com`
- Admin: `https://admin.kittisak.com`
- API: `https://api.kittisak.com`
- Database: `kittisakdb`
- Website SSR port: `3005`
- Backend API port: `3054`
- Bare repo: `/home/ti/git/kittisak.com.git`
- Backend path: `/home/ti/projects/kittisak-api`
- Website path: `/home/ti/projects/kittisak-website`
- SPA static path: `/home/ti/projects/MyNginxMain/nginx/html/kittisak/spa`
- Admin static path: `/home/ti/projects/MyNginxMain/nginx/html/kittisak/admin`
## Files Prepared In This Repo
- `serverinfo/kittisak-post-receive`
- `serverinfo/kittisak-backend.env.example`
- `serverinfo/kittisak-nginx/kittisak.com.conf`
- `serverinfo/kittisak-nginx/spa.kittisak.com.conf`
- `serverinfo/kittisak-nginx/admin.kittisak.com.conf`
- `serverinfo/kittisak-nginx/api.kittisak.com.conf`
## Preflight On Server
Run on `kitizone.com` before changing live services:
```bash
ss -ltnp | grep -E ':3005|:3054' || true
pm2 list
```
If ports `3005` or `3054` are already used, stop and choose new ports before applying these files.
## 1. Create Bare Git Repository
```bash
mkdir -p /home/ti/git/kittisak.com.git
cd /home/ti/git/kittisak.com.git
git init --bare
```
## 2. Create Project Directories
```bash
mkdir -p /home/ti/projects/kittisak-api
mkdir -p /home/ti/projects/kittisak-website
mkdir -p /home/ti/projects/MyNginxMain/nginx/html/kittisak/spa
mkdir -p /home/ti/projects/MyNginxMain/nginx/html/kittisak/admin
```
## 3. Create Database
```bash
docker exec -it arokro_postgres psql -U postgres -c "CREATE DATABASE kittisakdb;"
```
## 4. Open Firewall Ports
```bash
sudo ufw allow 3005/tcp
sudo ufw allow 3054/tcp
sudo ufw reload
```
## 5. Request SSL Certificates
```bash
cd /home/ti/projects/MyNginxMain
docker compose stop nginx
docker compose run --rm certbot certonly --standalone \
-d kittisak.com \
-d spa.kittisak.com \
-d admin.kittisak.com \
-d api.kittisak.com
```
## 6. Install Nginx Configs
Copy files from `serverinfo/kittisak-nginx/` to:
```text
/home/ti/projects/MyNginxMain/nginx/conf.d/
```
Then validate and restart nginx:
```bash
cd /home/ti/projects/MyNginxMain
docker compose run --rm nginx nginx -t
docker compose up -d nginx
```
## 7. Install Backend Environment File
Create this server file:
```text
/home/ti/projects/kittisak-api/.env
```
Use `serverinfo/kittisak-backend.env.example` as the base and replace placeholders before production use:
- `postgres:password` must match the real Postgres password.
- `JWT_SECRET` must be a new project-specific secret.
- `OMISE_SECRET_KEY` must be set if payments are enabled.
The deploy hook copies this file into `/home/ti/projects/kittisak-api/apps/backend/.env` before Prisma commands.
## 8. Install Git Hook
Copy:
```text
serverinfo/kittisak-post-receive
```
To:
```text
/home/ti/git/kittisak.com.git/hooks/post-receive
```
Then run:
```bash
chmod +x /home/ti/git/kittisak.com.git/hooks/post-receive
```
## 9. Deploy From Developer Machine
```bash
git remote add kittisak-website ti@kitizone.com:/home/ti/git/kittisak.com.git
git push kittisak-website master
```
## 10. Seed Database
Run after the first successful deployment:
```bash
ssh ti@kitizone.com
cd /home/ti/projects/kittisak-api/apps/backend
export PATH=$PATH:/home/ti/.bun/bin:/home/ti/.nvm/versions/node/v22.17.1/bin
bun run prisma/seed.cjs
```
Default seeded admin account is currently `admin / homaroma123`. Change it before handing production access to the customer.
## Verification
```bash
pm2 list
curl -I https://kittisak.com
curl -I https://spa.kittisak.com
curl -I https://admin.kittisak.com
curl -I https://api.kittisak.com
```
Expected PM2 processes:
- `kittisak-backend`
- `kittisak-website`
Check browser console for:
- CORS errors on admin and SPA
- missing static assets
- API 504 from website SSR build or runtime fetches
## Known Risks
- `serverinfo/kittisak-post-receive` rewrites `ecosystem.config.js` at deploy time from the base `homaroma` config. This follows the existing `pkh`/`ddc` pattern.
- The codebase still contains some hardcoded HomAroma URLs and storage keys in app code. This runbook only prepares infrastructure deployment, not full white-label branding cleanup.
- Payments and LINE LIFF need customer-specific production keys before launch.
+8
View File
@@ -0,0 +1,8 @@
PORT=3054
DATABASE_URL="postgresql://postgres:password@localhost:5132/kittisakdb?schema=public"
NODE_ENV=production
APP_URL="https://api.kittisak.com"
BACKEND_PUBLIC_URL="https://api.kittisak.com"
CORS_ORIGIN="https://admin.kittisak.com,https://spa.kittisak.com,https://kittisak.com"
JWT_SECRET="replace-with-kittisak-production-secret"
OMISE_SECRET_KEY=""
@@ -0,0 +1,31 @@
server {
listen 80;
server_name admin.kittisak.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name admin.kittisak.com;
ssl_certificate /etc/letsencrypt/live/admin.kittisak.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.kittisak.com/privkey.pem;
root /usr/share/nginx/html/kittisak/admin;
index index.html index.htm;
location / {
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;
}
}
@@ -0,0 +1,32 @@
server {
listen 80;
server_name api.kittisak.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name api.kittisak.com;
client_max_body_size 20m;
ssl_certificate /etc/letsencrypt/live/api.kittisak.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.kittisak.com/privkey.pem;
location / {
proxy_pass http://192.168.1.167:3054;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
}
@@ -0,0 +1,38 @@
server {
listen 80;
server_name kittisak.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name kittisak.com;
ssl_certificate /etc/letsencrypt/live/kittisak.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/kittisak.com/privkey.pem;
location / {
proxy_pass http://192.168.1.167:3005;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
location /_next/static/ {
proxy_pass http://192.168.1.167:3005;
proxy_http_version 1.1;
proxy_set_header Host $host;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
@@ -0,0 +1,31 @@
server {
listen 80;
server_name spa.kittisak.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name spa.kittisak.com;
ssl_certificate /etc/letsencrypt/live/spa.kittisak.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/spa.kittisak.com/privkey.pem;
root /usr/share/nginx/html/kittisak/spa;
index index.html index.htm;
location / {
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;
}
}
+156
View File
@@ -0,0 +1,156 @@
#!/bin/bash
# ==============================================================================
# Kittisak Project - Integrated Deployment Script (post-receive hook)
# ==============================================================================
# 1. TARGET PATHS (Nginx & PM2)
TARGET_SPA="/home/ti/projects/MyNginxMain/nginx/html/kittisak/spa"
TARGET_ADMIN="/home/ti/projects/MyNginxMain/nginx/html/kittisak/admin"
TARGET_BACKEND="/home/ti/projects/kittisak-api"
TARGET_WEBSITE="/home/ti/projects/kittisak-website"
# 2. REPOSITORY CONFIG
REPO="/home/ti/git/kittisak.com.git"
BRANCH="master"
TEMP_DIR="/tmp/kittisak_build_$(date +%s)"
# 3. BINARY PATHS & PM2
BUN="/home/ti/.bun/bin/bun"
PM2="/home/ti/.nvm/versions/node/v22.17.1/bin/pm2"
PM2_NAME_BACKEND="kittisak-backend"
PM2_NAME_WEBSITE="kittisak-website"
export PATH="$PATH:/home/ti/.bun/bin:/home/ti/.nvm/versions/node/v22.17.1/bin"
# 4. BUILD ENVIRONMENT
export VITE_API_URL="https://api.kittisak.com/api"
export NEXT_PUBLIC_API_URL="https://api.kittisak.com/api"
# Customer-specific values. Fill these in on the server if enabled.
export VITE_LIFF_ID="${VITE_LIFF_ID:-}"
export VITE_OMISE_PUBLIC_KEY="${VITE_OMISE_PUBLIC_KEY:-}"
# Service ports. Nginx proxies to these ports.
BACKEND_PORT=3054
SSR_PORT=3005
# ------------------------------------------------------------------------------
# START DEPLOYMENT
# ------------------------------------------------------------------------------
set -e
echo "------- Starting Full Deployment: Kittisak Platform -------"
# A. Prepare Temp Source from Checkout
echo "Cloning branch $BRANCH to temporary directory: $TEMP_DIR"
mkdir -p $TEMP_DIR
git --work-tree=$TEMP_DIR --git-dir=$REPO checkout -f $BRANCH
cd $TEMP_DIR
echo "Installing monorepo dependencies..."
$BUN install
# B. Build & Deploy SPA
echo ">>> Building SPA..."
cd apps/spa
$BUN run build
echo "Deploying SPA to $TARGET_SPA..."
mkdir -p $TARGET_SPA
rm -rf $TARGET_SPA/*
cp -rv dist/* $TARGET_SPA/
cd ../..
# C. Build & Deploy ADMIN PANEL
echo ">>> Building Admin Panel..."
cd apps/admin
$BUN run build
echo "Deploying Admin Panel to $TARGET_ADMIN..."
mkdir -p $TARGET_ADMIN
rm -rf $TARGET_ADMIN/*
cp -rv dist/* $TARGET_ADMIN/
cd ../..
# D. Deploy & Manage BACKEND (API)
echo ">>> Updating Backend at $TARGET_BACKEND..."
mkdir -p $TARGET_BACKEND
mkdir -p $TARGET_BACKEND/apps
mkdir -p $TARGET_BACKEND/packages
rsync -av --delete \
--exclude='.env' \
--exclude='public/' \
--exclude='node_modules/' \
apps/backend/ $TARGET_BACKEND/apps/backend/
rsync -av packages/ $TARGET_BACKEND/packages/
cp package.json bun.lock ecosystem.config.js $TARGET_BACKEND/
sed -i 's/homaroma/kittisak/g' $TARGET_BACKEND/ecosystem.config.js
sed -i 's/PORT: 3002/PORT: 3005/g' $TARGET_BACKEND/ecosystem.config.js
cd $TARGET_BACKEND
echo "Installing backend dependencies..."
$BUN install
mkdir -p $TARGET_BACKEND/apps/backend/public
cd $TARGET_BACKEND/apps/backend
echo ">>> Generating Prisma Client..."
$BUN x prisma generate
if [ ! -f "$TARGET_BACKEND/.env" ]; then
echo "Error: .env file is missing at $TARGET_BACKEND/.env. Deployment aborted."
exit 1
fi
cp "$TARGET_BACKEND/.env" "$TARGET_BACKEND/apps/backend/.env"
echo ">>> Running Prisma Migrate Deploy..."
$BUN x prisma migrate deploy
# E. PM2 Management - BACKEND
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
# F. Build & Deploy WEBSITE (Next.js SSR)
echo ">>> Building Next.js SSR App inside monorepo temp dir..."
cd $TEMP_DIR/apps/website
$BUN run build
# G. Prepare Next.js SSR
echo ">>> Preparing Next.js SSR App..."
rsync -av --delete \
$TEMP_DIR/apps/website/.next/standalone/ $TARGET_WEBSITE/
rsync -av \
$TEMP_DIR/apps/website/public/ $TARGET_WEBSITE/apps/website/public/
rsync -av \
$TEMP_DIR/apps/website/.next/static/ $TARGET_WEBSITE/apps/website/.next/static/
cp $TEMP_DIR/ecosystem.config.js $TARGET_WEBSITE/
sed -i 's/homaroma/kittisak/g' $TARGET_WEBSITE/ecosystem.config.js
sed -i 's/PORT: 3002/PORT: 3005/g' $TARGET_WEBSITE/ecosystem.config.js
# H. Fix Permissions
echo ">>> Fixing Permissions..."
chown -R ti:ti $TARGET_BACKEND $TARGET_WEBSITE
# I. PM2 Management - SSR Next.js
cd $TARGET_WEBSITE
echo ">>> Creating symlink for .next..."
ln -sf apps/website/.next .next
echo ">>> Managing PM2 Process: $PM2_NAME_WEBSITE..."
$PM2 startOrReload $TARGET_WEBSITE/ecosystem.config.js --only $PM2_NAME_WEBSITE --env production --update-env
echo "PM2 process '$PM2_NAME_WEBSITE' is running."
# J. Cleanup
echo "Cleaning up build artifacts..."
rm -rf $TEMP_DIR
echo "------- ALL DEPLOYMENTS COMPLETED SUCCESSFULLY -------"
echo ""
echo "Service Summary:"
echo " Website (SSR) -> https://kittisak.com (PM2: $PM2_NAME_WEBSITE, Port: $SSR_PORT)"
echo " SPA -> https://spa.kittisak.com"
echo " Admin Panel -> https://admin.kittisak.com"
echo " API Backend -> https://api.kittisak.com (PM2: $PM2_NAME_BACKEND, Port: $BACKEND_PORT)"