Skip to content

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

🌿 SEAPEDIA

Marketplace multi-peran bertema vintage - menghubungkan Buyer, Seller, Driver, dan Admin dalam satu ekosistem, dari katalog publik sampai pengiriman dan penanganan pesanan telat secara otomatis.

Dibuat untuk COMPFEST 18 Software Engineering Academy Technical Challenge. Seluruh 7 level (100 poin) telah diimplementasikan.


Daftar Isi

  1. Arsitektur & Tech Stack
  2. Struktur Proyek
  3. Setup & Instalasi
  4. Environment Variables
  5. Akun Demo
  6. Aturan Bisnis
  7. Keamanan
  8. Dokumentasi API
  9. Panduan Testing End-to-End
  10. Status Penyelesaian Level
  11. Deployment

Arsitektur & Tech Stack

Bagian Teknologi
Frontend React 19 + Vite + Tailwind CSS v4 + Framer Motion + React Router v7
Backend Node.js + Express
Database PostgreSQL (via Neon) + Prisma ORM
Autentikasi JWT disimpan di httpOnly cookie + bcrypt untuk hash password
Validasi Zod (frontend & backend)
Keamanan Helmet, express-rate-limit, sanitize-html, CORS terbatas
Dokumentasi API Swagger UI (OpenAPI 3.0), tersaji di /api/docs

Frontend dan backend adalah dua aplikasi terpisah (seapedia-vintage/ dan seapedia-api/) yang berkomunikasi lewat REST API.

Struktur Proyek

SEAPEDIA/
├── seapedia-api/                  Backend (Express + Prisma)
│   ├── prisma/
│   │   ├── schema.prisma           Skema database lengkap
│   │   └── seed.js                 Data demo (akun, toko, produk, voucher, promo)
│   └── src/
│       ├── constants/enums.js      Satu sumber kebenaran untuk semua aturan bisnis
│       ├── middleware/             requireAuth, requireRole, errorHandler
│       ├── modules/                Satu folder per domain (auth, product, order, dst)
│       ├── docs/openapi.js         Spesifikasi Swagger
│       └── app.js
│
├── seapedia-vintage/               Frontend (React + Vite)
│   └── src/
│       ├── api/                     Klien API per domain
│       ├── context/                 AuthContext, CartContext
│       ├── components/              Komponen reusable (UI, ProtectedRoute, ErrorBoundary)
│       ├── constants/                orderStatus.js - status & label bersama
│       └── pages/                    Dikelompokkan per peran (buyer/, seller/, driver/, admin/)
│
└── DEPLOYMENT.md                   Panduan deploy ke Render + Vercel + Neon

Setup & Instalasi

Butuh Node.js ≥ 18.

1. Database (Neon PostgreSQL - gratis, tanpa kedaluwarsa)

  1. Daftar di neon.tech, buat project baru.
  2. Salin connection string Postgres yang diberikan.

2. Backend

cd seapedia-api
cp .env.example .env
# Buka .env, isi DATABASE_URL dengan connection string dari Neon,
# dan JWT_SECRET dengan string acak (generate: node -e "console.log(require('crypto').randomBytes(48).toString('hex'))")

npm install
npm run prisma:migrate -- --name init
npm run seed
npm run dev

Server berjalan di http://localhost:4000. Cek http://localhost:4000/api/health.

3. Frontend

cd seapedia-vintage
cp .env.example .env
npm install
npm run dev

Buka http://localhost:5173.

Membuat Akun Admin Baru

Akun Admin tidak bisa didaftarkan lewat form register publik (sengaja dibatasi - lihat Aturan Bisnis). Cara membuat admin baru:

  • Cara termudah: pakai akun demo admin_akar dari seed data (lihat tabel di bawah).
  • Cara manual: jalankan npx prisma studio di folder seapedia-api, buka tabel User, buat user baru lalu tambahkan baris di tabel UserRole dengan role = "ADMIN" mengarah ke user tersebut.

Environment Variables

seapedia-api/.env

