diff --git a/src/.env.example b/src/.env.example index 8bb101b8..b6c90c98 100644 --- a/src/.env.example +++ b/src/.env.example @@ -103,3 +103,9 @@ ELASTICSEARCH_PASSWORD=example ELASTICSEARCH_HOST=localhost ELASTICSEARCH_PORT=9200 ELASTICSEARCH_SCHEME=http + +LOG_VIEWER_ENABLED=true +LOG_VIEWER_API_ONLY=false +LOG_VIEWER_ROUTE_PATH=log-viewer +LOG_VIEWER_CACHE_DRIVER= +LOG_VIEWER_API_STATEFUL_DOMAINS= diff --git a/src/.env.testing b/src/.env.testing index 39f382c5..f7f80c4a 100644 --- a/src/.env.testing +++ b/src/.env.testing @@ -97,3 +97,15 @@ JWT_SECRET=test SCRAPER_URL=http://test-scraper LOGSTASH_ADDRESS=udp://test-logstash:12201 + +ELASTICSEARCH_USER=test +ELASTICSEARCH_PASSWORD=test +ELASTICSEARCH_HOST=localhost +ELASTICSEARCH_PORT=9200 +ELASTICSEARCH_SCHEME=http + +LOG_VIEWER_ENABLED=true +LOG_VIEWER_API_ONLY=false +LOG_VIEWER_ROUTE_PATH=log-viewer +LOG_VIEWER_CACHE_DRIVER= +LOG_VIEWER_API_STATEFUL_DOMAINS= diff --git a/src/app/Providers/LogViewerServiceProvider.php b/src/app/Providers/LogViewerServiceProvider.php new file mode 100644 index 00000000..19a0e948 --- /dev/null +++ b/src/app/Providers/LogViewerServiceProvider.php @@ -0,0 +1,31 @@ +hasRole(RoleEnum::OWNER); + }); + } +} diff --git a/src/bootstrap/providers.php b/src/bootstrap/providers.php index 7aa245dc..26da283e 100644 --- a/src/bootstrap/providers.php +++ b/src/bootstrap/providers.php @@ -8,6 +8,7 @@ App\Providers\FacadeServiceProvider::class, App\Providers\FakerServiceProvider::class, App\Providers\HorizonServiceProvider::class, + App\Providers\LogViewerServiceProvider::class, App\Providers\MacroServiceProvider::class, App\Providers\RouteServiceProvider::class, ]; diff --git a/src/composer.json b/src/composer.json index 42b9f1e1..36f9e4ea 100644 --- a/src/composer.json +++ b/src/composer.json @@ -23,6 +23,7 @@ "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", "nutgram/laravel": "^1.4", + "opcodesio/log-viewer": "^3.10", "predis/predis": "^2.2", "pusher/pusher-php-server": "^7.2", "spatie/laravel-permission": "^6.7", @@ -69,10 +70,13 @@ "@php artisan package:discover --ansi" ], "post-install-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi", + "@php artisan vendor:publish --tag=log-viewer-assets --ansi", "@ide-helper" ], "post-update-cmd": [ "@php artisan vendor:publish --tag=laravel-assets --ansi", + "@php artisan vendor:publish --tag=log-viewer-assets --ansi", "@ide-helper" ], "post-root-package-install": [ diff --git a/src/composer.lock b/src/composer.lock index 8558a455..8e0deaac 100644 --- a/src/composer.lock +++ b/src/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b9007cdac8b37bf146f989471844b988", + "content-hash": "9bc171d0079892f31afd1e6c32ecf205", "packages": [ { "name": "aws/aws-crt-php", @@ -3525,6 +3525,147 @@ ], "time": "2024-06-19T09:09:33+00:00" }, + { + "name": "opcodesio/log-viewer", + "version": "v3.10.1", + "source": { + "type": "git", + "url": "https://github.com/opcodesio/log-viewer.git", + "reference": "d0d1e9429a0a3a59ac8d525bfb8415b1041b30ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/d0d1e9429a0a3a59ac8d525bfb8415b1041b30ca", + "reference": "d0d1e9429a0a3a59ac8d525bfb8415b1041b30ca", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0", + "opcodesio/mail-parser": "^0.1.6", + "php": "^8.0" + }, + "conflict": { + "arcanedev/log-viewer": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.2", + "itsgoingd/clockwork": "^5.1", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^7.6|^8.0|^9.0", + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-laravel": "^2.0", + "spatie/test-time": "^1.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Required for multi-host support. ^7.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Opcodes\\LogViewer\\LogViewerServiceProvider" + ], + "aliases": { + "LogViewer": "Opcodes\\LogViewer\\Facades\\LogViewer" + } + } + }, + "autoload": { + "psr-4": { + "Opcodes\\LogViewer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arunas Skirius", + "email": "arukomp@gmail.com", + "role": "Developer" + } + ], + "description": "Fast and easy-to-use log viewer for your Laravel application", + "homepage": "https://github.com/opcodesio/log-viewer", + "keywords": [ + "arukompas", + "better-log-viewer", + "laravel", + "log viewer", + "logs", + "opcodesio" + ], + "support": { + "issues": "https://github.com/opcodesio/log-viewer/issues", + "source": "https://github.com/opcodesio/log-viewer/tree/v3.10.1" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/arunas", + "type": "custom" + }, + { + "url": "https://github.com/arukompas", + "type": "github" + } + ], + "time": "2024-05-20T15:11:50+00:00" + }, + { + "name": "opcodesio/mail-parser", + "version": "v0.1.6", + "source": { + "type": "git", + "url": "https://github.com/opcodesio/mail-parser.git", + "reference": "639ef31cbd146a63416283e75afce152e13233ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opcodesio/mail-parser/zipball/639ef31cbd146a63416283e75afce152e13233ea", + "reference": "639ef31cbd146a63416283e75afce152e13233ea", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "pestphp/pest": "^2.16", + "symfony/var-dumper": "^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Opcodes\\MailParser\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Arunas Skirius", + "email": "arukomp@gmail.com", + "role": "Developer" + } + ], + "description": "Parse emails without the mailparse extension", + "keywords": [ + "arukompas", + "email", + "email parser", + "mail", + "opcodesio", + "php" + ], + "support": { + "issues": "https://github.com/opcodesio/mail-parser/issues", + "source": "https://github.com/opcodesio/mail-parser/tree/v0.1.6" + }, + "time": "2023-11-19T08:47:43+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.100", diff --git a/src/config/log-viewer.php b/src/config/log-viewer.php new file mode 100644 index 00000000..6871e411 --- /dev/null +++ b/src/config/log-viewer.php @@ -0,0 +1,235 @@ + env('LOG_VIEWER_ENABLED', true), + + 'api_only' => env('LOG_VIEWER_API_ONLY', false), + + 'require_auth_in_production' => true, + + /* + |-------------------------------------------------------------------------- + | Log Viewer Domain + |-------------------------------------------------------------------------- + | You may change the domain where Log Viewer should be active. + | If the domain is empty, all domains will be valid. + | + */ + + 'route_domain' => null, + + /* + |-------------------------------------------------------------------------- + | Log Viewer Route + |-------------------------------------------------------------------------- + | Log Viewer will be available under this URL. + | + */ + + 'route_path' => env('LOG_VIEWER_ROUTE_PATH', 'log-viewer'), + + /* + |-------------------------------------------------------------------------- + | Back to system URL + |-------------------------------------------------------------------------- + | When set, displays a link to easily get back to this URL. + | Set to `null` to hide this link. + | + | Optional label to display for the above URL. + | + */ + + 'back_to_system_url' => config('app.url'), + + 'back_to_system_label' => null, // Displayed by default: "Back to {{ app.name }}" + + /* + |-------------------------------------------------------------------------- + | Log Viewer time zone. + |-------------------------------------------------------------------------- + | The time zone in which to display the times in the UI. Defaults to + | the application's timezone defined in config/app.php. + | + */ + + 'timezone' => null, + + /* + |-------------------------------------------------------------------------- + | Log Viewer route middleware. + |-------------------------------------------------------------------------- + | Optional middleware to use when loading the initial Log Viewer page. + | + */ + + 'middleware' => [ + 'web', + \Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class, + ], + + /* + |-------------------------------------------------------------------------- + | Log Viewer API middleware. + |-------------------------------------------------------------------------- + | Optional middleware to use on every API request. The same API is also + | used from within the Log Viewer user interface. + | + */ + + 'api_middleware' => [ + \Opcodes\LogViewer\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Opcodes\LogViewer\Http\Middleware\AuthorizeLogViewer::class, + ], + + 'api_stateful_domains' => env('LOG_VIEWER_API_STATEFUL_DOMAINS') ? explode( + ',', + env('LOG_VIEWER_API_STATEFUL_DOMAINS') + ) : null, + + /* + |-------------------------------------------------------------------------- + | Log Viewer Remote hosts. + |-------------------------------------------------------------------------- + | Log Viewer supports viewing Laravel logs from remote hosts. They must + | be running Log Viewer as well. Below you can define the hosts you + | would like to show in this Log Viewer instance. + | + */ + + 'hosts' => [ + 'local' => [ + 'name' => ucfirst(env('APP_ENV', 'anilibrary')), + ], + + // 'staging' => [ + // 'name' => 'Staging', + // 'host' => 'https://staging.example.com/log-viewer', + // 'auth' => [ // Example of HTTP Basic auth + // 'username' => 'username', + // 'password' => 'password', + // ], + // ], + // + // 'production' => [ + // 'name' => 'Production', + // 'host' => 'https://example.com/log-viewer', + // 'auth' => [ // Example of Bearer token auth + // 'token' => env('LOG_VIEWER_PRODUCTION_TOKEN'), + // ], + // 'headers' => [ + // 'X-Foo' => 'Bar', + // ], + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Include file patterns + |-------------------------------------------------------------------------- + | + */ + + 'include_files' => [ + '*.log', + '**/*.log', + + // You can include paths to other log types as well, such as apache, nginx, and more. + '/var/log/httpd/*', + '/var/log/nginx/*', + + // MacOS Apple Silicon logs + '/opt/homebrew/var/log/nginx/*', + '/opt/homebrew/var/log/httpd/*', + '/opt/homebrew/var/log/php-fpm.log', + '/opt/homebrew/var/log/postgres*log', + '/opt/homebrew/var/log/redis*log', + '/opt/homebrew/var/log/supervisor*log', + + // '/absolute/paths/supported', + ], + + /* + |-------------------------------------------------------------------------- + | Exclude file patterns. + |-------------------------------------------------------------------------- + | This will take precedence over included files. + | + */ + + 'exclude_files' => [ + // 'my_secret.log' + ], + + /* + |-------------------------------------------------------------------------- + | Hide unknown files. + |-------------------------------------------------------------------------- + | The include/exclude options above might catch files which are not + | logs supported by Log Viewer. In that case, you can hide them + | from the UI and API calls by setting this to true. + | + */ + + 'hide_unknown_files' => true, + + /* + |-------------------------------------------------------------------------- + | Shorter stack trace filters. + |-------------------------------------------------------------------------- + | Lines containing any of these strings will be excluded from the full log. + | This setting is only active when the function is enabled via the user interface. + | + */ + + 'shorter_stack_trace_excludes' => [ + '/vendor/symfony/', + '/vendor/laravel/framework/', + '/vendor/barryvdh/laravel-debugbar/', + ], + + /* + |-------------------------------------------------------------------------- + | Cache driver + |-------------------------------------------------------------------------- + | Cache driver to use for storing the log indices. Indices are used to speed up + | log navigation. Defaults to your application's default cache driver. + | + */ + + 'cache_driver' => env('LOG_VIEWER_CACHE_DRIVER'), + + /* + |-------------------------------------------------------------------------- + | Cache key prefix + |-------------------------------------------------------------------------- + | Log Viewer prefixes all the cache keys created with this value. If for + | some reason you would like to change this prefix, you can do so here. + | The format of Log Viewer cache keys is: + | {prefix}:{version}:{rest-of-the-key} + | + */ + + 'cache_key_prefix' => 'lv', + + /* + |-------------------------------------------------------------------------- + | Chunk size when scanning log files lazily + |-------------------------------------------------------------------------- + | The size in MB of files to scan before updating the progress bar when searching across all files. + | + */ + + 'lazy_scan_chunk_size_in_mb' => 50, + + 'strip_extracted_context' => true, +]; diff --git a/src/tests/Feature/Horizon/HorizonAccessTest.php b/src/tests/Feature/Horizon/HorizonAccessTest.php deleted file mode 100644 index b9408d5e..00000000 --- a/src/tests/Feature/Horizon/HorizonAccessTest.php +++ /dev/null @@ -1,34 +0,0 @@ -seed(RoleSeeder::class); - } - - public function testHorizonDashboardCannotBeAccessedInProductionEnvironment(): void - { - config(['app.env' => 'production']); - - $user = $this->createAdmin(); - - $this->actingAs($user)->get('/horizon')->assertForbidden(); - } -} diff --git a/src/tests/Feature/Horizon/HorizonTest.php b/src/tests/Feature/Horizon/HorizonTest.php new file mode 100644 index 00000000..4793d89a --- /dev/null +++ b/src/tests/Feature/Horizon/HorizonTest.php @@ -0,0 +1,43 @@ +seed(RoleSeeder::class); + } + + public function testHorizonDashboardCanBeAccessedOnlyByOwner(): void + { + $this->actingAs($this->createUser())->get(route('horizon.index'))->assertForbidden(); + $this->actingAs($this->createAdmin())->get(route('horizon.index'))->assertForbidden(); + + $this->actingAs($this->createOwner())->get(route('horizon.index'))->assertOk(); + } + + public function testHorizonDashboardCanBeAccessedOnlyByOwnerInProductionEnvironment(): void + { + config(['app.env' => 'production']); + + $this->actingAs($this->createUser())->get(route('horizon.index'))->assertForbidden(); + $this->actingAs($this->createAdmin())->get(route('horizon.index'))->assertForbidden(); + + $this->actingAs($this->createOwner())->get(route('horizon.index'))->assertOk(); + } +} diff --git a/src/tests/Feature/LogViewer/LogViewerTest.php b/src/tests/Feature/LogViewer/LogViewerTest.php new file mode 100644 index 00000000..61e71fe1 --- /dev/null +++ b/src/tests/Feature/LogViewer/LogViewerTest.php @@ -0,0 +1,43 @@ +seed(RoleSeeder::class); + } + + public function testLogViewerDashboardCanBeAccessedOnlyByOwner(): void + { + $this->actingAs($this->createUser())->get(route('log-viewer.index'))->assertForbidden(); + $this->actingAs($this->createAdmin())->get(route('log-viewer.index'))->assertForbidden(); + + $this->actingAs($this->createOwner())->get(route('log-viewer.index'))->assertOk(); + } + + public function testLogViewerDashboardCanBeAccessedOnlyByOwnerInProductionEnvironment(): void + { + config(['app.env' => 'production']); + + $this->actingAs($this->createUser())->get(route('log-viewer.index'))->assertForbidden(); + $this->actingAs($this->createAdmin())->get(route('log-viewer.index'))->assertForbidden(); + + $this->actingAs($this->createOwner())->get(route('log-viewer.index'))->assertOk(); + } +}