feat: add app and database modules
This commit is contained in:
@@ -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'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user