Variabel Contoh Keterangan
PORT 4000 Port server backend
NODE_ENV development production saat deploy
DATABASE_URL postgresql://... Connection string Postgres (Neon)
JWT_SECRET string acak ≥32 karakter Kunci penandatanganan token sesi
JWT_EXPIRES_IN 7d Masa berlaku sesi login
CLIENT_ORIGIN http://localhost:5173 Origin frontend yang diizinkan CORS
ADMIN_EMAIL / ADMIN_USERNAME / ADMIN_PASSWORD - Dipakai seed.js untuk akun admin pertama

seapedia-vintage/.env

Variabel Contoh Keterangan
VITE_API_URL http://localhost:4000/api Base URL backend

Akun Demo

Seluruhnya dibuat oleh npm run seed. Password sama untuk memudahkan demo.

Username Password Role Catatan
admin_akar Admin123! Admin Akses dashboard monitoring & voucher/promo
toko_hutan Seller123! Seller Toko "Toko Hutan Akar" (kategori tumbuhan)
kios_waktu Seller123! Seller Toko "Kios Waktu" (elektronik & arloji vintage)
pustaka_lama Seller123! Seller Toko "Pustaka Lama" (buku & alat tulis)
pembeli_akar Buyer123! Buyer Saldo awal Rp500.000
kurir_akar Driver123! Driver -
pengembara_akar Pengembara123! Buyer + Seller + Driver Untuk demo alur pemilihan role aktif

Voucher & Promo demo (Level 4): kode AKAR10 (diskon 10%, maks Rp20rb), HEMAT25K (potongan tetap Rp25rb), PROMO_AWAL (diskon 15%, maks Rp30rb) - bisa langsung dipakai saat checkout tanpa setup tambahan.

Aturan Bisnis

Bagian ini mendokumentasikan seluruh keputusan desain yang PDF tugas izinkan untuk ditentukan sendiri.

Single-Store Checkout

Satu cart hanya boleh berisi produk dari satu toko. Kalau buyer mencoba menambah produk dari toko lain saat cart sudah terisi, backend menolak dengan kode error STORE_CONFLICT dan frontend menampilkan modal konfirmasi "Kosongkan keranjang untuk melanjutkan?" - bukan sekadar pesan error. Implementasi: seapedia-api/src/modules/cart/cart.service.js, UI: seapedia-vintage/src/components/AddToCartControl.jsx.

Kombinasi Diskon & Perhitungan PPN

  • Voucher dan Promo tidak dapat digabung dalam satu checkout - buyer memilih salah satu.
  • Urutan perhitungan (lihat seapedia-api/src/modules/order/order.pricing.js):
subtotal       = Σ(harga × qty)
discountAmount = nilai diskon dari voucher ATAU promo (pilih salah satu)
taxableBase    = subtotal - discountAmount
ppnAmount      = floor(taxableBase × 12%)   ← ongkir TIDAK dikenai PPN
deliveryFee    = flat per metode pengiriman
totalAmount    = taxableBase + ppnAmount + deliveryFee
  • Ongkir flat: Instant Rp25.000, Next Day Rp15.000, Regular Rp9.000.
  • Voucher wajib punya expiresAt + usageLimit; Promo wajib punya expiresAt (tanpa batas pemakaian, berlaku untuk semua buyer selama belum kedaluwarsa).

Aturan Driver & Earning

  • Job pengiriman baru muncul untuk Driver setelah Seller memproses order (status Menunggu Kurir).
  • Satu order hanya bisa diambil oleh satu Driver (dijamin lewat conditional update di database, aman dari race condition dua driver klik bersamaan).
  • Satu Driver hanya boleh punya satu job aktif pada satu waktu - harus menyelesaikan job berjalan sebelum mengambil job baru.
  • Earning Driver = 80% dari ongkir pesanan (20% sisanya dianggap fee platform). Dikreditkan otomatis ke wallet Driver saat job dikonfirmasi selesai.

SLA Overdue & Simulasi Waktu

Metode Pengiriman Batas Waktu (SLA)
Instant 1 hari
Next Day 2 hari
Regular 4 hari

SLA dihitung sejak Seller memproses order (bukan sejak checkout), disimpan di kolom Order.overdueDeadline. Sistem memakai waktu virtual (tabel SystemClock, bukan waktu asli komputer) supaya bisa disimulasikan kapan saja tanpa menunggu hari sungguhan berlalu.

