# 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.
