config/panda-panel.php
Halaman ini menjelaskan setiap key yang dibaca package, keputusan apa yang dipengaruhinya, dan apa yang terjadi jika nilainya tidak valid.
Ini adalah seluruh permukaan konfigurasi config/panda-panel.php: delapan kelompok top-level yang pada dasarnya berisi registration switch, security boundary, dan path frontend.
Semua konfigurasi yang mengandung logic — path Panel, domain, middleware, navigation, branding, access — ditulis sebagai code pada object Panel, karena keputusan yang memiliki kondisi tidak cocok disimpan sebagai array config.
Contoh minimal yang berfungsi
composer require chocoalano/panel
php artisan vendor:publish --tag=panda-panel-config2
// config/panda-panel.php
return [
'panels' => [
App\Panels\Admin\AdminPanelProvider::class,
],
];2
3
4
5
6
7
Itulah satu-satunya key yang wajib diisi application.
PandaPanel\PandaPanelServiceProvider::register() memanggil mergeConfigFrom(), sehingga semua key lain sudah memiliki default package meskipun file config belum dipublish.
Provider yang tidak tercantum di panels tidak memiliki route. Ini merupakan salah satu penyebab paling umum kondisi "install berhasil tetapi URL Panel 404".
php artisan panel:install mempublish file ini dan menambahkan provider untuk Anda. Lihat Publish Tags dan Running panel:install.
Semua config key
| Key | Type | Default | Dibaca oleh |
|---|---|---|---|
panels | list<class-string<PanelProvider>> | [] | PandaPanelServiceProvider::configuredPanels() |
register_routes | bool | true | PandaPanelServiceProvider::registerRoutes() |
register_web_middleware | bool | true | PandaPanelServiceProvider::registerMiddleware() |
register_guest_redirect | bool | true | PandaPanelServiceProvider::registerGuestRedirect() |
home_redirect.enabled | bool | true | PandaPanel\Support\PanelHomeRedirect |
home_redirect.paths | list<string> | ['dashboard'] | PandaPanel\Support\PanelHomeRedirect |
load_migrations | bool | true | PandaPanelServiceProvider::registerMigrations() |
integrations.allowed_hosts | list<string> | [] | PandaPanel\Integrations\OutboundUrl |
integrations.block_private_networks | bool | true | PandaPanel\Integrations\OutboundUrl |
integrations.history.enabled | bool | true | PandaPanel\Integrations\PanelIntegrationDelivery::enabled() |
integrations.history.keep_per_integration | int | 50 | PanelIntegrationDelivery::prune() |
integrations.history.retention_days | int | 30 | PanelIntegrationDelivery::prune() |
frontend.panel_path | string | 'js/panel' | PandaPanel\Support\FrontendPaths::panel() |
frontend.pages_path | string | 'js/pages/Panels' | PandaPanel\Support\FrontendPaths::pages() |
Tidak ada key di file ini yang secara default terhubung langsung ke env().
Package tidak menyediakan environment variable sendiri. Lihat Environment Variables untuk konfigurasi Laravel yang tetap memengaruhi Panel.
panels
'panels' => [
App\Panels\Admin\AdminPanelProvider::class,
App\Panels\App\AppPanelProvider::class,
],2
3
4
Panel didaftarkan secara eksplisit, bukan melalui discovery.
Tujuannya agar seluruh Panel yang dimiliki application dapat dilihat di satu tempat, dan menambah Panel merupakan perubahan yang disengaja, bukan side effect karena sebuah class kebetulan berada di directory.
Class di dalam Panel seperti Resource, Page, dan Widget tetap dapat ditemukan melalui discovery. Lihat Discovery.
Ada lima rule saat daftar ini dibaca:
| Kondisi | Hasil |
|---|---|
| Value bukan array | Dianggap []; tidak ada Panel yang diregistrasikan. |
Entry bukan class-string subclass PandaPanel\Core\PanelProvider | Dilewati secara silent. |
| Provider yang sama tercantum dua kali | Diregistrasikan satu kali karena registry di-key berdasarkan Panel id. |
| Dua provider berbeda menghasilkan Panel id sama | PanelRegistrationException::duplicatePanelId(). |
| Dua Panel memiliki path dan domain yang sama | PanelRegistrationException::duplicatePanelPath() karena salah satunya akan men-shadow yang lain. |
Daftar dibaca sesuai urutan file, tetapi PanelRegistry::all() mengembalikan Panel terurut berdasarkan id.
Urutan tersebut membuat route registration deterministic dan juga digunakan oleh firstAccessibleTo(). Jadi Panel admin dipertimbangkan sebelum app tanpa bergantung pada posisi masing-masing di file config.
Entry yang tidak dapat di-resolve sengaja dilewati, bukan membuat application fatal saat boot. Fatal error pada tahap ini terjadi sebelum route apa pun tersedia, termasuk route yang dapat membantu menjelaskan masalah tersebut.
php artisan panel:cache mencetak jumlah Panel yang berhasil dicache, sehingga provider yang terlewati akan terlihat dari count yang lebih sedikit dari ekspektasi.
use PandaPanel\Facades\PandaPanel;
PandaPanel::all(); // list<Panel>, sorted by id
PandaPanel::has('admin'); // bool
PandaPanel::get('admin'); // Panel, throws PanelRegistrationException when not registered2
3
4
5
register_routes
'register_routes' => true,Ketika aktif, framework mendaftarkan satu route group per Panel saat boot menggunakan:
- path;
- domain;
- middleware;
yang dideklarasikan oleh Panel.
Set ke false jika application ingin mendaftarkan route Panel sendiri, misalnya pada test harness yang membutuhkan registries tanpa HTTP routes.
Registry tetap dibangun. Jadi:
PandaPanel::resources('admin')tetap dapat digunakan meskipun tidak ada route Panel.
Lihat Route Registration.
register_web_middleware
'register_web_middleware' => true,Ketika aktif, package menambahkan empat middleware ke seluruh group web:
ResetPanelContext
RedirectPanelHome
ShareFlashToast
SharePanelData2
3
4
Set ke false jika Anda ingin menempatkannya sendiri di bootstrap/app.php, misalnya untuk mengontrol posisi middleware secara presisi.
Empat middleware alias tetap diregistrasikan walaupun key ini dimatikan.
Lihat Middleware Registration.
register_guest_redirect
'register_guest_redirect' => true,Guest yang membuka URL Panel diarahkan ke Login Page milik Panel tersebut jika Panel memanggil login(). Jika Panel tidak memiliki Login Page sendiri, behavior fallback tetap menggunakan route('login') milik application seperti default Laravel.
Jadi feature ini menambahkan satu case khusus Panel, bukan mengganti seluruh behavior guest redirect.
Set ke false jika bootstrap/app.php application sudah menggunakan redirectGuestsTo() sendiri; jika keduanya aktif, custom behavior application dapat saling menimpa tergantung registration order.
Lihat Guest Redirect.
home_redirect
'home_redirect' => [
'enabled' => true,
'paths' => ['dashboard'],
],2
3
4
5
Signed-in user yang membuka salah satu path ini diarahkan ke Panel pertama yang boleh mereka akses.
Application tetap memiliki route, route name, dan Page component aslinya. Middleware hanya menjawab request lebih awal.
paths berisi pattern Request::is(), sehingga:
'reports/*'dapat menyerahkan seluruh section.
Jika pattern mencakup path yang merupakan root sebuah Panel, root tersebut diabaikan agar tidak terjadi redirect loop.
Entry yang bukan non-empty string dibuang.
Feature dimatikan jika:
pathskosong; atauenabledbukan booleantrue.
Lihat Home Redirect.
load_migrations
'load_migrations' => true,Memuat migration langsung dari package.
Fresh installation dapat menjalankan:
php artisan migratetanpa harus mempublish migration terlebih dahulu.
Migration melakukan guard sebelum mengubah schema sehingga table/column yang sudah tersedia di application tidak diubah secara duplicate.
Jika Anda ingin application memiliki file migration sendiri:
php artisan vendor:publish --tag=panda-panel-migrationskemudian matikan:
'load_migrations' => false,Lihat Migration Loading.
integrations
Integration memungkinkan administrator mengkonfigurasi outbound HTTP request yang dipicu saat Resource mengalami write.
Karena destination URL diisi melalui form dan request dijalankan server, feature ini secara desain memiliki permukaan risiko Server-Side Request Forgery (SSRF).
Karena itu ada dua security gate, dan URL harus lolos keduanya.
'integrations' => [
'allowed_hosts' => [
// 'api.example.com',
// '*.partner.io',
],
'block_private_networks' => true,
'history' => [
'enabled' => true,
'keep_per_integration' => 50,
'retention_days' => 30,
],
],2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| Key | Behavior |
|---|---|
allowed_hosts | Pattern Str::is() dibandingkan terhadap host URL secara case-insensitive. Empty list berarti tidak ada host yang boleh diakses — deny by default. Entry non-string dan empty dibuang. |
block_private_networks | Menolak host yang tidak dapat di-resolve ke public A/AAAA address, atau yang mengarah ke private, loopback, carrier-grade NAT, dan link-local network. Value selain boolean true mematikan protection ini. |
history.enabled | Menentukan apakah setiap delivery attempt ditulis ke history. |
history.keep_per_integration | Hard limit jumlah history row per integration. Minimum 1 melalui max(1, (int) …). |
history.retention_days | Retention window dalam hari. Minimum 0; nilai 0 hanya mempertahankan hard cap jumlah row. |
Pattern:
*.partner.iomencakup subdomain tanpa perlu mendaftarkan satu per satu dan tidak mencakup:
partner.io.attacker.testkarena pattern ter-anchored pada kedua ujung.
Dengan block_private_networks = true, range berikut ditolak:
0.0.0.0/8
10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.168.0.0/16
100.64.0.0/10
::1/128
fc00::/7
fe80::/102
3
4
5
6
7
8
9
10
Alamat 169.254.169.254 sangat penting karena pada banyak cloud provider alamat tersebut merupakan metadata endpoint yang dapat mengembalikan IAM credential.
IPv4-mapped IPv6 literal dinormalisasi kembali ke IPv4 sebelum range check dijalankan.
Validation dilakukan:
- saat integration disimpan;
- sekali lagi tepat sebelum setiap request outbound.
Ini penting karena DNS host yang aman minggu lalu dapat me-resolve ke address berbeda hari ini.
Integration delivery tidak mengikuti HTTP redirect. Redirect target harus menjadi URL tersendiri yang juga lolos allowlist.
Sebaiknya pertahankan block_private_networks = true. Protection inilah yang membuat allowlist yang lebih longgar tetap memiliki boundary kedua.
use PandaPanel\Integrations\OutboundUrl;
OutboundUrl::isAllowed('https://api.example.com/hooks'); // bool
OutboundUrl::rejection('http://169.254.169.254/latest'); // the sentence shown to the user, or null2
3
4
Retention history diterapkan langsung setelah setiap delivery. Anda tidak perlu menjadwalkan pruning task terpisah.
Body response/request yang disimpan dipotong panjangnya. Header tidak disimpan karena header biasanya membawa API key atau credential.
Integrations default-nya tidak aktif pada setiap Resource. Resource harus opt-in menggunakan:
integrations()->isEnabled(true)Application yang tidak mengaktifkan integrations pada Resource mana pun tidak memiliki surface tambahan yang perlu dimatikan.
Lihat Resource API.
frontend
'frontend' => [
'panel_path' => 'js/panel',
'pages_path' => 'js/pages/Panels',
],2
3
4
panel_path menentukan destination komponen Vue package ketika menjalankan:
php artisan vendor:publish --tag=panda-panel-assetspages_path menentukan tempat generator menulis application-specific Panel component.
Keduanya relatif terhadap resources/.
Value yang bukan non-empty string fallback ke default.
Lihat Frontend Paths.
Membaca config key
config('panda-panel.register_routes'); // true
config('panda-panel.home_redirect.paths'); // ['dashboard']
config('panda-panel.integrations.allowed_hosts'); // []2
3
Di test, ordinary Config::set() dapat digunakan untuk value yang memang dibaca per request, misalnya:
- Home Redirect;
- integration gate;
- frontend path.
use Illuminate\Support\Facades\Config;
Config::set('panda-panel.home_redirect.enabled', false);
$this->actingAs($admin)->get('/dashboard')->assertOk();2
3
4
5
Namun registration switch dibaca satu kali ketika application boot. Mengubah switch tersebut setelah application selesai boot tidak mengubah route, middleware, atau migration registration yang sudah terjadi.
Hal yang perlu diperhatikan
Boolean menggunakan comparison
=== true.register_routes,register_web_middleware,register_guest_redirect,load_migrations,home_redirect.enabled, danintegrations.block_private_networkshanya aktif jika value benar-benar booleantrue. String'true'dari environment variable tetap dianggap bukantrue.mergeConfigFrom()menggunakan shallowarray_merge. Jika published config hanya mendefinisikan:php'integrations' => [ 'allowed_hosts' => [...], ]1
2
3seluruh array
integrationsdefault package akan terganti danhistorytidak lagi terlihat di file config. Reading code tetap memiliki default sendiri sehingga behavior dapat tetap sama, tetapi file tidak lagi mendokumentasikan konfigurasi efektif secara lengkap. Sebaiknya publish dan pertahankan seluruh structure.config:cachemenggunakanvar_export(). Seluruh value di file ini harus berupa scalar, array, atau class-name string. Jangan meletakkan Closure di config. Lihat Config Cache.config:cachetetap menyertakan default package. Application di-boot sebelum config diserialisasi, sehinggamergeConfigFrom()sudah berjalan meskipun file package belum dipublish.Panel yang baru ditambahkan ke
panelstetap membutuhkan discovery/cache class list. Di production jalankanphp artisan panel:cachesetelah perubahan agar Resource/Page/Widget baru masuk manifest. Lihat Caching.File ini bukan tempat mengkonfigurasi behavior satu Panel. Path, domain, middleware, branding, authorization, navigation, dan search berada pada object
Panel. Lihat Panel Config.