feat: add app and database modules

This commit is contained in:
2026-05-21 16:05:11 +07:00
parent 37b7e783f5
commit fad70d096b
212 changed files with 23901 additions and 0 deletions
+327
View File
@@ -0,0 +1,327 @@
<?php
/**
* ============================================================
*
* @project biiproject
*
* @author Andika Debi Putra
*
* @email andikadebiputra@gmail.com
*
* @website https://biiproject.com
*
* @copyright Copyright (c) 2026 Andika Debi Putra
* @license Proprietary - All Rights Reserved
*
* @version 1.0.0
*
* @created 2026-05-01
* ============================================================
*
* Unauthorized copying, modification, distribution, or use
* of this file is strictly prohibited without prior written
* permission from the author.
* ============================================================
*/
namespace App\Providers;
use App\Events\ActivityLogCreated;
use App\Listeners\AnalyzeSystemError;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Observers\PermissionObserver;
use App\Observers\RoleObserver;
use App\Observers\UserObserver;
use App\Services\Monitoring\SystemMonitoringService;
use App\Services\SystemConfig\SystemConfigService;
use Google\Client;
use Google\Service\Drive;
use Illuminate\Auth\Events\Login;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\Request;
use Illuminate\Log\Events\MessageLogged;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
use Masbug\Flysystem\GoogleDriveAdapter;
use Spatie\Activitylog\Models\Activity;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// 🔒 Configure Rate Limiters
$this->configureRateLimiting();
// Super Admin Gate (Developer has full power)
Gate::before(function ($user, $capability) {
// Monitoring dynamic gates
$monitoringKeys = [
'view pulse' => 'engine_pulse_enabled',
'view telescope' => 'engine_telescope_enabled',
'view api docs' => 'engine_swagger_enabled',
'viewHorizon' => 'engine_horizon_enabled',
];
if (isset($monitoringKeys[$capability])) {
if (!get_setting($monitoringKeys[$capability], true)) {
return false; // Force deny if disabled at engine level
}
}
return $user->hasRole('Developer') ? true : null;
});
// Force HTTPS in production
if ($this->app->environment('production')) {
URL::forceScheme('https');
}
// Global Password Reset Expiry Override
if ($this->app->bound(SystemConfigService::class)) {
$expiry = get_setting('password_reset_link_expiry', 60);
config(['auth.passwords.users.expire' => $expiry]);
}
// User Observer
User::observe(UserObserver::class);
// Role Observer
Role::observe(RoleObserver::class);
// Permission Observer
Permission::observe(PermissionObserver::class);
// Windows Backup Compatibility Layer
$this->ensureWindowsBackupCompatibility();
// Google Drive Filesystem Adapter
$this->registerGoogleDriveAdapter();
// S3 Dynamic Config
$this->injectS3DynamicConfig();
// Backup Dynamic Config
$this->injectBackupDynamicConfig();
// Monitoring Dynamic Config
$this->injectMonitoringDynamicConfig();
// Dashboard View Composer
$this->registerDashboardComposer();
// Tab-permission Blade directives
$this->registerTabPermissionDirectives();
// 📡 Real-time Activity Log Broadcasting
Activity::created(function ($activity) {
try {
broadcast(new ActivityLogCreated($activity))->toOthers();
} catch (\Throwable $e) {
// Silently fail if Reverb is not running
}
});
// 🔍 Register AI error analysis listener (not auto-discovered for MessageLogged)
Event::listen(
MessageLogged::class,
AnalyzeSystemError::class
);
}
/**
* Configure the rate limiters for the application.
*/
protected function configureRateLimiting(): void
{
// Rate limiting for API
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
// Rate limiting for Login attempts
RateLimiter::for('login', function (Request $request) {
return Limit::perMinute(5)->by($request->ip())->response(function (Request $request, array $headers) {
return response()->json([
'message' => 'Too many login attempts. Please try again in 1 minute.',
'status' => 'error',
], 429, $headers);
});
});
}
/**
* Inject Monitoring configuration from database.
*/
protected function injectMonitoringDynamicConfig(): void
{
if (!$this->app->bound(SystemConfigService::class)) {
return;
}
config(['pulse.enabled' => get_setting('engine_pulse_enabled', true)]);
config(['telescope.enabled' => get_setting('engine_telescope_enabled', true)]);
if (!get_setting('engine_swagger_enabled', true)) {
config(['l5-swagger.documentations.default.routes.api' => null]);
config(['l5-swagger.defaults.routes.docs' => null]);
}
}
/**
* Register a view composer for the dashboard.
*/
protected function registerDashboardComposer(): void
{
view()->composer('pages.dashboard', function ($view) {
$stats = Cache::remember('dashboard_stats', now()->addMinutes(5), function () {
return [
'total_users' => User::count(),
'active_sessions' => app(SystemMonitoringService::class)->getActiveUsers(),
'today_actions' => Activity::whereDate('created_at', now()->today())->count(),
'failed_jobs' => DB::table('failed_jobs')->count(),
'recent_activities' => Activity::with('causer')->latest()->take(5)->get(),
];
});
$view->with('dashboardStats', $stats);
});
}
/**
* Register Google Drive filesystem adapter.
*/
protected function registerGoogleDriveAdapter(): void
{
Storage::extend('google', function ($app, $config) {
$clientId = get_setting('gdrive_client_id', $config['clientId'] ?? '');
$clientSecret = get_setting('gdrive_client_secret', $config['clientSecret'] ?? '');
$refreshToken = get_setting('gdrive_refresh_token', $config['refreshToken'] ?? '');
$folder = get_setting('gdrive_folder', $config['folder'] ?? 'LaravelBackups');
$client = new Client;
$client->setClientId($clientId);
$client->setClientSecret($clientSecret);
if ($refreshToken) {
$token = $client->refreshToken($refreshToken);
if (!isset($token['error'])) {
$client->setAccessToken($token);
}
}
$service = new Drive($client);
$adapter = new GoogleDriveAdapter($service, $folder, []);
$driver = new Filesystem($adapter);
return new FilesystemAdapter($driver, $adapter, $config);
});
}
/**
* Inject S3 credentials from database if set.
*/
protected function injectS3DynamicConfig(): void
{
if (get_setting('backup_db_driver') === 's3') {
config([
'filesystems.disks.s3.key' => get_setting('s3_key', config('filesystems.disks.s3.key')),
'filesystems.disks.s3.secret' => get_setting('s3_secret', config('filesystems.disks.s3.secret')),
'filesystems.disks.s3.region' => get_setting('s3_region', config('filesystems.disks.s3.region')),
'filesystems.disks.s3.bucket' => get_setting('s3_bucket', config('filesystems.disks.s3.bucket')),
'filesystems.disks.s3.endpoint' => get_setting('s3_endpoint', config('filesystems.disks.s3.endpoint')),
]);
}
}
/**
* Ensures Windows backup compatibility.
*/
protected function ensureWindowsBackupCompatibility(): void
{
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
return;
}
$dbDriver = config('database.default');
if (config("database.connections.{$dbDriver}.dump.dump_binary_path")) {
return;
}
$isPg = $dbDriver === 'pgsql';
$binary = $isPg ? 'pg_dump.exe' : 'mysqldump.exe';
$paths = $isPg ? ['C:\laragon\bin\postgresql\*\bin', 'C:\Program Files\PostgreSQL\*\bin'] : ['C:\laragon\bin\mysql\*\bin', 'C:\xampp\mysql\bin'];
foreach ($paths as $path) {
$matchedPaths = glob($path, GLOB_ONLYDIR);
if (!$matchedPaths) {
continue;
}
$foundPath = $matchedPaths[count($matchedPaths) - 1];
if (file_exists($foundPath . DIRECTORY_SEPARATOR . $binary)) {
config(["database.connections.{$dbDriver}.dump.dump_binary_path" => $foundPath]);
break;
}
}
}
/*
* Register Blade if/unless directives for tab-level permission checks.
*
* Usage in Blade:
* @cantab('global settings', 'login-security') ... @endcantab
* @managetab('mobile settings', 'branding') ... @endmanagetab
*/
protected function registerTabPermissionDirectives(): void
{
Blade::if('cantab', fn (string $menu, string $tab) => can_view_tab($menu, $tab));
Blade::if('managetab', fn (string $menu, string $tab) => can_manage_tab($menu, $tab));
Blade::if('canviewanytab', fn (string $menu) => can_view_any_tab($menu));
Blade::if('canmanageanytab', fn (string $menu) => can_manage_any_tab($menu));
}
/**
* Inject Backup configuration from database.
*/
protected function injectBackupDynamicConfig(): void
{
if (!$this->app->bound(SystemConfigService::class)) {
return;
}
$driver = get_setting('backup_db_driver', 'local');
config(['backup.backup.destination.disks' => [$driver]]);
config(['backup.monitor_backups.0.disks' => [$driver]]);
if ($driver === 'gdrive') {
config([
'filesystems.disks.gdrive.clientId' => get_setting('gdrive_client_id'),
'filesystems.disks.gdrive.clientSecret' => get_setting('gdrive_client_secret'),
'filesystems.disks.gdrive.refreshToken' => get_setting('gdrive_refresh_token'),
'filesystems.disks.gdrive.folder' => get_setting('gdrive_folder', 'LaravelBackups'),
]);
}
}
}
@@ -0,0 +1,61 @@
<?php
namespace App\Providers\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages\Dashboard;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets\AccountWidget;
use Filament\Widgets\FilamentInfoWidget;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
$panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Indigo,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
->pages([
Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
->widgets([
AccountWidget::class,
FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
PreventRequestForgery::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
return $panel;
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function ($user = null) {
return optional($user)->hasRole('Administrator');
});
}
}
@@ -0,0 +1,175 @@
<?php
namespace App\Providers;
use App\Services\MobileConfig\MobileConfigService;
use App\Services\SystemConfig\SystemConfigService;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
class SystemConfigServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$this->app->singleton(SystemConfigService::class);
}
/**
* Bootstrap services.
*/
public function boot(): void
{
// 1. Ensure settings helper is loaded
$helper = app_path('Helpers/SettingsHelper.php');
if (file_exists($helper)) {
require_once $helper;
}
/** @var SystemConfigService $systemConfig */
$systemConfig = $this->app->make(SystemConfigService::class);
// 2. Localization
$locale = $systemConfig->get('default_locale', 'en');
app()->setLocale($locale);
// Sync app.name globally so mail sender name, document title etc. use DB value
config(['app.name' => $systemConfig->get('app_name', config('app.name'))]);
// 3. Performance: Only run config overrides if not in console (optional, but speeds up Artisan)
// or prioritize based on current context
// Apply timezone globally from settings
$timezone = $systemConfig->get('regional_timezone', 'Asia/Jakarta');
config(['app.timezone' => $timezone]);
date_default_timezone_set($timezone);
// HTTPS Enforcement
if ($systemConfig->get('force_https', false)) {
URL::forceScheme('https');
}
// CORS Dynamic Configuration
config([
'cors.allowed_origins' => array_filter(array_map('trim', explode(',', $systemConfig->get('cors_origins', '*')))),
'cors.allowed_methods' => array_filter(array_map('trim', explode(',', $systemConfig->get('cors_methods', '*')))),
'cors.allowed_headers' => array_filter(array_map('trim', explode(',', $systemConfig->get('cors_headers', '*')))),
]);
// Apply Captcha Config
config([
'captcha.sitekey' => $systemConfig->get('captcha_site_key'),
'captcha.secret' => $systemConfig->get('captcha_secret_key'),
'captcha.options' => [
'timeout' => 30,
],
]);
// Unified Social Login Callback Base
$callbackUrl = $systemConfig->get('social_login_callback_url', '/auth/callback');
// Apply Socialite Config
config([
'services.google' => [
'client_id' => $systemConfig->get('google_client_id'),
'client_secret' => $systemConfig->get('google_client_secret'),
'redirect' => url($callbackUrl),
],
'services.facebook' => [
'client_id' => $systemConfig->get('facebook_app_id'),
'client_secret' => $systemConfig->get('facebook_app_secret'),
'redirect' => url($callbackUrl),
],
'services.github' => [
'client_id' => $systemConfig->get('github_client_id'),
'client_secret' => $systemConfig->get('github_client_secret'),
'redirect' => url($callbackUrl),
],
]);
// Apply Session Configuration
config([
'session.driver' => $systemConfig->get('session_driver', 'file'),
'session.lifetime' => $systemConfig->get('session_lifetime', 120),
'session.secure' => $systemConfig->get('session_secure_cookie', false),
'session.encrypt' => $systemConfig->get('session_encrypt', false),
'session.remember' => $systemConfig->get('session_remember_me_duration', 30) * 1440, // Days to Minutes
]);
config(['auth.passwords.users.expire' => $systemConfig->get('password_reset_link_expiry', 60)]);
// 7. DYNAMIC MAIL CONFIGURATION
if ($systemConfig->get('mail_host')) {
config([
'mail.default' => $systemConfig->get('mail_driver', 'smtp'),
'mail.mailers.smtp.host' => $systemConfig->get('mail_host'),
'mail.mailers.smtp.port' => $systemConfig->get('mail_port', 587),
'mail.mailers.smtp.encryption' => $systemConfig->get('mail_encryption', 'tls'),
'mail.mailers.smtp.username' => $systemConfig->get('mail_username'),
'mail.mailers.smtp.password' => $systemConfig->get('mail_password'),
'mail.from.address' => $systemConfig->get('mail_from_address', 'noreply@example.com'),
'mail.from.name' => $systemConfig->get('mail_from_name', config('app.name')),
]);
// If using smtp transport specifically
}
// 8. DYNAMIC BACKUP CONFIGURATION (Spatie Backup)
if ($systemConfig->get('backup_db_enabled')) {
$driver = $systemConfig->get('backup_db_driver', 'local');
config([
'backup.backup.destination.disks' => [$driver],
'backup.monitor_backups.0.disks' => [$driver],
'backup.cleanup.default_strategy.keep_all_backups_for_days' => (int) $systemConfig->get('backup_db_retention', 7),
]);
// Encryption
if ($systemConfig->get('backup_db_encrypt') && $systemConfig->get('backup_db_encrypt_key')) {
config([
'backup.backup.password' => $systemConfig->get('backup_db_encrypt_key'),
'backup.backup.encryption' => 'aes256',
]);
} else {
config([
'backup.backup.password' => null,
'backup.backup.encryption' => 'none',
]);
}
// Exclude Tables (Injected via database config dump key)
if ($excludeTables = $systemConfig->get('backup_db_exclude')) {
$tables = array_map('trim', explode(',', $excludeTables));
$dbDriver = config('database.default');
config(["database.connections.{$dbDriver}.dump.exclude_tables" => $tables]);
}
// Notifications
$notifyTo = $systemConfig->get('backup_db_notify_to');
if ($notifyTo) {
if (filter_var($notifyTo, FILTER_VALIDATE_EMAIL)) {
config(['backup.notifications.mail.to' => $notifyTo]);
} elseif (str_starts_with($notifyTo, 'http')) {
config(['backup.notifications.notifications.webhook.url' => $notifyTo]);
}
}
}
// 9. DYNAMIC MOBILE AUTH CONFIGURATION
$mobileConfig = $this->app->make(MobileConfigService::class);
$allMobile = $mobileConfig->all();
if ($ttl = ($allMobile['security_auth']['token_ttl_minutes'] ?? null)) {
config(['sanctum.expiration' => (int) $ttl]);
}
// 3. View Sharing (Cached for request duration)
view()->share($systemConfig->getPublicSettings());
// 4. Pulse Access Gate
Gate::define('viewPulse', function ($user) {
return $user->can('view pulse');
});
}
}
@@ -0,0 +1,63 @@
<?php
namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// Telescope::night();
$this->hideSensitiveRequestDetails();
$isLocal = $this->app->environment('local');
Telescope::filter(function (IncomingEntry $entry) use ($isLocal) {
return $isLocal ||
$entry->isReportableException() ||
$entry->isFailedRequest() ||
$entry->isFailedJob() ||
$entry->isScheduledTask() ||
$entry->hasMonitoredTag();
});
}
/**
* Prevent sensitive request details from being logged by Telescope.
*/
protected function hideSensitiveRequestDetails(): void
{
if ($this->app->environment('local')) {
return;
}
Telescope::hideRequestParameters(['_token']);
Telescope::hideRequestHeaders([
'cookie',
'x-csrf-token',
'x-xsrf-token',
]);
}
/**
* Register the Telescope gate.
*
* This gate determines who can access Telescope in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewTelescope', function (User $user) {
return $user->can('view telescope');
});
}
}