Prepare BizOne portal production wallet and UI
This commit is contained in:
350
docs/production-server-checklist.md
Normal file
350
docs/production-server-checklist.md
Normal file
@ -0,0 +1,350 @@
|
||||
# Production Server Checklist BizOne Portal
|
||||
|
||||
Panduan ini berisi langkah production `1-7` yang perlu dilakukan di server sebelum aplikasi BizOne Portal dipakai live.
|
||||
|
||||
Asumsi deployment:
|
||||
|
||||
- Domain: `portal.bizone.id`
|
||||
- App path: `/srv/bizone-web`
|
||||
- Backend: `127.0.0.1:3001`
|
||||
- Frontend: `127.0.0.1:3000`
|
||||
- PostgreSQL dan Redis memakai Docker Compose dari `deploy/debian12/docker-compose.infra.yml`
|
||||
|
||||
## 1. Bersihkan real secret dari repo dan rotate secret
|
||||
|
||||
File berikut tidak boleh berisi credential asli:
|
||||
|
||||
- `.env.example`
|
||||
- `deploy/debian12/app.env.example`
|
||||
- `README.md`
|
||||
|
||||
Isi semua secret di file example cukup memakai placeholder:
|
||||
|
||||
```dotenv
|
||||
DATABASE_URL=postgresql://bizone:change-this-password@127.0.0.1:5432/wa_dashboard
|
||||
JWT_SECRET=replace-with-32-plus-char-random-secret
|
||||
JWT_REFRESH_SECRET=replace-with-32-plus-char-random-refresh-secret
|
||||
WEBHOOK_VERIFY_TOKEN=replace-with-32-plus-char-random-token
|
||||
WEBHOOK_SHARED_SECRET=replace-with-32-plus-char-random-secret
|
||||
META_WEBHOOK_APP_SECRET=replace-with-real-meta-app-secret
|
||||
MAIL_PASSWORD=replace-with-real-smtp-password
|
||||
```
|
||||
|
||||
Di server, generate secret baru:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
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`
|
||||
- `META_WEBHOOK_APP_SECRET`
|
||||
|
||||
Jika secret lama pernah masuk repo, anggap bocor dan jangan dipakai lagi.
|
||||
|
||||
## 2. Siapkan `.env` production langsung di server
|
||||
|
||||
Masuk server:
|
||||
|
||||
```bash
|
||||
ssh user@SERVER_IP
|
||||
cd /srv/bizone-web
|
||||
nano .env
|
||||
```
|
||||
|
||||
Isi minimal seperti ini:
|
||||
|
||||
```dotenv
|
||||
NODE_ENV=production
|
||||
|
||||
DATABASE_URL=postgresql://bizone:ISI_PASSWORD_DB_URL_ENCODED@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
|
||||
INTERNAL_API_URL=http://127.0.0.1:3001/api
|
||||
NEXT_PUBLIC_API_URL=https://portal.bizone.id/backend-api
|
||||
|
||||
JWT_SECRET=ISI_SECRET_BARU
|
||||
JWT_EXPIRES_IN=1d
|
||||
JWT_REFRESH_SECRET=ISI_REFRESH_SECRET_BARU
|
||||
JWT_REFRESH_EXPIRES_IN=30d
|
||||
|
||||
WEBHOOK_VERIFY_TOKEN=ISI_VERIFY_TOKEN_BARU
|
||||
WEBHOOK_SHARED_SECRET=ISI_SHARED_SECRET_BARU
|
||||
META_WEBHOOK_APP_SECRET=ISI_META_APP_SECRET_BARU
|
||||
WEBHOOK_ALLOW_UNSIGNED=false
|
||||
|
||||
MAIL_HOST=mail.bizone.id
|
||||
MAIL_PORT=465
|
||||
MAIL_SECURE=true
|
||||
MAIL_USER=no-reply@bizone.id
|
||||
MAIL_PASSWORD=ISI_PASSWORD_SMTP
|
||||
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
|
||||
```
|
||||
|
||||
Amankan permission file `.env`:
|
||||
|
||||
```bash
|
||||
sudo chown bizone:bizone /srv/bizone-web/.env
|
||||
sudo chmod 600 /srv/bizone-web/.env
|
||||
```
|
||||
|
||||
Load env untuk command manual:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
```
|
||||
|
||||
## 3. Jalankan build backend dan frontend
|
||||
|
||||
Install dependency root untuk Prisma:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web
|
||||
npm install
|
||||
```
|
||||
|
||||
Build backend:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/backend
|
||||
npm install
|
||||
npm run db:generate
|
||||
npm run build
|
||||
```
|
||||
|
||||
Build frontend:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/frontend
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
Jika build gagal, jangan lanjut deploy. Bereskan error terlebih dahulu.
|
||||
|
||||
## 4. Jalankan database migration deploy
|
||||
|
||||
Pastikan PostgreSQL dan Redis hidup:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web/deploy/debian12
|
||||
docker compose -f docker-compose.infra.yml up -d
|
||||
docker compose -f docker-compose.infra.yml ps
|
||||
```
|
||||
|
||||
Jalankan migration:
|
||||
|
||||
```bash
|
||||
cd /srv/bizone-web
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
cd backend
|
||||
npm run db:migrate:deploy
|
||||
```
|
||||
|
||||
Jika database masih kosong dan butuh admin awal:
|
||||
|
||||
```bash
|
||||
npm run seed:admin
|
||||
```
|
||||
|
||||
Setelah login pertama, langsung ganti password admin dan aktifkan 2FA.
|
||||
|
||||
## 5. Tambahkan nginx security headers
|
||||
|
||||
Edit config nginx:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/portal.bizone.id
|
||||
```
|
||||
|
||||
Di dalam blok `server { ... }`, tambahkan:
|
||||
|
||||
```nginx
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
```
|
||||
|
||||
Pastikan route penting tetap seperti ini:
|
||||
|
||||
```nginx
|
||||
location = /api/health {
|
||||
proxy_pass http://127.0.0.1:3001/api/health;
|
||||
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;
|
||||
}
|
||||
|
||||
location /api/webhooks/ {
|
||||
proxy_pass http://127.0.0.1:3001/api/webhooks/;
|
||||
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;
|
||||
}
|
||||
|
||||
location /backend-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;
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
```
|
||||
|
||||
Test dan reload nginx:
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## 6. Deploy via systemd
|
||||
|
||||
Copy service files:
|
||||
|
||||
```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/
|
||||
```
|
||||
|
||||
Reload systemd:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
Enable service:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable bizone-backend
|
||||
sudo systemctl enable bizone-frontend
|
||||
```
|
||||
|
||||
Start atau restart service:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart bizone-backend
|
||||
sudo systemctl restart bizone-frontend
|
||||
```
|
||||
|
||||
Cek status:
|
||||
|
||||
```bash
|
||||
sudo systemctl status bizone-backend
|
||||
sudo systemctl status bizone-frontend
|
||||
```
|
||||
|
||||
Lihat log kalau ada error:
|
||||
|
||||
```bash
|
||||
sudo journalctl -u bizone-backend -f
|
||||
sudo journalctl -u bizone-frontend -f
|
||||
```
|
||||
|
||||
## 7. Smoke test production
|
||||
|
||||
Cek backend health:
|
||||
|
||||
```bash
|
||||
curl https://portal.bizone.id/api/health
|
||||
curl https://portal.bizone.id/backend-api/health
|
||||
```
|
||||
|
||||
Response ideal:
|
||||
|
||||
```json
|
||||
{"status":"ok","service":"wa-dashboard-backend","database":"ok"}
|
||||
```
|
||||
|
||||
Cek frontend:
|
||||
|
||||
```bash
|
||||
curl -I https://portal.bizone.id
|
||||
```
|
||||
|
||||
Cek service:
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
Test manual dari browser:
|
||||
|
||||
1. Buka `https://portal.bizone.id`.
|
||||
2. Login admin.
|
||||
3. Ganti password admin default.
|
||||
4. Aktifkan 2FA.
|
||||
5. Buka dashboard.
|
||||
6. Buka `Settings > WhatsApp API Setting`.
|
||||
7. Pastikan secret tidak tampil sebagai plain text di production.
|
||||
8. Buka `Webhook Logs`.
|
||||
9. Test webhook verification dari Meta.
|
||||
10. Kirim pesan WhatsApp ke nomor bisnis.
|
||||
11. Pastikan pesan masuk di Conversations.
|
||||
12. Balas dari dashboard jika token Meta dan phone number ID sudah benar.
|
||||
|
||||
Meta callback production:
|
||||
|
||||
```text
|
||||
https://portal.bizone.id/api/webhooks/whatsapp
|
||||
```
|
||||
|
||||
Verify token:
|
||||
|
||||
```text
|
||||
Pakai nilai WEBHOOK_VERIFY_TOKEN dari .env production.
|
||||
```
|
||||
|
||||
## Catatan setelah smoke test
|
||||
|
||||
Jika semua smoke test lolos, lanjutkan dengan hardening tambahan:
|
||||
|
||||
- Setup backup PostgreSQL terjadwal.
|
||||
- Setup monitor uptime ke `/api/health`.
|
||||
- Setup log retention `journald`.
|
||||
- Audit dependency dengan `npm audit` di root, backend, dan frontend.
|
||||
- Rotate admin password dan wajibkan 2FA untuk akun admin.
|
||||
Reference in New Issue
Block a user