Cara simulasi: Login sebagai Admin → Dashboard Admin → masukkan jumlah hari → klik "Majukan Waktu". Aksi ini otomatis:

  1. Memajukan SystemClock.virtualNow.
  2. Mencari semua order yang overdueDeadline-nya sudah lewat dan masih berstatus Menunggu Kurir/Sedang Dikirim.
  3. Memindahkan order tersebut ke status Dikembalikan.
  4. Mengembalikan totalAmount penuh ke wallet Buyer (tercatat sebagai transaksi REFUND).
  5. Membalik pendapatan Seller yang sudah tercatat (transaksi SELLER_INCOME_REVERSAL) - supaya laporan pendapatan Seller tetap akurat.
  6. Mengembalikan stok produk sesuai kuantitas tiap item.

Seluruh proses berjalan dalam transaksi database, dengan pengecekan status ganda (idempotency guard) untuk mencegah satu order diproses refund dua kali.

Role & Autentikasi

  • Satu username non-admin boleh memiliki lebih dari satu role (Buyer/Seller/Driver) sekaligus.
  • Setelah login, kalau user punya >1 role non-admin, sistem meminta pemilihan role aktif sebelum masuk ke dashboard privat manapun.
  • Otorisasi selalu memakai role aktif saat itu, bukan daftar seluruh role yang dimiliki - kalau role aktif adalah Buyer, endpoint Seller tetap ditolak meski username itu juga terdaftar sebagai Seller.
  • Admin adalah akun privileged terpisah: tidak bisa didaftarkan lewat form publik, dan tidak ditawari fitur "tambah role" seperti akun non-admin.

Keamanan

Ringkasan implementasi untuk tiap area yang diminta di Level 7:

Area Implementasi
SQL Injection Seluruh akses database memakai Prisma ORM (parameterized query otomatis). Tidak ada satu pun $queryRaw/$executeRaw atau string SQL manual di codebase.
XSS Komentar ulasan publik disanitasi dengan sanitize-html (seluruh tag HTML dibuang) sebelum disimpan ke database - bukan hanya saat ditampilkan. React juga meng-escape output secara default; dangerouslySetInnerHTML tidak dipakai sama sekali di frontend.
Validasi Input Setiap endpoint yang menerima body/query memvalidasi dengan skema Zod sebelum diproses; error dikembalikan dalam format konsisten { field, message }.
Password Di-hash dengan bcrypt (10 salt rounds), tidak pernah disimpan atau dikembalikan dalam bentuk plain text.
Sesi JWT disimpan di cookie httpOnly (tidak bisa dibaca lewat JavaScript browser, tahan dari pencurian token via XSS), secure + sameSite=none otomatis aktif di production. Masa berlaku 7 hari (JWT_EXPIRES_IN). Logout menghapus cookie di server.
Session Expiry Handling Interceptor axios di frontend mendeteksi 401 di tengah pemakaian, membersihkan state login, dan mengarahkan user ke halaman login dengan pesan jelas - bukan error mentah di halaman acak.
RBAC (Role-Based Access Control) Middleware requireAuth + requireRole diterapkan di setiap endpoint privat (diaudit satu per satu, lihat daftar lengkap di kode). Role diverifikasi dari payload JWT yang sudah ditandatangani server, bukan dari data yang dikirim client - frontend tidak pernah dipercaya sebagai sumber kebenaran otorisasi.
Ownership Check Semua resource milik user (produk, alamat, item cart, order, delivery job) diverifikasi kepemilikannya di service layer sebelum diizinkan diubah/dihapus - bukan hanya mengandalkan role.
Rate Limiting Limiter global (300 req/15 menit) di seluruh API, plus limiter lebih ketat khusus endpoint sensitif: login/register (20 req/15 menit) dan submit ulasan (10 req/jam) untuk mencegah brute force dan spam.
Security Headers Helmet aktif secara default (mis. X-Content-Type-Options, HSTS).
CORS Dibatasi ke origin frontend spesifik (CLIENT_ORIGIN), bukan wildcard *, dengan credentials: true untuk cookie sesi.
Error Handling Error tak terduga di backend tidak pernah membocorkan detail internal ke client (pesan digeneralisir), tapi tetap di-log penuh di server untuk debugging. React ErrorBoundary mencegah satu error UI menjatuhkan seluruh aplikasi.

Suggested Test Cases (sesuai PDF)

