- Laravel 13.9 + PHP 8.5 + MySQL - Bootstrap 5.3 + jQuery 3.7 + Chart.js (replacing Alpine/Tailwind) - Packages: intervention/image, dompdf, simple-qrcode, league/csv, laravel/breeze, laravel/boost - 17 database migrations: users, programs, qr_codes, participants, attendances, certificates, questionnaires, email_logs, audit_logs - 13 Eloquent models with full relationships - Admin layout (Bootstrap 5 sidebar) + public layout (mobile-first) - Rate limiters: checkin (60/min), certificate (30/min) - Admin seeder: admin@mbip.gov.my - Storage directories + symlink configured Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.4 KiB
7.4 KiB
eCert MBIP — System Architecture
Overview
Sistem pengurusan sijil digital (eCert) untuk program-program yang dianjurkan oleh MBIP (Majlis Bandaraya Ipoh Perak). Sistem membolehkan admin urus program, kehadiran peserta, soalselidik, dan penjanaan sijil digital secara automatik.
Technology Stack
| Komponen | Pilihan | Versi |
|---|---|---|
| Backend Framework | Laravel | 12.x (latest stable) |
| PHP | PHP | 8.5.x |
| Database | MySQL | 8.x |
| Frontend CSS | Bootstrap | 5.3 |
| Frontend JS | jQuery | 3.7 |
| Template Engine | Blade | (built-in) |
| Asset Pipeline | Vite | (built-in Laravel) |
| Queue Driver | Database (→ Redis production) | — |
| Storage | Laravel Storage (local disk) | — |
Composer Packages (Cadangan)
Core / Wajib
| Package | Tujuan | Sebab Pilih |
|---|---|---|
intervention/image:^3.0 |
Image manipulation untuk generate sijil | GD sudah ada, sokongan penuh Malay font TTF |
barryvdh/laravel-dompdf:^3.0 |
Generate PDF sijil dari HTML | Standard Laravel, senang maintain |
simplesoftwareio/simple-qrcode:^4.0 |
Generate QR Code PNG | Wrapper BaconQRCode, Laravel-friendly |
league/csv:^9.0 |
Import CSV peserta | Ringan, tanpa dependency besar, handle UTF-8 BOM |
laravel/breeze:^2.0 |
Admin auth scaffolding | Minimal, Blade stack, senang customise |
Optional (Cadang Fasa Lanjut)
| Package | Tujuan | Bila Perlukan |
|---|---|---|
maatwebsite/excel:^3.1 |
Import Excel (.xlsx) | Jika perlu format Excel (bukan CSV) |
spatie/laravel-activitylog:^4.0 |
Audit log | Jika audit trail perlu lebih structured |
Kenapa Tidak Pakai
- Imagick: Tidak ada sebagai PHP extension dalam environment ini — guna GD sahaja.
- Laravel Sanctum/Passport: Tidak perlu — public peserta akses via token, bukan API JWT.
- Livewire/Vue/React: Tidak dalam requirement — kekal Blade + jQuery.
- Spatie Media Library: Over-engineered untuk keperluan ini — guna Laravel Storage terus.
Laravel Boost — Perlu Penjelasan
NOTA: "Laravel Boost" tidak jelas merujuk kepada package mana. Kemungkinan:
- Laravel Breeze — Auth scaffolding (cadangan saya untuk admin auth)
- Package dalaman — Jika ada package khusus MBIP/organisasi
- Laravel Octane — Performance boost (tidak diperlukan untuk sistem ini)
Sila sahkan apa yang dimaksudkan dengan "Laravel Boost" sebelum Fasa 1 dimulakan.
Application Architecture
eCert MBIP
├── Admin Module (authenticated, /admin/*)
│ ├── Program Management
│ ├── QR Code Management
│ ├── Participant Management (pre-register + import)
│ ├── Certificate Template Management
│ ├── Questionnaire Management
│ └── Statistics & Reports
│
├── Public Module (token-based, /p/* dan /certificate/*)
│ ├── QR Scan → Check-in Page
│ ├── Staff Check-in Flow
│ ├── Walk-in Registration Flow
│ ├── Questionnaire Page
│ └── Certificate Download
│
└── Background Jobs (Queue)
├── GenerateCertificateJob
├── SendCertificateEmailJob
└── BlastCertificateLinkJob
Key Design Decisions
1. Token-Based Public Access
- Peserta awam tidak login.
- Semua akses public guna
tokenatau Laravel Signed URL. - QR code bawa ke
/p/{qr_token}— token UUID 64 char, bukan ID. - Certificate download guna
/certificate/{cert_token}— UUID unik per sijil. - Questionnaire link guna
participant.uuiddalam signed URL.
2. Participant Identity
no_kpadalah pengecam unik peserta dalam sistem.- Jika peserta hadir program berbeza, rekod
participantsdikongsi (cari byno_kp). - Satu peserta boleh ada banyak
program_participantsdanattendances.
3. Certificate Generation Strategy
- On-demand: Sijil dijana semasa peserta minta download buat pertama kali.
- Flow: Soalselidik selesai → dispatch
GenerateCertificateJob→ simpan PNG → wrap PDF. - Jika
certificate.file_pathsudah ada, terus serve — tidak jana semula. - Gagal generate: simpan
status = failed, log error ringkas.
4. Queue Strategy
- Development:
QUEUE_CONNECTION=database(simpan dalamjobstable). - Production: cadang
QUEUE_CONNECTION=redis(perlu Redis server). - Queue worker perlu jalan sebagai service (Supervisor di Linux / Task Scheduler di Windows).
5. File Storage
- Semua fail dalam
storage/app/private/— tidak boleh akses direct. - Download melalui controller dengan
Storage::download(). - QR code images dalam
storage/app/public/qrcodes/(boleh akses via symlink). - Certificate template dalam
storage/app/private/templates/. - Generated certificates dalam
storage/app/private/certificates/{program_uuid}/.
6. Font untuk Certificate
- Guna
.ttffont file dalamstorage/app/fonts/atauresources/fonts/. - Cadang bundel Noto Sans (Google Fonts, open source, sokong Malay charset).
- Font size perlu auto-scale berdasarkan panjang nama peserta.
7. Admin Auth
- Standard Laravel auth (email + password).
- Guna Laravel Breeze (Blade stack) untuk scaffolding.
- Semua
/admin/*route dilindungi middlewareauth. - Satu role sahaja buat masa ini:
admin(users table dengan is_admin flag atau role enum).
Security Architecture
Request Layer:
- Rate limiting: /p/* (60/min), /certificate/* (30/min), /admin/* (unlimited)
- CSRF protection: semua POST form
- Input validation: FormRequest classes
Auth Layer:
- Admin: session-based Laravel auth
- Public: token validation + signed URL verification
Data Layer:
- no_kp: TIDAK dalam URL — guna UUID/token sahaja
- Sensitive files: dalam storage/app/private (tidak boleh direct access)
- Database: prepared statements (Eloquent ORM)
Audit Layer:
- audit_logs table: admin actions (create/update program, upload template, dll)
- email_logs table: semua email attempt
- certificates table: track generated_at, emailed_at, downloaded_at
Directory Structure (Cadangan)
app/
├── Http/
│ ├── Controllers/
│ │ ├── Admin/ ← semua admin controller
│ │ └── Public/ ← semua public controller
│ ├── Requests/ ← FormRequest validation
│ └── Middleware/
├── Models/
├── Jobs/
│ ├── GenerateCertificateJob.php
│ └── SendCertificateEmailJob.php
├── Mail/
│ └── CertificateReadyMail.php
├── Services/
│ ├── CertificateService.php ← image manipulation logic
│ ├── QrCodeService.php
│ └── AttendanceService.php
└── Imports/
└── ParticipantImport.php ← CSV import logic
resources/
├── views/
│ ├── admin/ ← semua admin views
│ ├── public/ ← public check-in, questionnaire, download
│ ├── emails/ ← email templates
│ └── layouts/
│ ├── admin.blade.php
│ └── public.blade.php
├── fonts/ ← TTF fonts untuk certificate
└── js/ + css/
storage/app/
├── public/qrcodes/ ← QR code images (accessible via symlink)
├── private/
│ ├── templates/ ← certificate template images
│ ├── certificates/ ← generated certificates
│ └── imports/ ← temporary CSV uploads