Lompat ke konten
dokumentasi.id
Commit a2522f5
v1
Star645

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

2

Arsitektur Aplikasi

Aplikasi ini adalah satu aplikasi Laravel dengan dua permukaan terpisah: panel admin Filament di path admin, dan portal eksternal berbasis komponen Livewire. Keduanya berbagi model domain, policy, dan layer pendukung yang sama. Tiga service provider didaftarkan secara eksplisit, yaitu AppServiceProvider, EventServiceProvider, dan AdminPanelProvider (bootstrap/providers.php:1-7). Routing web dimuat dari routes/web.php melalui konfigurasi aplikasi di bootstrap/app.php:7-12.

Peta lapisan

Diagram berikut menunjukkan bagaimana routes/web.php membelah trafik ke panel admin, portal eksternal, dan dua route autentikasi Google, lalu bagaimana tiap cabang menyentuh model domain, policy, dan layer pendukung.

graph TD
  REQ["HTTP request"] --> ROUTES["routes/web.php"]
  ROUTES --> WELCOME["closure welcome"]
  ROUTES --> GOOGLE["GoogleController"]
  ROUTES --> EXTLOGIN["ExternalLogin"]
  ROUTES --> EXTDASH["ExternalDashboard"]
  ROUTES --> PANEL["AdminPanelProvider panel admin /admin"]
  subgraph ADMIN["Admin panel"]
    PANEL --> MW["FilamentUserSettings"]
    MW --> SETTING["Setting::getUserValue"]
    MW --> PALETTE["ColorPalette::constantFor"]
    PANEL --> RES["Filament Resources dan Pages"]
    RES --> POLICY["TicketPolicy"]
  end
  subgraph EXTERNAL["External portal"]
    EXTLOGIN --> EA["ExternalAccess"]
    EXTDASH --> EA
  end
  subgraph SUPPORT["Supporting layer"]
    EVENT["ProjectMemberAttached dan ProjectMemberDetached"] --> LISTENER["SendProjectAssignmentNotification dan SendProjectRemovalNotification"]
    NS["NotificationService"] --> NOTIF["Notification"]
  end
  POLICY --> MODELS["Eloquent models"]
  EA --> MODELS
  NOTIF --> MODELS
  MODELS --> DB[("MySQL")]
  style DB fill:#336791,color:#fff
  style MW fill:#f59e0b,color:#000
  style SETTING fill:#f59e0b,color:#000
  style POLICY fill:#8b5cf6,color:#fff
  style EA fill:#f59e0b,color:#000
%% fig: Peta lapisan aplikasi
%% desc: Satu berkas route membelah trafik ke panel admin Filament, portal eksternal Livewire, dan autentikasi Google, lalu semuanya bermuara ke model Eloquent dan database.
Fig. 1: Peta lapisan aplikasiSatu berkas route membelah trafik ke panel admin Filament, portal eksternal Livewire, dan autentikasi Google, lalu semuanya bermuara ke model Eloquent dan database.

Panel admin didaftarkan lewat PanelProvider dengan ->default(), ->id('admin'), dan ->path('admin'), serta mengaktifkan mode SPA dan pembungkusan transaksi database (app/Providers/Filament/AdminPanelProvider.php:25-43). Resource, Page, dan Widget tidak didaftarkan satu per satu, melainkan ditemukan otomatis dari direktori app/Filament/Resources, app/Filament/Pages, dan app/Filament/Widgets.

Di atas panel itu terpasang rantai middleware EncryptCookies, StartSession, AuthenticateSession, VerifyCsrfToken, SubstituteBindings, hingga FilamentUserSettings, ditambah plugin FilamentShieldPlugin, ->passwordReset(), ->emailVerification(), ->profile(), dan tema Vite resources/css/filament/admin/theme.css (app/Providers/Filament/AdminPanelProvider.php:44-66).

Middleware pengaturan per pengguna

FilamentUserSettings adalah middleware yang menyentuh database pada setiap request panel. Ketika auth()->check() bernilai benar, middleware membaca warna primer tersimpan lewat Setting::getUserValue('filament_primary_color', 'blue', $userId), mendaftarkannya ke FilamentColor::register, lalu mengatur gaya navigasi (app/Http/Middleware/FilamentUserSettings.php:15-29). Nilai filament_navigation_style menentukan apakah panel memakai topNavigation() atau sidebarCollapsibleOnDesktop(true) (app/Http/Middleware/FilamentUserSettings.php:40-57). Kedua pembacaan dibungkus try/catch dengan nilai cadangan blue dan sidebar, sehingga kegagalan baca tidak memutus request.

⚠️ Middleware ini berjalan untuk setiap request panel setelah login, bukan sekali saat sesi dibuat. Setiap request membaca tabel settings dua kali.

Alur satu request di panel admin

sequenceDiagram
  participant B as Browser
  participant R as routes/web.php
  participant P as AdminPanelProvider
  participant M as FilamentUserSettings
  participant S as Setting
  participant F as Filament Resource
  participant Pol as TicketPolicy
  B->>R: GET /admin/...
  R->>P: panel admin id admin path admin
  P->>M: middleware FilamentUserSettings
  M->>S: getUserValue filament_primary_color
  S-->>M: nama warna atau cadangan blue
  M->>P: register FilamentColor primary
  M-->>P: next request
  P->>F: render halaman atau resource
  F->>Pol: can view_ticket
  Pol-->>F: permission dan hasAccessToTicket
  F-->>B: HTML response