# Tes anti-XSS: kirim script tag di komentar ulasan
curl -X POST http://localhost:4000/api/reviews \
  -H "Content-Type: application/json" \
  -d '{"name":"Tester","rating":5,"comment":"<script>alert(1)</script>Halo"}'
# Hasil yang benar: field "comment" pada response HANYA berisi "Halo",
# tag <script> sudah dibuang sebelum masuk database.

# Tes anti-SQLi: masukkan payload SQL-like di username login
curl -X POST http://localhost:4000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin_akar OR 1=1--","password":"apa saja"}'
# Hasil yang benar: "Username atau kata sandi salah" (401) - payload
# diperlakukan sebagai string biasa oleh Prisma, tidak memengaruhi query.

Dokumentasi API

Dokumentasi interaktif (Swagger UI) tersedia di:

http://localhost:4000/api/docs

Mencakup seluruh ~50 endpoint dari Level 1-6, lengkap dengan contoh request body dan skema response. Bisa langsung dicoba dari browser lewat tombol "Try it out" (kirim request nyata ke backend lokal kamu).

Panduan Testing End-to-End

Urutan ini menguji seluruh alur utama dari Level 1 sampai Level 6 dalam satu sesi demo:

  1. Guest - buka /, jelajahi katalog produk lintas kategori (tumbuhan, elektronik vintage, buku), buka detail salah satu produk, submit ulasan publik tanpa login.
  2. Registrasi & Login - daftar akun baru sebagai Buyer, logout, login lagi memakai akun demo multi-role pengembara_akar / Pengembara123! - perhatikan layar pemilihan role muncul karena akun ini punya Buyer+Seller+Driver.
  3. Seller (toko_hutan / Seller123!) - Dashboard → Kelola Produk → tambah 1 produk baru dengan stok terbatas.
  4. Buyer (pembeli_akar / Buyer123!) - Dashboard → Dompet → top up saldo → Alamat → tambah alamat → cari produk dari Seller di atas → tambah ke keranjang → Checkout → coba kode voucher AKAR10 → selesaikan pembayaran. Perhatikan status awal order: Sedang Dikemas.
  5. Seller lagi - Dashboard → Pesanan Masuk → klik Proses Pesanan (status → Menunggu Kurir, job otomatis muncul untuk Driver).
  6. Driver (kurir_akar / Driver123!) - Dashboard → Papan Tugas → ambil job yang baru muncul (status order → Sedang Dikirim) → buka detail job → Konfirmasi Selesai (status order → Pesanan Selesai, earning masuk wallet Driver).
  7. Cek Laporan - buka Laporan Belanja (Buyer) dan Laporan Pendapatan (Seller), pastikan angkanya konsisten dengan transaksi di atas.
  8. Admin (admin_akar / Admin123!) - Dashboard Admin → jelajahi tab monitoring (Users/Stores/Products/Orders/Deliveries) → buat 1 order baru sampai status "Menunggu Kurir" tapi jangan diambil Driver → masukkan angka hari lebih dari SLA metode pengirimannya (Instant=1, Next Day=2, Regular=4) di kotak "Majukan Waktu" → klik tombol → order tersebut otomatis pindah ke Dikembalikan, saldo Buyer ter-refund, dan hasilnya tampil langsung di layar.
  9. Kelola Diskon - dari Dashboard Admin, buka Voucher/Promo, buat kode baru, pastikan langsung bisa dipakai di checkout Buyer.

Status Penyelesaian Level

Level Fitur Status
1 Marketplace publik, autentikasi, role awareness, ulasan ✅ Selesai
2 Manajemen toko & produk Seller ✅ Selesai
3 Wallet, alamat, cart, checkout Buyer ✅ Selesai
4 Voucher/Promo, proses order Seller, laporan ✅ Selesai
5 Alur kerja Driver (cari/ambil/selesaikan job) ✅ Selesai
6 Monitoring Admin & auto-refund overdue ✅ Selesai
7 Security hardening & dokumentasi akhir ✅ Selesai

Deployment

Lihat DEPLOYMENT.md untuk panduan lengkap deploy ke Render (backend) + Vercel (frontend) + Neon (database), termasuk semua environment variable yang perlu diatur di masing-masing platform.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages