Initial BizOne portal setup
This commit is contained in:
314
deploy/debian12/README.md
Normal file
314
deploy/debian12/README.md
Normal file
@ -0,0 +1,314 @@
|
||||
# Deploy Debian 12 for `portal.bizone.id`
|
||||
|
||||
Panduan ini menyiapkan `bizone-web` di server Debian 12 kosong dengan topologi berikut:
|
||||
|
||||
- `nginx` sebagai reverse proxy publik
|
||||
- `frontend` Next.js pada `127.0.0.1:3000`
|
||||
- `backend` NestJS pada `127.0.0.1:3001`
|
||||
- `PostgreSQL` dan `Redis` via Docker Compose
|
||||
- TLS dari Let's Encrypt
|
||||
|
||||
## URL Production Final
|
||||
|
||||
- Aplikasi: `https://portal.bizone.id`
|
||||
- Backend API public base URL: `https://portal.bizone.id/api`
|
||||
- Health check backend: `https://portal.bizone.id/api/health`
|
||||
- Webhook verify URL Meta: `https://portal.bizone.id/api/webhooks/whatsapp`
|
||||
- Webhook event URL Meta: `https://portal.bizone.id/api/webhooks/whatsapp`
|
||||
- Alternate provider-specific webhook URL: `https://portal.bizone.id/api/webhooks/whatsapp/meta`
|
||||
- Webhook logs UI: `https://portal.bizone.id/dashboard/webhooks/logs`
|
||||
|
||||
Untuk integrasi Meta, gunakan URL default berikut:
|
||||
|
||||
- Callback URL: `https://portal.bizone.id/api/webhooks/whatsapp`
|
||||
- Verify token: nilai `WEBHOOK_VERIFY_TOKEN` yang sama persis dengan env production
|
||||
|
||||
Catatan penting:
|
||||
|
||||
- Route backend memakai global prefix `/api`, jadi endpoint controller `GET /webhooks/whatsapp` menjadi `GET /api/webhooks/whatsapp`.
|
||||
- Jika Anda ingin verifikasi tanda tangan resmi dari Meta, isi `META_WEBHOOK_APP_SECRET`.
|
||||
- Bila `META_WEBHOOK_APP_SECRET` terisi, request ke `POST /api/webhooks/whatsapp/meta` menuntut header `x-hub-signature-256`.
|
||||
- Endpoint `POST /api/webhooks/whatsapp` tetap bisa dipakai untuk Meta bila Anda memilih verify token + shared secret non-Meta untuk test lain, tetapi untuk produksi Meta lebih aman menargetkan URL default callback dan menyimpan `META_WEBHOOK_APP_SECRET`.
|
||||
|
||||
## 1. DNS
|
||||
|
||||
Buat `A record`:
|
||||
|
||||
- `portal.bizone.id` -> IP publik server Debian 12
|
||||
|
||||
Pastikan propagasi selesai sebelum minta sertifikat TLS.
|
||||
|
||||
## 2. Install paket dasar
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y nginx certbot python3-certbot-nginx curl git build-essential
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt install -y nodejs
|
||||
curl -fsSL https://get.docker.com | sudo sh
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
Logout lalu login ulang setelah menambahkan grup `docker`.
|
||||
|
||||
Verifikasi:
|
||||
|
||||
```bash
|
||||
node -v
|
||||
npm -v
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
## 3. Siapkan user dan direktori aplikasi
|
||||
|
||||
```bash
|
||||
sudo useradd -m -s /bin/bash bizone
|
||||
sudo mkdir -p /srv
|
||||
sudo chown -R $USER:$USER /srv
|
||||
cd /srv
|
||||
git clone https://git.iptek.co/wirabasalamah/BizOne-portal.git bizone-web
|
||||
cd /srv/bizone-web
|
||||
```
|
||||
|
||||
Jika repo dikirim manual:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /srv/bizone-web
|
||||
sudo chown -R $USER:$USER /srv/bizone-web
|
||||
```
|
||||
|
||||
Lalu salin source code ke `/srv/bizone-web`.
|
||||
|
||||
## 4. Install dependency dan build
|
||||
|
||||
Root dependency Prisma:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web
|
||||
npm install
|
||||
```
|
||||
|
||||
Backend:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/backend
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/frontend
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 5. Siapkan `.env` production
|
||||
|
||||
Salin template:
|
||||
|
||||
```bash
|
||||
cp /srv/bizone-web/deploy/debian12/app.env.example /srv/bizone-web/.env
|
||||
```
|
||||
|
||||
Lalu ubah semua placeholder, terutama:
|
||||
|
||||
- `DATABASE_URL`
|
||||
- `JWT_SECRET`
|
||||
- `JWT_REFRESH_SECRET`
|
||||
- `WEBHOOK_VERIFY_TOKEN`
|
||||
- `WEBHOOK_SHARED_SECRET`
|
||||
- `META_WEBHOOK_APP_SECRET`
|
||||
- `MAIL_*`
|
||||
|
||||
Nilai yang wajib dipakai untuk domain ini:
|
||||
|
||||
```dotenv
|
||||
NODE_ENV=production
|
||||
FRONTEND_ORIGIN=https://portal.bizone.id
|
||||
PUBLIC_API_URL=https://portal.bizone.id
|
||||
NEXT_PUBLIC_API_URL=https://portal.bizone.id/api
|
||||
PORT=3001
|
||||
WEBHOOK_ALLOW_UNSIGNED=false
|
||||
```
|
||||
|
||||
Generate secret aman:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
openssl rand -hex 32
|
||||
openssl rand -hex 32
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
Gunakan hasil berbeda untuk:
|
||||
|
||||
- `JWT_SECRET`
|
||||
- `JWT_REFRESH_SECRET`
|
||||
- `WEBHOOK_VERIFY_TOKEN`
|
||||
- `WEBHOOK_SHARED_SECRET`
|
||||
|
||||
## 6. Jalankan PostgreSQL dan Redis
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/deploy/debian12
|
||||
docker compose -f docker-compose.infra.yml up -d
|
||||
docker compose -f docker-compose.infra.yml ps
|
||||
```
|
||||
|
||||
## 7. Generate Prisma client dan migrasi database
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/backend
|
||||
npm run db:generate
|
||||
npm run db:migrate:deploy
|
||||
```
|
||||
|
||||
Jika database masih kosong total, Anda bisa seed admin:
|
||||
|
||||
```bash
|
||||
npm run seed:admin
|
||||
```
|
||||
|
||||
Seed default saat ini:
|
||||
|
||||
- Email: `admin@example.com`
|
||||
- Password: `ChangeMe123!`
|
||||
|
||||
Masuk lalu segera ganti password.
|
||||
|
||||
## 8. Pasang service `systemd`
|
||||
|
||||
Salin file service:
|
||||
|
||||
```bash
|
||||
sudo cp /srv/bizone-web/deploy/debian12/bizone-backend.service /etc/systemd/system/
|
||||
sudo cp /srv/bizone-web/deploy/debian12/bizone-frontend.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable bizone-backend bizone-frontend
|
||||
sudo systemctl start bizone-backend bizone-frontend
|
||||
```
|
||||
|
||||
Cek status:
|
||||
|
||||
```bash
|
||||
sudo systemctl status bizone-backend
|
||||
sudo systemctl status bizone-frontend
|
||||
```
|
||||
|
||||
Lihat log:
|
||||
|
||||
```bash
|
||||
sudo journalctl -u bizone-backend -f
|
||||
sudo journalctl -u bizone-frontend -f
|
||||
```
|
||||
|
||||
## 9. Pasang `nginx`
|
||||
|
||||
Salin config:
|
||||
|
||||
```bash
|
||||
sudo cp /srv/bizone-web/deploy/debian12/nginx.portal.bizone.id.conf /etc/nginx/sites-available/portal.bizone.id
|
||||
sudo ln -s /etc/nginx/sites-available/portal.bizone.id /etc/nginx/sites-enabled/portal.bizone.id
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
Uji HTTP lokal:
|
||||
|
||||
```bash
|
||||
curl -I http://portal.bizone.id
|
||||
curl http://portal.bizone.id/api/health
|
||||
```
|
||||
|
||||
## 10. Aktifkan HTTPS
|
||||
|
||||
```bash
|
||||
sudo certbot --nginx -d portal.bizone.id
|
||||
```
|
||||
|
||||
Setelah cert aktif, uji:
|
||||
|
||||
```bash
|
||||
curl -I https://portal.bizone.id
|
||||
curl https://portal.bizone.id/api/health
|
||||
```
|
||||
|
||||
Respons health ideal:
|
||||
|
||||
```json
|
||||
{"status":"ok","service":"wa-dashboard-backend","database":"ok","timestamp":"..."}
|
||||
```
|
||||
|
||||
## 11. Data Meta yang harus Anda masukkan
|
||||
|
||||
Di Meta App Dashboard / WhatsApp configuration:
|
||||
|
||||
- Callback URL: `https://portal.bizone.id/api/webhooks/whatsapp`
|
||||
- Verify token: isi dengan nilai `WEBHOOK_VERIFY_TOKEN`
|
||||
|
||||
Jika Anda menyimpan `META_WEBHOOK_APP_SECRET`, backend akan memverifikasi signature event masuk dari header `x-hub-signature-256`.
|
||||
|
||||
Subscription event yang relevan untuk aplikasi ini:
|
||||
|
||||
- `messages`
|
||||
- `message_deliveries`
|
||||
- `message_read`
|
||||
- `message_sent`
|
||||
- `message_failed`
|
||||
- `template_category_update`
|
||||
- `account_update`
|
||||
|
||||
Mapping internal saat ini:
|
||||
|
||||
- `messages` -> `message.inbound`
|
||||
- `message_deliveries` -> `message.delivered`
|
||||
- `message_read` -> `message.read`
|
||||
- `message_sent` -> `message.sent`
|
||||
- `message_failed` -> `message.failed`
|
||||
- `template_category_update` -> `template.updated`
|
||||
- `account_update` -> `account.updated`
|
||||
|
||||
## 12. Urutan test live yang saya sarankan
|
||||
|
||||
1. Pastikan `https://portal.bizone.id/api/health` mengembalikan `200`.
|
||||
2. Coba buka `https://portal.bizone.id`.
|
||||
3. Lakukan webhook verification dari Meta dengan callback URL production.
|
||||
4. Kirim test webhook dari Meta.
|
||||
5. Login ke dashboard dan buka `Dashboard > Webhooks Logs`.
|
||||
6. Kirim pesan WhatsApp sungguhan ke nomor bisnis yang terhubung.
|
||||
7. Pastikan inbound message masuk ke inbox conversation.
|
||||
8. Balas dari dashboard bila access token dan `phoneNumberId` sudah terisi.
|
||||
9. Cek status `sent`, `delivered`, `read`, atau `failed` kembali masuk lewat webhook.
|
||||
|
||||
## 13. Command update deploy berikutnya
|
||||
|
||||
Setelah ada perubahan code:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web
|
||||
git pull
|
||||
npm install
|
||||
cd backend && npm install && npm run build && npm run db:generate && npm run db:migrate:deploy
|
||||
cd ../frontend && npm install && npm run build
|
||||
sudo systemctl restart bizone-backend bizone-frontend
|
||||
```
|
||||
|
||||
Jika server belum punya identity Git dan Anda ingin commit langsung dari server:
|
||||
|
||||
```bash
|
||||
git config user.name "Wira Irawan"
|
||||
git config user.email "wira.irawan@gmail.com"
|
||||
```
|
||||
|
||||
## 14. Smoke check minimal
|
||||
|
||||
```bash
|
||||
curl https://portal.bizone.id/api/health
|
||||
curl -I https://portal.bizone.id
|
||||
sudo systemctl is-active bizone-backend
|
||||
sudo systemctl is-active bizone-frontend
|
||||
docker compose -f /srv/bizone-web/deploy/debian12/docker-compose.infra.yml ps
|
||||
```
|
||||
33
deploy/debian12/app.env.example
Normal file
33
deploy/debian12/app.env.example
Normal file
@ -0,0 +1,33 @@
|
||||
NODE_ENV=production
|
||||
|
||||
DATABASE_URL=postgresql://bizone:change-this-postgres-password@127.0.0.1:5432/wa_dashboard
|
||||
REDIS_URL=redis://127.0.0.1:6379
|
||||
|
||||
PORT=3001
|
||||
FRONTEND_ORIGIN=https://portal.bizone.id
|
||||
PUBLIC_API_URL=https://portal.bizone.id
|
||||
NEXT_PUBLIC_API_URL=https://portal.bizone.id/api
|
||||
|
||||
JWT_SECRET=UsmlPBa61fKDgTjUR+9sS9f5SKw3OF7X0CjGWoHibg2eF7gQO6sS57pc2Hj8XIv4
|
||||
JWT_EXPIRES_IN=1d
|
||||
JWT_REFRESH_SECRET=mH50eOHDoJu3Ay6KQPt1IRdI9yED5P1sajq7LamFhiCRs51kcJvsg4azdjf8eq2W
|
||||
JWT_REFRESH_EXPIRES_IN=30d
|
||||
|
||||
WEBHOOK_VERIFY_TOKEN=iUFaqbqv98giFiYHGl1vcVQZRWGFKBHuewMZiHXufYU30uWE+TlC27pn/Ln2vtis
|
||||
WEBHOOK_SHARED_SECRET=CPK2/u9Gb/1pcsJL/jGbZA1N+ohEuL3l3T8mxZuyI4cIZtqnKW8QIfyguGD+nMMa
|
||||
META_WEBHOOK_APP_SECRET=replace-with-meta-app-secret
|
||||
WEBHOOK_ALLOW_UNSIGNED=false
|
||||
|
||||
MAIL_HOST=mail.bizone.id
|
||||
MAIL_PORT=465
|
||||
MAIL_SECURE=true
|
||||
MAIL_USER=no-reply@bizone.id
|
||||
MAIL_PASSWORD=62FwN86$3Y~#utQ@
|
||||
MAIL_FROM=Bizone Portal <no-reply@bizone.id>
|
||||
|
||||
AUTH_LOGIN_MAX_ATTEMPTS=5
|
||||
AUTH_LOGIN_WINDOW_MINUTES=15
|
||||
AUTH_2FA_MAX_ATTEMPTS=5
|
||||
AUTH_2FA_WINDOW_MINUTES=10
|
||||
AUTH_PASSWORD_RESET_MAX_ATTEMPTS=3
|
||||
AUTH_PASSWORD_RESET_WINDOW_MINUTES=30
|
||||
18
deploy/debian12/bizone-backend.service
Normal file
18
deploy/debian12/bizone-backend.service
Normal file
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Bizone Backend API
|
||||
After=network.target docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=bizone
|
||||
Group=bizone
|
||||
WorkingDirectory=/srv/bizone-web/backend
|
||||
EnvironmentFile=/srv/bizone-web/.env
|
||||
ExecStart=/usr/bin/npm run start:prod
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStopSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
18
deploy/debian12/bizone-frontend.service
Normal file
18
deploy/debian12/bizone-frontend.service
Normal file
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Bizone Frontend
|
||||
After=network.target bizone-backend.service
|
||||
Requires=bizone-backend.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=bizone
|
||||
Group=bizone
|
||||
WorkingDirectory=/srv/bizone-web/frontend
|
||||
EnvironmentFile=/srv/bizone-web/.env
|
||||
ExecStart=/usr/bin/npm run start
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStopSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
29
deploy/debian12/docker-compose.infra.yml
Normal file
29
deploy/debian12/docker-compose.infra.yml
Normal file
@ -0,0 +1,29 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
container_name: bizone-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: wa_dashboard
|
||||
POSTGRES_USER: bizone
|
||||
POSTGRES_PASSWORD: change-this-postgres-password
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
container_name: bizone-redis
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--appendonly", "yes"]
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
27
deploy/debian12/nginx.portal.bizone.id.conf
Normal file
27
deploy/debian12/nginx.portal.bizone.id.conf
Normal file
@ -0,0 +1,27 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name portal.bizone.id;
|
||||
|
||||
client_max_body_size 20m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
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 Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3001/api/;
|
||||
proxy_http_version 1.1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user