%% fig: Alur request panel admin
%% desc: Request panel admin melewati FilamentUserSettings yang membaca Setting, lalu otorisasi resource diperiksa TicketPolicy sebelum response dikirim.
Fig. 2: Alur request panel adminRequest panel admin melewati FilamentUserSettings yang membaca Setting, lalu otorisasi resource diperiksa TicketPolicy sebelum response dikirim.

Langkah otorisasi tidak berhenti pada string permission. TicketPolicy memisahkan pemeriksaan izin dari pemeriksaan keterjangkauan data: view mengembalikan $authUser->can('view_ticket') && $this->hasAccessToTicket($authUser, $ticket) (app/Policies/TicketPolicy.php:46-49), sedangkan hasAccessToTicket meloloskan super_admin, pembuat ticket, assignee, dan anggota project pemilik ticket (app/Policies/TicketPolicy.php:22-39). Pola yang sama dipakai update, delete, restore, dan forceDelete. Rincian peran dan permission ada di Kontrol Akses dan Peran.

Portal eksternal klien

Portal eksternal tidak memakai panel Filament. Dua route Livewire menanganinya, yaitu /external/{token} dan /external/{token}/dashboard (routes/web.php:17-20). ExternalLogin memvalidasi $token terhadap kolom access_token yang is_active bernilai true, dan abort(404, 'External access not found') bila tidak ada (app/Livewire/ExternalLogin.php:22-37). Setelah password cocok, komponen menyimpan external_project_id, external_authenticated, external_authenticated_at_<token>, dan external_authenticated_<token> ke session (app/Livewire/ExternalLogin.php:44-81).

ExternalDashboard memeriksa session itu di mount, dan menolak sesi yang lebih tua dari SESSION_LIFETIME_MINUTES = 720 menit sebelum mengalihkan kembali ke route login (app/Livewire/ExternalDashboard.php:17-22, app/Livewire/ExternalDashboard.php:84-110). Token dan password dibuat oleh ExternalAccess::generateForProject dengan Str::random(32) untuk token dan Str::random(8) untuk password (app/Models/ExternalAccess.php:36-47); kolom password memakai cast encrypted (app/Models/ExternalAccess.php:25-29).

⚠️ Kedua route eksternal tidak memuat middleware autentikasi apa pun. Seluruh proteksi bergantung pada mount komponen dan kunci session, dan login dibatasi RateLimiter lima percobaan per 300 detik per token dan IP (app/Livewire/ExternalLogin.php:44-81). Detail perilaku portal ada di Portal Eksternal Klien.

Endpoint yang dipetakan di routes/web.php

MethodPathHandlerJenis
GET/closure view('welcome')halaman statis
GETauth/googleGoogleController::redirectToGoogleautentikasi
GETauth/google/callbackGoogleController::handleGoogleCallbackautentikasi
GETexternal/{token}ExternalLoginLivewire
GETexternal/{token}/dashboardExternalDashboardLivewire

Route halaman depan dan Google berada di bagian atas berkas (routes/web.php:8-14), sedangkan pasangan route eksternal dibungkus Route::prefix('external')->name('external.') (routes/web.php:17-20). Panel admin tidak muncul di berkas ini karena AdminPanelProvider mendaftarkan path admin sendiri (app/Providers/Filament/AdminPanelProvider.php:25-43).

Layer pendukung

AppServiceProvider mengisi dua peran yang tidak ditangani provider lain. Pertama, komponen Livewire edit-comment-modal diikat manual ke EditCommentModal::class. Kedua, FilamentShield::buildPermissionKeyUsing mengganti cara permission key dibentuk untuk tiga jenis entitas: Resource, Page (page_ + basename), dan Widget (widget_ + basename) (app/Providers/AppServiceProvider.php:27-54). Bentuk konkret permission key dibahas di Kontrol Akses dan Peran.

EventServiceProvider memetakan dua event secara eksplisit: ProjectMemberAttached ke SendProjectAssignmentNotification, dan ProjectMemberDetached ke SendProjectRemovalNotification (app/Providers/EventServiceProvider.php:18-25). Karena shouldDiscoverEvents() mengembalikan false, tidak ada listener lain yang ditemukan otomatis (app/Providers/EventServiceProvider.php:38-41).

NotificationService adalah satu-satunya kelas di app/Services. notifyCommentAdded membuat baris Notification bertipe comment_added untuk setiap pengguna sasaran, dengan payload ticket_id, comment_id, commenter_id, dan commenter_name (app/Services/NotificationService.php:18-39). Daftar sasaran dirakit getUsersToNotifyForComment dari tiga sumber, yaitu pembuat ticket, assignee ticket selain komentator, dan komentator lain pada ticket yang sama, lalu dirapikan dengan unique('id') (app/Services/NotificationService.php:64-84).

KomponenPeran dalam arsitekturRujukan
AdminPanelProvidermendaftarkan panel admin, middleware, plugin, dan temaapp/Providers/Filament/AdminPanelProvider.php:25-43
FilamentUserSettingsmenerapkan warna primer dan gaya navigasi per penggunaapp/Http/Middleware/FilamentUserSettings.php:15-29
AppServiceProviderbinding komponen Livewire dan format permission keyapp/Providers/AppServiceProvider.php:27-54
EventServiceProviderpemetaan event ke listener, tanpa auto-discoveryapp/Providers/EventServiceProvider.php:18-25
NotificationServicepembuatan notifikasi dari komentar ticketapp/Services/NotificationService.php:18-39
TicketPolicyizin aksi yang digabung dengan cakupan dataapp/Policies/TicketPolicy.php:22-39

Model domain, relasi antar tabel, dan kolomnya dijelaskan di Model Data dan Migration. Urutan pemasangan provider, panel, dan container ada di Instalasi, Konfigurasi, dan Docker.