431 lines
11 KiB
Markdown
431 lines
11 KiB
Markdown
# Panduan Install WhatsApp Inbox di Ubuntu (Domain: app.zappcare.id)
|
|
|
|
Dokumen ini menyusun langkah deployment lengkap untuk server Ubuntu dengan kondisi:
|
|
|
|
- PostgreSQL sudah terinstall
|
|
- Nginx sudah terinstall
|
|
- Gitea berjalan di port `3001`
|
|
- Aplikasi ini tidak boleh pakai port `3000` karena dipakai layanan lain
|
|
|
|
Pada panduan ini, aplikasi akan jalan di **port `3002`** di loopback (`127.0.0.1:3002`) dan di-serve via Nginx ke domain `app.zappcare.id`.
|
|
|
|
## 1) Prasyarat
|
|
|
|
Pastikan server sudah memenuhi:
|
|
|
|
- Ubuntu 22.04/24.04 (sesuaikan)
|
|
- User dengan sudo
|
|
- PostgreSQL aktif
|
|
- Nginx aktif
|
|
- Node.js 20.x + npm
|
|
- Git
|
|
- domain `app.zappcare.id` mengarah ke IP server (DNS A record)
|
|
|
|
## 2) Install runtime (jika belum)
|
|
|
|
```bash
|
|
sudo apt update && sudo apt upgrade -y
|
|
sudo apt install -y curl ca-certificates git nginx postgresql postgresql-contrib
|
|
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
|
sudo apt install -y nodejs
|
|
|
|
node -v
|
|
npm -v
|
|
```
|
|
|
|
## 3) Buat database PostgreSQL
|
|
|
|
Masuk psql:
|
|
|
|
```bash
|
|
sudo -u postgres psql
|
|
```
|
|
|
|
Jalankan:
|
|
|
|
```sql
|
|
CREATE USER whatsapp_inbox WITH PASSWORD 'GANTI_PASSWORD_KUAT';
|
|
CREATE DATABASE whatsapp_inbox OWNER whatsapp_inbox;
|
|
\q
|
|
```
|
|
|
|
## 4) Setup user deploy
|
|
|
|
```bash
|
|
sudo useradd --system --home /var/www/whatsapp-inbox --shell /usr/sbin/nologin whatsapp-inbox || true
|
|
sudo mkdir -p /var/www/whatsapp-inbox
|
|
sudo chown -R whatsapp-inbox:whatsapp-inbox /var/www/whatsapp-inbox
|
|
```
|
|
|
|
## 5) Clone source & install dependency
|
|
|
|
```bash
|
|
sudo -u whatsapp-inbox git clone <REPO_URL> /var/www/whatsapp-inbox
|
|
cd /var/www/whatsapp-inbox
|
|
sudo -u whatsapp-inbox npm ci
|
|
```
|
|
|
|
> Ganti `<REPO_URL>` dengan URL repo Anda.
|
|
|
|
## 6) Buat `.env` production
|
|
|
|
Buat file dari template:
|
|
|
|
```bash
|
|
sudo cp /var/www/whatsapp-inbox/.env.example /var/www/whatsapp-inbox/.env
|
|
sudo chown whatsapp-inbox:whatsapp-inbox /var/www/whatsapp-inbox/.env
|
|
sudo -u whatsapp-inbox nano /var/www/whatsapp-inbox/.env
|
|
```
|
|
|
|
Isi `.env` yang wajib:
|
|
|
|
```env
|
|
NODE_ENV=production
|
|
PORT=3002
|
|
HOST=127.0.0.1
|
|
|
|
DATABASE_URL="postgresql://whatsapp_inbox:GANTI_PASSWORD_KUAT@127.0.0.1:5432/whatsapp_inbox?schema=public"
|
|
AUTH_SECRET="ganti_secret_acak_minimal_32_karakter"
|
|
|
|
APP_URL="https://app.zappcare.id"
|
|
NEXT_PUBLIC_APP_URL="https://app.zappcare.id"
|
|
OPS_BASE_URL="https://app.zappcare.id"
|
|
|
|
CAMPAIGN_RETRY_JOB_URL="https://app.zappcare.id"
|
|
CAMPAIGN_RETRY_JOB_TOKEN="ganti_token_acak"
|
|
HEALTHCHECK_TOKEN="ganti_token_health"
|
|
|
|
WHATSAPP_WEBHOOK_VERIFY_TOKEN="token_verify_webhook_anda"
|
|
WHATSAPP_WEBHOOK_SECRET="webhook_secret_anda"
|
|
WHATSAPP_API_TOKEN="meta_whatsapp_api_token"
|
|
WHATSAPP_API_VERSION="v22.0"
|
|
WHATSAPP_ALLOW_SIMULATED_SEND="false"
|
|
|
|
CAMPAIGN_RETRY_BATCH_SIZE="100"
|
|
CAMPAIGN_RETRY_MAX_CAMPAIGNS="20"
|
|
CAMPAIGN_RETRY_JOB_LOCK_TTL_SECONDS="300"
|
|
CAMPAIGN_RETRY_DAEMON_INTERVAL_SECONDS="300"
|
|
CAMPAIGN_RETRY_DAEMON_TIMEOUT_MS="30000"
|
|
CAMPAIGN_RETRY_ALERT_ON_FAILURE="true"
|
|
CAMPAIGN_RETRY_ALERT_WEBHOOK_URL=""
|
|
|
|
WEBHOOK_FAILURE_RATE_THRESHOLD_PER_HOUR="20"
|
|
RETRY_WORKER_STALE_MINUTES="30"
|
|
LOGIN_RATE_LIMIT_ATTEMPTS="10"
|
|
LOGIN_RATE_LIMIT_WINDOW_MS="900000"
|
|
CAMPAIGN_RETRY_JOB_RATE_LIMIT_GET="60"
|
|
CAMPAIGN_RETRY_JOB_RATE_LIMIT_POST="20"
|
|
CAMPAIGN_RETRY_JOB_RATE_LIMIT_WINDOW_MS="60000"
|
|
WHATSAPP_WEBHOOK_RATE_LIMIT_GET="60"
|
|
WHATSAPP_WEBHOOK_RATE_LIMIT_POST="120"
|
|
WHATSAPP_WEBHOOK_RATE_LIMIT_WINDOW_MS="60000"
|
|
|
|
AUTH_TOKEN_CONSUMED_RETENTION_HOURS="24"
|
|
CAMPAIGN_RETRY_STALE_LOCK_MINUTES="120"
|
|
WEBHOOK_EVENT_RETENTION_DAYS="30"
|
|
AUDIT_LOG_RETENTION_DAYS="365"
|
|
```
|
|
|
|
> Pastikan nilai `DATABASE_URL` sesuai username/password/password database Anda.
|
|
|
|
## 7) Pastikan Prisma pakai PostgreSQL
|
|
|
|
### Cek `schema.prisma`
|
|
|
|
Buka `prisma/schema.prisma` dan pastikan datasource:
|
|
|
|
```prisma
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
```
|
|
|
|
Jika masih `sqlite`, migrasi ke Postgres harus dibangun ulang.
|
|
Catatan: perubahan provider dan migration bisa dilakukan di environment staging/dev sebelum deploy production.
|
|
|
|
### Jalankan migration + seed
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
sudo -u whatsapp-inbox npm run db:deploy
|
|
sudo -u whatsapp-inbox npm run db:seed
|
|
```
|
|
|
|
## 8) Build & verifikasi
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
sudo -u whatsapp-inbox npm run ci:verify
|
|
```
|
|
|
|
Jika berhasil, lanjut ke service.
|
|
|
|
## 9) Test manual port 3002
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
sudo -u whatsapp-inbox npm run start -- --hostname 127.0.0.1 --port 3002
|
|
```
|
|
|
|
Di terminal lain:
|
|
|
|
```bash
|
|
curl -I http://127.0.0.1:3002
|
|
curl -s http://127.0.0.1:3002/api/health
|
|
```
|
|
|
|
## 10) Buat service systemd (Next.js app)
|
|
|
|
Buat file `/etc/systemd/system/whatsapp-inbox.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=WhatsApp Inbox (Next.js App)
|
|
After=network.target postgresql.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=whatsapp-inbox
|
|
Group=whatsapp-inbox
|
|
WorkingDirectory=/var/www/whatsapp-inbox
|
|
EnvironmentFile=/var/www/whatsapp-inbox/.env
|
|
ExecStart=/usr/bin/npm run start -- --hostname 127.0.0.1 --port 3002
|
|
Restart=always
|
|
RestartSec=5
|
|
LimitNOFILE=65535
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
Enable dan start:
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now whatsapp-inbox
|
|
sudo systemctl status whatsapp-inbox
|
|
```
|
|
|
|
## 11) Service retry worker (daemon)
|
|
|
|
Buat file `/etc/systemd/system/whatsapp-inbox-retry.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=WhatsApp Inbox Campaign Retry Daemon
|
|
After=network.target whatsapp-inbox.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=whatsapp-inbox
|
|
Group=whatsapp-inbox
|
|
WorkingDirectory=/var/www/whatsapp-inbox
|
|
EnvironmentFile=/var/www/whatsapp-inbox/.env
|
|
ExecStart=/usr/bin/npm run job:campaign-retry:daemon
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now whatsapp-inbox-retry
|
|
sudo systemctl status whatsapp-inbox-retry
|
|
```
|
|
|
|
## 12) Konfigurasi Nginx reverse proxy
|
|
|
|
Buat file `/etc/nginx/sites-available/app.zappcare.id`:
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name app.zappcare.id;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/html;
|
|
}
|
|
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name app.zappcare.id;
|
|
|
|
client_max_body_size 20m;
|
|
proxy_buffering off;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/app.zappcare.id/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/app.zappcare.id/privkey.pem;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3002;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Connection "";
|
|
proxy_set_header Host $host;
|
|
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_set_header X-Forwarded-Host $host;
|
|
proxy_read_timeout 120s;
|
|
proxy_send_timeout 120s;
|
|
}
|
|
}
|
|
```
|
|
|
|
Aktifkan site dan reload:
|
|
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/app.zappcare.id /etc/nginx/sites-enabled/
|
|
sudo nginx -t
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
## 13) Install SSL (Let's Encrypt)
|
|
|
|
```bash
|
|
sudo apt install -y certbot python3-certbot-nginx
|
|
sudo certbot --nginx -d app.zappcare.id
|
|
```
|
|
|
|
## 14) Cek akhir deployment
|
|
|
|
```bash
|
|
curl -I https://app.zappcare.id
|
|
curl -I https://app.zappcare.id/api/health
|
|
curl -s https://app.zappcare.id/api/health | jq
|
|
```
|
|
|
|
Ops check:
|
|
|
|
```bash
|
|
APP_URL=https://app.zappcare.id NEXT_PUBLIC_APP_URL=https://app.zappcare.id OPS_BASE_URL=https://app.zappcare.id npm run ops:readiness
|
|
APP_URL=https://app.zappcare.id NEXT_PUBLIC_APP_URL=https://app.zappcare.id OPS_BASE_URL=https://app.zappcare.id npm run ops:smoke
|
|
```
|
|
|
|
## 15) Update rutin
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
git pull origin main
|
|
sudo -u whatsapp-inbox npm ci
|
|
sudo -u whatsapp-inbox npm run ci:verify
|
|
sudo -u whatsapp-inbox npm run db:deploy
|
|
sudo systemctl restart whatsapp-inbox
|
|
sudo systemctl restart whatsapp-inbox-retry
|
|
```
|
|
|
|
## 16) Rollback cepat
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
git log --oneline -n 10
|
|
git checkout <commit-sebelumnya>
|
|
sudo systemctl restart whatsapp-inbox
|
|
```
|
|
|
|
## 17) Hal penting untuk environment produksi
|
|
|
|
- Pastikan semua token tidak lagi `change-me`/`your-*`.
|
|
- Monitor `ops:readiness` + `ops:smoke` tiap hari / saat deploy.
|
|
- Jalankan `ops:maintenance` berkala (cron harian/mingguan).
|
|
- Pastikan job retry tetap hidup:
|
|
- `sudo systemctl status whatsapp-inbox-retry`
|
|
|
|
```bash
|
|
sudo systemctl status whatsapp-inbox
|
|
sudo systemctl status whatsapp-inbox-retry
|
|
journalctl -u whatsapp-inbox -f
|
|
journalctl -u whatsapp-inbox-retry -f
|
|
```
|
|
|
|
## 18) Troubleshooting cepat
|
|
|
|
### 18.1 Domain menjawab 502 Bad Gateway
|
|
|
|
```bash
|
|
sudo systemctl status whatsapp-inbox
|
|
sudo ss -ltnp | rg "127.0.0.1:3002"
|
|
sudo nginx -t
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
- Jika service app tidak jalan, cek log:
|
|
`journalctl -u whatsapp-inbox -n 200 --no-pager`
|
|
- Pastikan Nginx proxy ke `127.0.0.1:3002` (bukan 3000/3001).
|
|
- Jika app jalan, cek `.env` dan `PORT=3002`.
|
|
|
|
### 18.2 Halaman health 500 / tidak bisa start
|
|
|
|
```bash
|
|
cd /var/www/whatsapp-inbox
|
|
sudo -u whatsapp-inbox npm run ops:readiness
|
|
```
|
|
|
|
- Jika `DATABASE_URL` error:
|
|
- cek service PostgreSQL: `sudo systemctl status postgresql`
|
|
- cek koneksi manual: `psql "postgresql://whatsapp_inbox:GANTI_PASSWORD_KUAT@127.0.0.1:5432/whatsapp_inbox?schema=public" -c '\dt'`
|
|
- Jika token/secret bermasalah:
|
|
- cek value `AUTH_SECRET`, `CAMPAIGN_RETRY_JOB_TOKEN`, `WHATSAPP_WEBHOOK_SECRET`
|
|
- jangan ada placeholder seperti `change-me`, `your-*`
|
|
|
|
### 18.3 Retry job tidak berjalan
|
|
|
|
```bash
|
|
sudo systemctl status whatsapp-inbox-retry
|
|
sudo -u whatsapp-inbox npm run job:campaign-retry
|
|
```
|
|
|
|
- Cek token pada `.env` (`CAMPAIGN_RETRY_JOB_TOKEN`) dan endpoint:
|
|
- `https://app.zappcare.id/api/jobs/campaign-retry?token=<token>`
|
|
- Jika lock stuck, jalankan:
|
|
- `sudo -u whatsapp-inbox npm run job:campaign-retry`
|
|
- restart service: `sudo systemctl restart whatsapp-inbox-retry`
|
|
|
|
### 18.4 Webhook tidak terima event
|
|
|
|
```bash
|
|
curl -i https://app.zappcare.id/api/webhooks/whatsapp
|
|
```
|
|
|
|
- Pastikan URL di Meta adalah:
|
|
- `https://app.zappcare.id/api/webhooks/whatsapp`
|
|
- Untuk validasi:
|
|
- cek `WHATSAPP_WEBHOOK_VERIFY_TOKEN`
|
|
- cek `WHATSAPP_WEBHOOK_SECRET`
|
|
- cek `Signature` header dari provider sesuai konfigurasi
|
|
|
|
### 18.5 Port bentrok / service lain
|
|
|
|
```bash
|
|
sudo lsof -i :3000
|
|
sudo lsof -i :3001
|
|
sudo lsof -i :3002
|
|
```
|
|
|
|
- Jika app tidak boleh pakai 3000/3001, pastikan `.env` dan service tetap di 3002.
|
|
- Jika ada proses yang tidak dikenal, stop service itu atau pindahkan port dengan service systemd yang benar.
|
|
|
|
### 18.6 Cek cepat setelah reboot atau deploy
|
|
|
|
```bash
|
|
sudo systemctl restart whatsapp-inbox
|
|
sudo systemctl restart whatsapp-inbox-retry
|
|
sudo systemctl status whatsapp-inbox whatsapp-inbox-retry --no-pager
|
|
curl -s https://app.zappcare.id/api/health | cat
|
|
```
|
|
|
|
Jika masih ada masalah:
|
|
|
|
```bash
|
|
APP_URL=https://app.zappcare.id NEXT_PUBLIC_APP_URL=https://app.zappcare.id OPS_BASE_URL=https://app.zappcare.id npm run ops:readiness
|
|
APP_URL=https://app.zappcare.id NEXT_PUBLIC_APP_URL=https://app.zappcare.id OPS_BASE_URL=https://app.zappcare.id npm run ops:smoke
|
|
```
|