Sistem Laporan Penggunaan Kartu Kredit Direksi
Stack: React · Node.js · Express · PostgreSQL · Prisma ORM · JWT
- Gambaran Proyek
- Arsitektur Sistem
- Tumpukan Teknologi
- Struktur Proyek
- Prasyarat
- Instalasi & Pengaturan
- Konfigurasi Lingkungan
- Pengaturan Database
- Menjalankan Aplikasi
- Sistem Autentikasi
- Referensi API
- Fitur Ekspor (PDF & Excel)
- Log Aktivitas
- Deployment Ke Produksi
- Panduan Migrasi ke Active Directory
- Panduan Migrasi ke Microsoft SQL Server
- Pertimbangan Keamanan
CC Report adalah aplikasi web internal untuk mengelola dan melaporkan pengeluaran kartu kredit direksi perusahaan.
Fitur Utama:
- Login aman dengan JWT dan rotasi refresh token
- Dashboard dengan statistik real-time dan grafik aktivitas
- Manajemen data Direksi beserta kartu kredit
- Pembuatan dan pengelolaan laporan CC bulanan
- Manajemen transaksi per laporan secara inline
- Ekspor ke PDF dan Excel dalam format dokumen resmi
- Log aktivitas lengkap dengan filter, pencarian, dan paginasi
- Manajemen pengguna oleh Admin dengan kontrol akses berbasis peran (RBAC)
┌─────────────────────────────────────────────────────────────┐
│ Browser (React SPA) │
│ React 18 + Vite │ React Router v7 │ lucide-react │
│ AuthContext (JWT di memori) │ ToastContext (notifikasi UI) │
└─────────────────────────────┬───────────────────────────────┘
│ HTTP/HTTPS (REST API)
│ Access Token: Authorization: Bearer
│ Refresh Token: httpOnly Cookie
▼
┌─────────────────────────────────────────────────────────────┐
│ Nginx Reverse Proxy (Produksi) │
│ Terminasi HTTPS │ Melayani file statis │ Proxy API │
└─────────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Node.js + Express Backend │
│ Port: 5000 │
│ ├── Middleware: Helmet, CORS, Rate Limit, Cookie Parser │
│ ├── Auth Middleware: Verifikasi JWT + pemeriksaan peran │
│ ├── Validasi: Skema Zod per endpoint │
│ ├── Services: auth.service.js (dapat diganti untuk AD) │
│ └── Controllers: auth, directors, reports, transactions, │
│ users, dashboard, activityLog │
└─────────────────────────────┬───────────────────────────────┘
│ Prisma ORM
▼
┌─────────────────────────────────────────────────────────────┐
│ Database PostgreSQL (port 5432) │
│ Tabel: User, Director, CreditCard, Report, │
│ Transaction, ActivityLog │
└─────────────────────────────────────────────────────────────┘
| Lapisan | Teknologi | Versi | Fungsi |
|---|---|---|---|
| Frontend | React | 18.x | Framework UI |
| Build Tool | Vite | 6.x | Dev server + bundler |
| Routing | React Router | 7.x | Routing sisi klien |
| Ikon | lucide-react | 0.465.x | Perpustakaan ikon |
| HTTP Client | Axios | 1.7.x | Panggilan API + interceptor |
| Ekspor PDF | jsPDF + autotable | 4.x / 5.x | Pembuatan PDF di browser |
| Ekspor Excel | ExcelJS + file-saver | 4.x | Pembuatan Excel di browser |
| Backend Runtime | Node.js | 18+ | Runtime JavaScript |
| Framework Backend | Express | 4.x | Server HTTP |
| ORM | Prisma | 6.x | Akses database |
| Database | PostgreSQL | 14+ | Database relasional |
| Autentikasi | jsonwebtoken | 9.x | Penerbitan dan verifikasi JWT |
| Enkripsi Password | bcryptjs | 2.x | Hashing password aman |
| Validasi | Zod | 4.x | Validasi skema data |
| Header Keamanan | Helmet | 8.x | Header HTTP keamanan |
| Rate Limiting | express-rate-limit | 8.x | Proteksi brute-force |
| Cookie | cookie-parser | 1.x | Penanganan cookie httpOnly |
CC Report/
├── .env # Variabel lingkungan
├── .env.example # Template
├── .gitignore
├── nginx.conf.example # Konfigurasi Nginx untuk produksi
├── User Manual.md # Panduan untuk pengguna akhir
│
├── backend/
│ ├── .gitignore
│ ├── package.json
│ ├── prisma/
│ │ ├── schema.prisma # Skema database
│ │ └── migrations/ # Riwayat migrasi Prisma
│ └── src/
│ ├── server.js # Setup Express + middleware
│ ├── db.js # Singleton Prisma client
│ ├── controllers/ # Handler HTTP request (satu per domain)
│ ├── services/ # Layer logika bisnis
│ │ ├── auth.service.js # Dapat diganti untuk AD
│ │ └── AD_INTEGRATION_GUIDE.js # Referensi migrasi AD
│ ├── middleware/
│ │ ├── auth.js # Verifikasi JWT + penjaga peran
│ │ └── validate.js # Middleware validasi Zod
│ ├── routes/ # Definisi rute (satu per domain)
│ └── utils/
│ ├── activityLogger.js # Pencatatan jejak audit
│ └── reportMapper.js # Transformasi data
│
└── client/
├── .gitignore
├── package.json
└── src/
├── App.jsx # Komponen root + routing + penjaga rute
├── main.jsx # Titik masuk React
├── index.css # Sistem desain global (CSS variables)
├── api/axios.js # Instance Axios + interceptor auto-refresh
├── context/ # AuthContext + ToastContext
├── components/ # Layout, Sidebar, CustomSelect, ExportModal
├── pages/ # Satu file per halaman/rute
└── utils/ # helpers.js, exportPdf.js, exportExcel.js
| Kebutuhan | Versi Minimum |
|---|---|
| Node.js | 18.x LTS |
| npm | 9.x |
| PostgreSQL | 14.x |
| Git | Versi terbaru |
git clone https://github.com/Irsanngrh/CCREPORT.git
cd "CC Report"cd backend
npm installcd ../client
npm install# Dari direktori root proyek
cp .env.example .env
# Edit .env dengan nilai yang sesuaiIsi file .env di direktori root:
# Koneksi PostgreSQL
DATABASE_URL="postgresql://USER:PASSWORD@localhost:5432/cc_report?schema=public"
# JWT Secrets (gunakan string acak yang kuat)
JWT_SECRET="masukkan-string-rahasia-kuat-di-sini"
JWT_REFRESH_SECRET="masukkan-string-rahasia-berbeda-kuat-di-sini"
# Port server
PORT=5000
# URL frontend untuk konfigurasi CORS (development)
CLIENT_URL="http://localhost:5173"
# Environment
NODE_ENV="development"Cara membuat secret yang kuat:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"CREATE DATABASE cc_report;cd backend
# Dorong skema ke database (development)
npm run db:push
# Atau jalankan migrasi (setelah baseline diatur)
npx prisma migrate deployUser ──(1:N)──► ActivityLog
Director ──(1:N)──► CreditCard
Director ──(1:N)──► Report
CreditCard ──(1:N)──► Report
Report ──(1:N)──► Transaction
Keputusan Desain Utama:
- Semua kolom keuangan menggunakan
Decimal(15,2)untuk presisi akurat @@unique([directorId, creditCardId, month, year])mencegah duplikasi laporanonDelete: Cascadememastikan integritas referensial di seluruh relasiActivityLogmemiliki 4 index untuk performa query yang optimal
Terminal 1 — Backend:
cd backend
npm start # node src/server.js
# Atau dengan hot-reload:
npm run dev # nodemon src/server.jsTerminal 2 — Frontend:
cd client
npm run dev # Vite dev server di http://localhost:5173cd client
npm run build # Output: client/dist/Permintaan Login
│
└─► auth.service.loginWithCredentials()
│ Verifikasi password dengan bcrypt
│ Buat Access Token (JWT, 15 menit)
│ Buat Refresh Token (JWT, 7 hari)
│ Simpan Refresh Token di DB
▼
Set cookie httpOnly (refresh_token, 7 hari)
Kembalikan Access Token di body response
│
▼
Frontend simpan Access Token di memori JS saja
(BUKAN localStorage — aman dari serangan XSS)
│
└─► Semua panggilan API: Authorization: Bearer <accessToken>
Saat API mengembalikan 401 TOKEN_EXPIRED, interceptor Axios secara otomatis:
- Kirim POST ke
/api/auth/refresh(token via cookie — tidak dapat diakses JS) - Terima access token baru
- Antre semua permintaan yang gagal dan ulangi dengan token baru
Saat logout, refresh token di-set null di database — mencegah reuse meskipun klien masih menyimpan salinannya.
| Penjaga Rute | File | Perlindungan |
|---|---|---|
ProtectedRoute |
App.jsx | Memerlukan pengguna terautentikasi |
AdminRoute |
App.jsx | Memerlukan role = 'admin' |
authenticate |
middleware/auth.js | Verifikasi JWT di backend |
requireAdmin |
middleware/auth.js | Pemeriksaan peran admin di backend |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| POST | /api/auth/login |
Publik | Login dengan username + password |
| POST | /api/auth/refresh |
Cookie | Rotasi refresh token |
| POST | /api/auth/logout |
Cookie | Cabut refresh token |
| GET | /api/auth/me |
Bearer | Informasi pengguna saat ini |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| GET | /api/directors |
Bearer | Daftar semua direksi |
| POST | /api/directors |
Bearer | Buat direksi baru |
| PUT | /api/directors/:id |
Bearer | Ubah data direksi |
| DELETE | /api/directors/:id |
Bearer | Hapus direksi |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| GET | /api/reports |
Bearer | Daftar laporan (dapat difilter) |
| POST | /api/reports |
Bearer | Buat laporan baru |
| PUT | /api/reports/:id |
Bearer | Ubah laporan |
| DELETE | /api/reports/:id |
Bearer | Hapus laporan |
| POST | /api/reports/:id/log-export/:format |
Bearer | Catat aksi ekspor |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| POST | /api/reports/:reportId/transactions |
Bearer | Tambah transaksi |
| PUT | /api/transactions/:id |
Bearer | Ubah transaksi |
| DELETE | /api/transactions/:id |
Bearer | Hapus transaksi |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| GET | /api/users |
Admin | Daftar semua pengguna |
| POST | /api/users |
Admin | Buat pengguna baru |
| PUT | /api/users/:id |
Admin | Ubah pengguna |
| DELETE | /api/users/:id |
Admin | Hapus pengguna |
| POST | /api/users/me/change-password |
Bearer | Ubah password sendiri |
| Metode | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| GET | /api/dashboard |
Bearer | Statistik dashboard |
| GET | /api/logs |
Admin | Log aktivitas (dapat difilter) |
| GET | /api/logs/filters |
Admin | Opsi filter log |
Kedua ekspor berjalan sepenuhnya di browser tanpa rendering server.
- Library:
jspdf+jspdf-autotable - Output: Format resmi PT ASABRI dengan logo, tabel rekapitulasi, terbilang, dan blok tanda tangan dua kolom
- Pratinjau: Dapat dibuka di tab baru sebelum diunduh
- Library:
ExcelJS+file-saver - Output: Format
.xlsxdengan sel yang digabung, border, dan logo tertanam
- Klik "Ekspor Laporan" di halaman detail laporan
- Modal
ExportModalterbuka — isi nomor rekap, nomor PO, nama penandatangan - Klik "Unduh PDF" atau "Unduh Excel"
- File dibuat di browser dan diunduh secara otomatis
- Aksi ekspor dicatat via
POST /api/reports/:id/log-export/:format
Semua aksi signifikan dicatat ke tabel ActivityLog via logActivity() di activityLogger.js.
| Kategori | Aksi |
|---|---|
| Autentikasi | LOGIN, LOGOUT |
| Direksi | CREATE_DIRECTOR, UPDATE_DIRECTOR, DELETE_DIRECTOR |
| Laporan | CREATE_REPORT, UPDATE_REPORT, DELETE_REPORT |
| Transaksi | CREATE_TRANSACTION, UPDATE_TRANSACTION, DELETE_TRANSACTION |
| Pengguna | CREATE_USER, UPDATE_USER, DELETE_USER, CHANGE_PASSWORD |
| Ekspor | EXPORT_PDF, EXPORT_EXCEL |
cd client && npm run buildNODE_ENV=production node src/server.jsPerubahan yang aktif otomatis di produksi:
- Cookie
secure: true(hanya via HTTPS) - Cookie
sameSite: 'strict'(proteksi CSRF lebih ketat)
Salin nginx.conf.example ke server dan sesuaikan:
server_namedengan domain Anda- Path sertifikat SSL
rootke path folderclient/dist
npm install -g pm2
cd backend
pm2 start src/server.js --name cc-report-api --env production
pm2 save && pm2 startupHanya satu fungsi yang perlu diganti: loginWithCredentials() di src/services/auth.service.js.
Lihat src/services/AD_INTEGRATION_GUIDE.js untuk contoh lengkap.
- Pembuatan JWT (
generateTokens) - Rotasi dan pencabutan token
auth.controller.js- Semua middleware perlindungan rute
- Seluruh logika autentikasi frontend
npm install ldapjsLDAP_URL="ldap://your-ad-server.company.local"
LDAP_BASE_DN="dc=company,dc=local"datasource db {
provider = "sqlserver" // Ganti dari "postgresql"
url = env("DATABASE_URL")
}DATABASE_URL="sqlserver://server;database=cc_report;user=sa;password=PASSWORD;encrypt=true"npx prisma generate
npx prisma migrate deploy- Collation MSSQL: Gunakan
Latin1_General_CI_ASuntuk mendukung pencarian case-insensitive Decimal(15,2): Kompatibel penuh dengan MSSQLDECIMAL(15,2)- Tidak ada query raw yang bergantung pada sintaks PostgreSQL-spesifik
| Area | Implementasi |
|---|---|
| Penyimpanan password | bcrypt dengan cost factor 12 |
| Access token | JWT, 15 menit, disimpan di memori saja |
| Refresh token | JWT, 7 hari, cookie httpOnly + secure + sameSite |
| Pencabutan token | Database-backed: refreshToken = null saat logout |
| Rate limiting | 20 req/15 menit autentikasi, 200 req/15 menit umum |
| Header keamanan | Helmet.js (X-Frame-Options, HSTS, nosniff) |
| Validasi input | Skema Zod di semua endpoint write |
| CORS | Dibatasi hanya ke CLIENT_URL |
| SQL injection | Dicegah oleh query terparameterisasi Prisma |
| XSS | React meng-escape output secara default |