diff --git a/deploy/nginx/abelbirdnest.id.http.conf b/deploy/nginx/abelbirdnest.id.http.conf
new file mode 100644
index 0000000..7a387f5
--- /dev/null
+++ b/deploy/nginx/abelbirdnest.id.http.conf
@@ -0,0 +1,26 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name abelbirdnest.id www.abelbirdnest.id;
+
+ client_max_body_size 20m;
+
+ location / {
+ proxy_pass http://127.0.0.1:3007;
+ 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 http;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 120s;
+ }
+
+ location /api/v1/health {
+ proxy_pass http://127.0.0.1:3007;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-Proto http;
+ }
+}
diff --git a/deploy/systemd/abelbirdnest-web.service b/deploy/systemd/abelbirdnest-web.service
index 2be85a6..d055c0b 100644
--- a/deploy/systemd/abelbirdnest-web.service
+++ b/deploy/systemd/abelbirdnest-web.service
@@ -4,10 +4,10 @@ After=network.target postgresql.service
[Service]
Type=simple
-WorkingDirectory=/var/www/abelbirdnest-web
+WorkingDirectory=/var/www/abelbirdnest-web/AbelBirdNest-Stock
Environment=NODE_ENV=production
Environment=PORT=3007
-EnvironmentFile=/var/www/abelbirdnest-web/.env.production
+EnvironmentFile=/var/www/abelbirdnest-web/AbelBirdNest-Stock/.env.production
ExecStart=/usr/bin/npm run start
Restart=always
RestartSec=5
diff --git a/docs/deploy-production.md b/docs/deploy-production.md
index 1d9b86e..58d134f 100644
--- a/docs/deploy-production.md
+++ b/docs/deploy-production.md
@@ -1,6 +1,6 @@
# Deploy Production
-Dokumen ini menyiapkan deploy production untuk:
+Panduan ini untuk deploy production dengan asumsi:
- domain `abelbirdnest.id`
- reverse proxy `nginx`
@@ -8,26 +8,26 @@ Dokumen ini menyiapkan deploy production untuk:
- database `PostgreSQL`
- source code dari git `https://git.iptek.co/wirabasalamah/AbelBirdNest-Stock.git`
- user service khusus `abelbirdnest`
+- repo berada di:
+ `/var/www/abelbirdnest-web/AbelBirdNest-Stock`
## 1. Persiapan Server
-Siapkan:
+Install:
- Node.js LTS
- npm
- PostgreSQL
- nginx
-- certbot / SSL Let’s Encrypt
+- certbot
-Direktori contoh:
+Direktori aplikasi:
```bash
/var/www/abelbirdnest-web
```
-## 2. Buat User Khusus Aplikasi
-
-Jalankan sebagai `root` atau dengan `sudo`:
+## 2. Buat User OS Khusus Aplikasi
```bash
sudo useradd -r -m -d /var/www/abelbirdnest-web -s /bin/bash abelbirdnest
@@ -35,54 +35,44 @@ sudo mkdir -p /var/www/abelbirdnest-web
sudo chown -R abelbirdnest:abelbirdnest /var/www/abelbirdnest-web
```
-Catatan:
-
-- user `abelbirdnest` dipakai khusus untuk menjalankan service aplikasi
-- jangan jalankan app production dengan user pribadi atau `root`
-
-## 3. Clone Repo dari Git
-
Masuk sebagai user aplikasi:
```bash
sudo -u abelbirdnest -H bash
cd /var/www/abelbirdnest-web
-git clone https://git.iptek.co/wirabasalamah/AbelBirdNest-Stock.git .
```
-Kalau server butuh autentikasi git internal, siapkan credential sesuai kebijakan server Git Anda.
+## 3. Clone Repo
-Catatan:
-
-- perintah di atas hanya benar jika `/var/www/abelbirdnest-web` masih kosong
-- jika Anda sudah menjalankan clone biasa dan hasilnya menjadi:
- `/var/www/abelbirdnest-web/AbelBirdNest-Stock`
- maka lanjutkan semua perintah deploy dari folder itu:
+Clone normal:
```bash
+git clone https://git.iptek.co/wirabasalamah/AbelBirdNest-Stock.git
cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
```
-- alternatifnya, jika ingin struktur tanpa subfolder tambahan, hapus isi folder tujuan lalu clone ulang dengan titik:
+Catatan:
+
+- panduan ini mengikuti struktur clone normal di atas
+- jadi `WorkingDirectory`, `.env.production`, dan semua perintah memakai path:
+ `/var/www/abelbirdnest-web/AbelBirdNest-Stock`
+
+## 4. Siapkan Environment Production
+
+Salin file contoh:
```bash
-rm -rf /var/www/abelbirdnest-web/AbelBirdNest-Stock
-cd /var/www/abelbirdnest-web
-git clone https://git.iptek.co/wirabasalamah/AbelBirdNest-Stock.git .
+cp .env.production.example .env.production
```
-## 4. Environment Production
-
-Salin `.env.production.example` menjadi `.env.production`, lalu isi nilainya.
-
-Yang wajib:
+Isi minimal:
```env
NODE_ENV=production
PORT=3007
APP_URL=https://abelbirdnest.id
-DATABASE_URL=postgresql://...
-AUTH_SECRET=...
+DATABASE_URL=postgresql://abelbirdnest_app:password@127.0.0.1:5432/abelbirdnest_prod?schema=public
+AUTH_SECRET=ganti-dengan-random-string-panjang
AUTH_BOOTSTRAP=false
SMTP_HOST=...
SMTP_PORT=465
@@ -94,20 +84,13 @@ SMTP_FROM=...
Catatan:
-- `AUTH_SECRET` harus random panjang.
-- `AUTH_BOOTSTRAP=false` wajib untuk production.
-- `APP_URL` harus domain production final.
+- `AUTH_BOOTSTRAP=false` wajib di production
+- akun default dev tidak akan aktif
+- `AUTH_SECRET` harus random dan panjang
-## 5. Inisialisasi Database PostgreSQL
+## 5. Inisialisasi PostgreSQL
-Contoh di bawah memakai:
-
-- database: `abelbirdnest_prod`
-- database user: `abelbirdnest_app`
-- host: `127.0.0.1`
-- port: `5432`
-
-Masuk ke PostgreSQL sebagai superuser:
+Masuk sebagai superuser PostgreSQL:
```bash
sudo -u postgres psql
@@ -119,26 +102,16 @@ Buat user database:
CREATE USER abelbirdnest_app WITH PASSWORD 'ganti-dengan-password-yang-kuat';
```
-Buat database production:
+Buat database:
```sql
CREATE DATABASE abelbirdnest_prod OWNER abelbirdnest_app;
-```
-
-Pastikan owner database benar:
-
-```sql
ALTER DATABASE abelbirdnest_prod OWNER TO abelbirdnest_app;
-```
-
-Opsional tapi disarankan, kunci privilege default:
-
-```sql
REVOKE ALL ON DATABASE abelbirdnest_prod FROM PUBLIC;
GRANT ALL PRIVILEGES ON DATABASE abelbirdnest_prod TO abelbirdnest_app;
```
-Keluar dari `psql`:
+Keluar:
```sql
\q
@@ -150,68 +123,79 @@ Tes koneksi:
psql "postgresql://abelbirdnest_app:ganti-dengan-password-yang-kuat@127.0.0.1:5432/abelbirdnest_prod"
```
-Jika koneksi berhasil, pakai URL itu di `.env.production`:
-
-```env
-DATABASE_URL="postgresql://abelbirdnest_app:ganti-dengan-password-yang-kuat@127.0.0.1:5432/abelbirdnest_prod?schema=public"
-```
-
Catatan:
-- untuk `psql`, jangan tambahkan `?schema=public`
-- untuk Prisma `DATABASE_URL`, tetap gunakan `?schema=public`
+- untuk `psql`, jangan pakai `?schema=public`
+- untuk `DATABASE_URL` Prisma, tetap pakai `?schema=public`
-## 6. Install Dependency, Database & Migration
+## 6. Install Dependency dan Buat Tabel
-Repo ini sudah disiapkan memakai migration Prisma.
-
-Jalankan:
+Masih sebagai user `abelbirdnest`:
```bash
cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
npm install
npm run prisma:generate
+```
+
+Load env production ke shell saat menjalankan command manual:
+
+```bash
+set -a
+source .env.production
+set +a
+```
+
+Jalankan migration:
+
+```bash
npm run prisma:migrate:deploy
```
-Kalau perlu isi master awal:
+Opsional cek status:
```bash
-npm run seed:master
+npx prisma migrate status
```
-Data seed yang dibawa:
+## 7. Seed Data Awal
-- grade
-- bank
-- currency
-
-### Khusus Seed Grade
-
-Seed `grade` membutuhkan file sumber `Grade.xls`.
-
-Sebelum menjalankan:
+Untuk fresh database:
```bash
-npm run seed:master
+cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
+set -a
+source .env.production
+set +a
+
+npm run seed:banks
+npm run seed:currencies
```
-unggah dulu file `Grade.xls` ke server, misalnya ke:
+### Khusus Grade
+
+`seed:grades` butuh file `Grade.xls`.
+
+Buat folder data:
+
+```bash
+mkdir -p scripts/data
+```
+
+Upload file `Grade.xls` ke:
```bash
/var/www/abelbirdnest-web/AbelBirdNest-Stock/scripts/data/Grade.xls
```
-Contoh dari laptop lokal:
-
-```bash
-scp "Grade.xls" user@server:/var/www/abelbirdnest-web/AbelBirdNest-Stock/scripts/data/Grade.xls
-```
-
Lalu jalankan:
```bash
cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
+set -a
+source .env.production
+set +a
+
npm run seed:grades
```
@@ -221,132 +205,210 @@ Alternatif jika file ada di lokasi lain:
node scripts/seed-grades-from-xls.mjs /path/ke/Grade.xls
```
-atau:
+## 8. Buat User Pertama di PostgreSQL
+
+Karena `AUTH_BOOTSTRAP=false`, Anda harus buat user login pertama sendiri.
+
+### 8.1 Generate Hash Password
+
+Ganti password contoh ini:
```bash
-GRADE_XLS_PATH=/path/ke/Grade.xls npm run seed:grades
+node -e 'const {randomBytes,scryptSync}=require("crypto"); const p="GantiPasswordKuat123!"; const salt=randomBytes(16).toString("hex"); const derived=scryptSync(p,salt,64).toString("hex"); console.log(`${salt}:${derived}`)'
```
-Urutan pertama kali untuk fresh database:
+Simpan output hash-nya.
+
+### 8.2 Masuk ke PostgreSQL
+
+```bash
+psql "postgresql://abelbirdnest_app:password@127.0.0.1:5432/abelbirdnest_prod"
+```
+
+### 8.3 Pastikan Role `SYSTEM_ADMIN` Ada
+
+```sql
+INSERT INTO roles (code, name, created_at, updated_at)
+VALUES ('SYSTEM_ADMIN', 'System Admin', NOW(), NOW())
+ON CONFLICT (code) DO UPDATE
+SET name = EXCLUDED.name,
+ updated_at = NOW();
+```
+
+### 8.4 Buat User Pertama
+
+Ganti:
+
+- `Nama Anda`
+- `superadmin`
+- `superadmin@abelbirdnest.id`
+- `HASH_HASIL_LANGKAH_8_1`
+
+```sql
+INSERT INTO users (
+ role_id,
+ name,
+ username,
+ email,
+ email_verified_at,
+ phone,
+ password_hash,
+ status,
+ created_at,
+ updated_at
+)
+SELECT
+ r.id,
+ 'Nama Anda',
+ 'superadmin',
+ 'superadmin@abelbirdnest.id',
+ NOW(),
+ NULL,
+ 'HASH_HASIL_LANGKAH_8_1',
+ 'ACTIVE',
+ NOW(),
+ NOW()
+FROM roles r
+WHERE r.code = 'SYSTEM_ADMIN'
+ON CONFLICT (email) DO UPDATE
+SET role_id = EXCLUDED.role_id,
+ name = EXCLUDED.name,
+ username = EXCLUDED.username,
+ email_verified_at = EXCLUDED.email_verified_at,
+ password_hash = EXCLUDED.password_hash,
+ status = EXCLUDED.status,
+ updated_at = NOW();
+```
+
+Verifikasi:
+
+```sql
+SELECT u.id, u.name, u.username, u.email, u.status, r.code AS role
+FROM users u
+JOIN roles r ON r.id = u.role_id
+WHERE u.email = 'superadmin@abelbirdnest.id';
+```
+
+## 9. Build Production
```bash
cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
-npm install
-npm run prisma:generate
-npm run prisma:migrate:deploy
-npm run seed:banks
-npm run seed:currencies
-npm run seed:grades
-```
+set -a
+source .env.production
+set +a
-Catatan:
-
-- `prisma:migrate:deploy` akan membuat seluruh tabel dari migration yang ada di repo
-- `seed:banks` dan `seed:currencies` bisa langsung dijalankan
-- `seed:grades` butuh file `Grade.xls` lebih dulu
-- user login production tetap harus dibuat terpisah, jangan mengandalkan akun dev/default
-
-## 7. Build Production
-
-```bash
-cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
npm run build
```
-## 8. Jalankan App di Port 3007
+## 10. Jalankan App dengan systemd
-Manual:
-
-```bash
-PORT=3007 npm run start
-```
-
-Atau gunakan `systemd` dari:
+File service repo sudah disiapkan untuk struktur subfolder ini:
```bash
deploy/systemd/abelbirdnest-web.service
```
-Contoh setup:
+Pasang:
```bash
-sudo cp deploy/systemd/abelbirdnest-web.service /etc/systemd/system/
+sudo cp /var/www/abelbirdnest-web/AbelBirdNest-Stock/deploy/systemd/abelbirdnest-web.service /etc/systemd/system/
+sudo chown -R abelbirdnest:abelbirdnest /var/www/abelbirdnest-web/AbelBirdNest-Stock
sudo systemctl daemon-reload
sudo systemctl enable abelbirdnest-web
-sudo systemctl start abelbirdnest-web
+sudo systemctl restart abelbirdnest-web
sudo systemctl status abelbirdnest-web
```
-Autostart saat server restart terjadi karena service di-`enable`.
-
-Untuk verifikasi:
+Verifikasi port:
```bash
-sudo systemctl is-enabled abelbirdnest-web
+ss -ltnp | grep 3007
+curl http://127.0.0.1:3007/api/v1/health
```
-## 9. Reverse Proxy Nginx
-
-Gunakan file:
+Kalau service gagal, cek:
```bash
-deploy/nginx/abelbirdnest.id.conf
+sudo systemctl cat abelbirdnest-web
+sudo journalctl -u abelbirdnest-web -n 100 --no-pager
+which npm
+```
+
+## 11. Pasang Nginx Tahap 1: HTTP Dulu
+
+Jangan langsung pakai config HTTPS sebelum sertifikat ada.
+
+Pakai file ini dulu:
+
+```bash
+deploy/nginx/abelbirdnest.id.http.conf
```
Pasang:
```bash
-sudo cp deploy/nginx/abelbirdnest.id.conf /etc/nginx/sites-available/abelbirdnest.id.conf
-sudo ln -s /etc/nginx/sites-available/abelbirdnest.id.conf /etc/nginx/sites-enabled/abelbirdnest.id.conf
+sudo cp /var/www/abelbirdnest-web/AbelBirdNest-Stock/deploy/nginx/abelbirdnest.id.http.conf /etc/nginx/sites-available/abelbirdnest.id.conf
+sudo ln -sf /etc/nginx/sites-available/abelbirdnest.id.conf /etc/nginx/sites-enabled/abelbirdnest.id.conf
sudo nginx -t
sudo systemctl reload nginx
```
-## 10. Health Check
-
-Endpoint health:
+Tes:
```bash
-GET /api/v1/health
+curl http://abelbirdnest.id/api/v1/health
```
-Contoh:
+## 12. Buat Sertifikat SSL
+
+Setelah HTTP sudah hidup:
+
+```bash
+sudo certbot --nginx -d abelbirdnest.id -d www.abelbirdnest.id
+```
+
+## 13. Ganti ke Config HTTPS Final
+
+Setelah sertifikat berhasil dibuat, baru pakai config final:
+
+```bash
+sudo cp /var/www/abelbirdnest-web/AbelBirdNest-Stock/deploy/nginx/abelbirdnest.id.conf /etc/nginx/sites-available/abelbirdnest.id.conf
+sudo nginx -t
+sudo systemctl reload nginx
+```
+
+Verifikasi:
```bash
curl https://abelbirdnest.id/api/v1/health
```
-## 11. Update Deployment Berikutnya
-
-Jika aplikasi sudah live dan ada update dari git:
+## 14. Update Deployment Berikutnya
```bash
cd /var/www/abelbirdnest-web/AbelBirdNest-Stock
git pull origin main
+
+set -a
+source .env.production
+set +a
+
npm install
+npm run prisma:generate
npm run prisma:migrate:deploy
npm run build
sudo systemctl restart abelbirdnest-web
+sudo systemctl status abelbirdnest-web
```
-Jika branch utama nanti bukan `main`, sesuaikan perintah `git pull`.
+## 15. Smoke Test Setelah Live
-## 12. Checklist Go-Live
+Cek minimal:
-- `AUTH_BOOTSTRAP=false`
-- `AUTH_SECRET` sudah production-grade
-- `APP_URL=https://abelbirdnest.id`
-- SSL aktif
-- database backup aktif
-- `npm run build` lulus
-- `npm run prisma:migrate:deploy` lulus
-- `npm run seed:master` selesai jika dibutuhkan
-- login, reset password, dan email verifikasi sudah dites
-- create purchase, receipt, lot, sale sudah dites
-
-## 13. Catatan Penting
-
-- Jangan pakai `npm run db:push` untuk production.
-- Jangan pakai akun default development.
-- Jangan simpan `.env.production` di repo.
-- Pastikan ownership file tetap `abelbirdnest:abelbirdnest`.
+- login dengan user `SYSTEM_ADMIN`
+- dashboard terbuka
+- health check OK
+- buat master `bank`, `currency`, `grade` tampil
+- buat transaksi sederhana
+- logout/login ulang
+- email reset/verifikasi jika SMTP sudah aktif
diff --git a/src/components/layout/app-shell.tsx b/src/components/layout/app-shell.tsx
index 7070619..1580c6f 100644
--- a/src/components/layout/app-shell.tsx
+++ b/src/components/layout/app-shell.tsx
@@ -31,6 +31,9 @@ export async function AppShell({
{dict.login.devAccounts}
-- {dict.login.autoFillHint} -
-