# Tasyakuran NFBS Bogor

Aplikasi undangan digital tasyakuran kelulusan NFBS Bogor, dikelola per angkatan dan dikirim ke orang tua/wali santri via WhatsApp.

## Stack

- **Backend**: Express.js (ESM, Node 18+)
- **Template**: EJS + express-ejs-layouts
- **DB**: MariaDB / MySQL via Drizzle ORM + mysql2
- **Auth**: Passport (local + Google OAuth) + sesi MySQL
- **Frontend**: Tailwind CSS + Alpine.js (untuk drag posisi label & toggle UI)
- **Upload**: Multer + Sharp (auto-compress gambar ke WebP)
- **Encryption**: AES-256-GCM untuk nomor WA

Stack ini dipilih ringan agar muat di cPanel ~1GB disk (tanpa Next.js build).

## Setup Lokal

```bash
# 1. Install dependency
npm install

# 2. Salin dan isi env
cp .env.example .env
# Edit .env: DB_*, SESSION_SECRET, ENCRYPTION_KEY (64 char hex), Google OAuth (opsional)
# Generate ENCRYPTION_KEY:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# 3. Buat database (di MySQL/MariaDB)
mysql -u root -p -e "CREATE DATABASE tasyakuran CHARACTER SET utf8mb4;"

# 4. Migrasi tabel
npm run db:migrate

# 5. Seed admin awal (pakai ADMIN_EMAIL & ADMIN_PASSWORD dari .env)
npm run db:seed

# 6. Build CSS Tailwind
npm run build:css

# 7. Jalankan
npm run dev
```

Akses: http://localhost:3000

## Alur Pemakaian

### Admin
1. Login dengan akun admin yang di-seed.
2. Buat **Angkatan** baru (contoh: Angkatan 7, tahun 2026).
3. Buat **Kelas** dan tetapkan **Wali Kelas** (user dengan role `wali_kelas`).
4. Buat **User wali kelas** (Manajemen User).
5. **Setting Undangan** per angkatan: upload background, atur posisi nama (drag/koordinat), backsound (opsional), custom text rich text, template WA.
6. **Bulk Import Santri** lewat CSV (download template dulu).
7. Pantau progress di dashboard.

### Wali Kelas
1. Login.
2. Buka kelas yang dipegang.
3. Edit nomor WA santri jika perlu.
4. Klik **Preview** atau **Test** ke nomor sendiri.
5. Klik **Kirim WA** → konfirmasi → tab WhatsApp terbuka, status auto-marked terkirim.
6. Bisa **Kirim Ulang** kapan saja, atau **Reset** marking.

### Orang Tua
- Buka link `https://domain/{nomor-angkatan}/{slug-nama}` di HP.
- Lihat nama anak di atas background, custom text undangan, backsound looping (jika ada).

## Format CSV Bulk Import

Header wajib: `nis,nama,kelas,nomor_wa`

```csv
nis,nama,kelas,nomor_wa
22001,Ahmad Fauzi,XII IPA 1,08123456789
22002,Siti Aminah,XII IPA 1,628987654321
```

- Nama kelas harus **persis sama** dengan kelas yang sudah dibuat di angkatan target.
- Nomor WA otomatis dinormalisasi ke format `628xxx`.
- NIS harus unik per angkatan.

## Placeholder Template WA

- `{nama}` → nama santri
- `{kelas}` → nama kelas
- `{nis}` → NIS
- `{link}` → URL undangan

## Deploy ke cPanel

1. Upload semua file (kecuali `node_modules`, `.env`).
2. Buat database MySQL/MariaDB di cPanel.
3. Di **Setup Node.js App**: pilih Node LTS, application root, startup file `app.js`.
4. Set environment variables (lihat `.env.example`).
5. Klik **Run NPM Install**.
6. Lewat cPanel terminal:
   ```bash
   npm run db:migrate
   npm run db:seed
   npm run build:css
   ```
7. Restart aplikasi.

### Optimasi cPanel
- Set `NODE_ENV=production` agar express-session pakai secure cookie (butuh HTTPS).
- Set `APP_URL` ke domain publik.
- Pastikan folder `public/uploads/` writable.
- Untuk Google OAuth, set callback URL: `https://domain/auth/google/callback` di Google Cloud Console.

## Keamanan

- Nomor WA terenkripsi at-rest (AES-256-GCM, key di `ENCRYPTION_KEY`).
- Password di-hash bcrypt cost 10.
- Session cookie httpOnly, sameSite=lax, secure di production.
- Rate limit login: 10 attempt / 15 menit.
- Wali kelas hanya bisa akses kelasnya sendiri (middleware filter).
- Custom text undangan di-sanitize dengan `sanitize-html` sebelum disimpan & dirender.
- Audit log untuk semua aksi penting.

## Reset Password

Tidak ada fitur self-service reset. Admin reset manual via DB:

```sql
-- Generate hash dulu di Node:
-- node -e "require('bcrypt').hash('passwordbaru', 10).then(console.log)"
UPDATE users SET password_hash = '$2b$10$...' WHERE email = 'user@nfbs.local';
```

## Lisensi

Internal NFBS Bogor.
