diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 65913d7..71463c5 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -13,6 +13,16 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Create Sentry Release + uses: getsentry/action-release@v1 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + with: + environment: production + version: ${{ gitea.sha }} + - name: Debug Environment run: | echo "User: $(whoami)" diff --git a/GEMINI.md b/GEMINI.md index 21e1b5f..cf8c4ff 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -4,11 +4,11 @@ **Growup Pro** is a whitelabel implementation of [Krayin Laravel CRM](https://github.com/krayin/laravel-crm) with multi-tenancy support. The platform allows multiple companies/organizations to use the same CRM instance with complete data isolation through subdomain-based tenancy. -**CURRENT STATUS: DEBUGGING PHASE** -- **Multi-tenancy implementation**: Installed but encountering issues. -- **Issue 1**: `localhost` not recognized as central domain (TenantCouldNotBeIdentifiedOnDomainException). -- **Issue 2**: Tenant instances (`tenant1.localhost`) load but missing CSS (Asset 404). -- **Goal**: Fix central domain detection and static asset serving for tenants. +**CURRENT STATUS: FEATURE IMPLEMENTATION PHASE** +- **Multi-tenancy implementation**: Stable. +- **Authentication**: Stable (Infinite redirect loop resolved). +- **Core Systems**: Localhost and central domain detection working. +- **Goal**: Implement advanced admin features, Sentry integration, and finalize branding. ### Key Differentiators from Krayin - **Rebranding**: Complete visual overhaul with Growup Pro branding @@ -326,350 +326,15 @@ Route::post('/register-tenant', [TenantController::class, 'register']); - `resources/lang/pt_BR/` - Portuguese translations - `resources/views/layouts/app.blade.php` - Main layout (branded) -## CRITICAL: Krayin Authentication Issues (MUST RESOLVE BEFORE MULTI-TENANCY) +## Resolved Issues (History) -**IMPORTANT:** Multi-tenancy implementation is ON HOLD until we stabilize Krayin's base authentication system. We have experienced critical login issues that must be resolved first. +### Authentication Stabilization +Previous critical issues with infinite redirect loops, CSRF token mismatches (419), and session persistence have been resolved. The authentication system is now stable across local, staging, and production environments. -### Known Authentication Problems - -#### Problem 1: Error 419 - CSRF Token Mismatch -**Symptoms:** -- User enters credentials -- Form submission returns 419 error -- Login fails even with correct credentials - -**Possible Causes:** -- Session driver misconfiguration (`file` vs `redis` vs `database`) -- Session cookie not being set properly -- Domain/subdomain mismatch in session configuration -- `APP_URL` mismatch with actual access URL -- CSRF token expiration (session timeout too short) -- Reverse proxy stripping session cookies - -**Debug Steps:** -```bash -# Check current session driver -grep SESSION_DRIVER .env - -# Clear all caches -php artisan cache:clear -php artisan config:clear -php artisan route:clear -php artisan view:clear - -# Check session files (if using file driver) -ls -la storage/framework/sessions/ - -# Test session configuration -php artisan tinker ->>> session()->put('test', 'value'); ->>> session()->get('test'); # Should return 'value' -``` - -**Potential Fixes:** -1. **Ensure APP_URL matches access URL exactly:** - ```env - # If accessing via localhost:8000 - APP_URL=http://localhost:8000 - - # If accessing via IP - APP_URL=http://192.168.1.100:8000 - ``` - -2. **Check session configuration** (`config/session.php`): - ```php - 'domain' => env('SESSION_DOMAIN', null), // Should match your domain - 'secure' => env('SESSION_SECURE_COOKIE', false), // false for HTTP, true for HTTPS - 'same_site' => 'lax', // Not 'strict' - can cause issues - ``` - -3. **Verify CSRF middleware** is present in `app/Http/Kernel.php`: - ```php - protected $middlewareGroups = [ - 'web' => [ - \App\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\VerifyCsrfToken::class, // THIS IS CRITICAL - // ... - ], - ]; - ``` - -4. **Check CSRF exceptions** (if login route needs to be excluded): - ```php - // app/Http/Middleware/VerifyCsrfToken.php - protected $except = [ - // Add routes here ONLY as last resort - ]; - ``` - -#### Problem 2: Tenant Database Connection Issues -**Symptoms:** -- Login attempt fails silently -- Database connection errors in logs -- Can't authenticate against tenant-specific users table - -**Possible Causes:** -- Tenant database doesn't exist yet -- Database migrations not run on tenant database -- Wrong database being queried (central vs tenant) -- Tenant context not initialized before authentication attempt - -**Debug Steps:** -```bash -# List all databases -php artisan tinker ->>> DB::select('SHOW DATABASES'); - -# Check which database Laravel is connected to ->>> DB::connection()->getDatabaseName(); - -# Verify users table exists in tenant DB ->>> Schema::connection('tenant')->hasTable('users'); -``` - -**Critical Notes for Multi-tenancy:** -- Authentication MUST work in single-tenant (non-multi-tenant) mode FIRST -- Only after stable authentication should we add tenant context switching -- Tenant middleware should NOT interfere with session/CSRF handling - -#### Problem 3: Infinite Login Redirect Loop (CRITICAL - UNRESOLVED) -**Symptoms:** -- User enters valid credentials -- Form submits successfully (no 419 error) -- Page redirects back to login page -- User is never authenticated -- Dashboard never loads -- No error messages displayed - -**This is the MOST CRITICAL issue and must be debugged thoroughly.** - -**Possible Causes:** -1. **Session not persisting after authentication:** - - Session written but immediately destroyed - - Session driver issue (file permissions, Redis connection) - - Session ID regeneration failing - -2. **Authentication middleware misconfigured:** - - `Auth` middleware blocking authenticated users - - Wrong guard being checked - - Middleware order issues in Kernel.php - -3. **Redirect logic broken:** - - `redirectTo` property in LoginController pointing to login route - - Intended destination not being preserved - - Route name/path mismatch - -4. **Auth guard misconfigured:** - - Wrong guard specified in `config/auth.php` - - Provider not finding users correctly - - User model relationship issues - -5. **Cookie domain/path mismatch:** - - Session cookie being set but not read back - - Domain attribute too restrictive - - Path attribute incorrect - -**Detailed Debug Steps:** - -```bash -# 1. Enable query logging to see what's happening -# Add to AppServiceProvider boot(): -DB::listen(function ($query) { - Log::info('Query: ' . $query->sql . ' - Bindings: ' . json_encode($query->bindings)); -}); - -# 2. Add extensive logging to login process -# In LoginController or wherever authentication happens: -Log::info('Login attempt for: ' . request()->email); -Log::info('Session ID before auth: ' . session()->getId()); - -# After Auth::attempt(): -Log::info('Auth attempt result: ' . (Auth::check() ? 'success' : 'failed')); -Log::info('Session ID after auth: ' . session()->getId()); -Log::info('Authenticated user: ' . (Auth::user() ? Auth::user()->id : 'none')); - -# 3. Check session files directly -# If using file driver: -tail -f storage/logs/laravel.log & -# Make login attempt -# Check if session file is created and contains auth data: -cat storage/framework/sessions/* - -# 4. Test authentication in Tinker -php artisan tinker ->>> $user = User::first(); ->>> Auth::login($user); ->>> Auth::check(); # Should return true ->>> session()->all(); # Check session data ->>> session()->save(); # Manually save session -``` - -**Configuration Files to Check:** - -1. **config/auth.php** - ```php - 'defaults' => [ - 'guard' => 'web', // Verify this matches your usage - 'passwords' => 'users', - ], - - 'guards' => [ - 'web' => [ - 'driver' => 'session', - 'provider' => 'users', - ], - ], - - 'providers' => [ - 'users' => [ - 'driver' => 'eloquent', - 'model' => App\Models\User::class, // Verify model path - ], - ], - ``` - -2. **config/session.php** - ```php - 'driver' => env('SESSION_DRIVER', 'file'), - 'lifetime' => env('SESSION_LIFETIME', 120), - 'expire_on_close' => false, - 'encrypt' => false, - 'files' => storage_path('framework/sessions'), - 'connection' => env('SESSION_CONNECTION', null), - 'table' => 'sessions', - 'store' => env('SESSION_STORE', null), - 'lottery' => [2, 100], - 'cookie' => env('SESSION_COOKIE', Str::slug(env('APP_NAME', 'laravel'), '_').'_session'), - 'path' => '/', - 'domain' => env('SESSION_DOMAIN', null), // Should be NULL for localhost - 'secure' => env('SESSION_SECURE_COOKIE', false), // FALSE for HTTP - 'http_only' => true, - 'same_site' => 'lax', // NOT 'strict' - ``` - -3. **Verify LoginController redirect logic:** - ```php - // Typical Laravel LoginController - protected $redirectTo = '/dashboard'; // NOT '/login'! - - // Or method: - protected function redirectTo() { - return '/dashboard'; // Verify this route exists and is accessible - } - - // Check authenticated() method isn't causing issues: - protected function authenticated(Request $request, $user) { - // Should NOT return redirect to login - Log::info('User authenticated: ' . $user->id); - } - ``` - -4. **Check middleware in routes:** - ```php - // routes/web.php - Route::middleware(['auth'])->group(function () { - Route::get('/dashboard', [DashboardController::class, 'index']); - }); - - // Verify 'auth' middleware is defined in Kernel.php - // and uses the correct guard - ``` - -**Testing Methodology:** - -1. **Isolate the problem:** - ```bash - # Create a minimal test route - Route::get('/test-auth', function () { - Auth::login(User::first()); - return [ - 'authenticated' => Auth::check(), - 'user_id' => Auth::id(), - 'session_id' => session()->getId(), - 'session_data' => session()->all(), - ]; - }); - ``` - -2. **Test session persistence separately:** - ```bash - Route::get('/test-session-write', function () { - session(['test_key' => 'test_value_' . time()]); - return 'Session written: ' . session('test_key'); - }); - - Route::get('/test-session-read', function () { - return 'Session read: ' . session('test_key', 'NOT FOUND'); - }); - ``` - -3. **Test authentication without redirect:** - ```bash - Route::post('/test-login', function (Request $request) { - $credentials = $request->only('email', 'password'); - - if (Auth::attempt($credentials)) { - return [ - 'status' => 'success', - 'user' => Auth::user()->only(['id', 'email']), - 'session_id' => session()->getId(), - ]; - } - - return ['status' => 'failed']; - }); - ``` - -**Critical Checklist Before Multi-tenancy:** - -- [ ] Session driver works correctly (can write and read session data) -- [ ] CSRF protection works (no 419 errors) -- [ ] Auth::attempt() returns true for valid credentials -- [ ] Auth::check() returns true immediately after Auth::attempt() -- [ ] Session persists across requests (test with simple session()->put/get) -- [ ] User can access protected routes after authentication -- [ ] No infinite redirect loops -- [ ] Login process completes in browser devtools Network tab (check for redirect chains) -- [ ] Session cookie is set and sent back with subsequent requests (check devtools Application/Storage) - -**DO NOT proceed with multi-tenancy until ALL checklist items pass.** - -### Debugging Tools - -```bash -# Enable debug mode -APP_DEBUG=true - -# Tail logs in real-time -tail -f storage/logs/laravel.log - -# Check Redis connection (if using Redis sessions) -redis-cli ping - -# Check MySQL connection -php artisan tinker ->>> DB::connection()->getPdo(); - -# Dump session driver configuration -php artisan tinker ->>> config('session.driver'); ->>> config('session.files'); ->>> config('session.connection'); - -# Test file permissions -ls -la storage/framework/sessions/ -# Should be writable by web server user -``` - -### Next Steps After Resolving Authentication - -1. Document the exact fix that resolved the issue -2. Create test cases to prevent regression -3. Only then begin multi-tenancy implementation -4. Add monitoring/logging to detect authentication issues early +### Multi-tenancy Setup +- **Localhost Detection**: Fixed `TenantCouldNotBeIdentifiedOnDomainException`. +- **Asset Loading**: Fixed 404 errors for tenant static assets. +- **Database Isolation**: Successfully implemented per-tenant databases. --- @@ -883,32 +548,28 @@ CLOUDFLARE_ZONE_ID=... ## Admin Dashboard for Tenant Management -The platform includes a comprehensive admin dashboard for managing tenants and monitoring platform health. +The platform includes a basic admin dashboard for managing tenants. -### Dashboard Features +### Current Implementation (MVP) +- **Route**: `/super-admin` (protected by `super_admin` middleware) +- **Features**: + - Basic Tenant CRUD (Create, Read, Update, Delete) + - Session management (Login/Logout) +- **Tech Stack**: Custom Blade views (`resources/views/super-admin`) + Bootstrap/Tailwind (inherited) -**Tenant Management:** -- View all active tenants with key metrics -- Create/edit/delete tenant accounts -- Suspend or activate tenant access -- View tenant resource usage (storage, bandwidth, users) -- Manage tenant subscriptions and billing +### Pending Features (Roadmap) **Analytics & Monitoring:** - Total active tenants count - Growth metrics (new tenants per month) - Resource utilization per tenant - Most active tenants (by usage) -- Revenue tracking (if applicable) - System health overview -**Tenant Details View:** -- Company information and contact details -- Subdomain and custom domain settings -- User count and activity logs -- Storage usage and database size -- Last login and activity timestamps -- Custom branding settings (logo, colors) +**Enhanced Tenant Management:** +- View tenant resource usage (storage, bandwidth, users) +- Manage tenant subscriptions and billing +- Suspend or activate tenant access **Bulk Operations:** - Bulk tenant creation via CSV import @@ -923,18 +584,11 @@ The platform includes a comprehensive admin dashboard for managing tenants and m - Feature flags management - Maintenance mode per tenant -### Access Control -- Admin dashboard accessible only to super admins -- Route: `/admin` (protected by `SuperAdmin` middleware) -- Audit logs for all admin actions -- Two-factor authentication required - ### Implementation Notes -- Built with Filament Admin Panel (recommended) or custom Blade views -- Real-time updates using Livewire -- Caching for performance (tenant list, metrics) -- API endpoints for programmatic access -- Webhooks for tenant lifecycle events (created, suspended, deleted) +- Currently built with custom Blade views. +- **Future Upgrade**: Consider migrating to Filament Admin Panel for richer UI/UX. +- **Security**: Route protected by `super_admin` middleware. +- **Audit**: Need to implement audit logs for admin actions. ## Notes for Gemini AI Assistant diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index ba3d384..8937ee8 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -3,6 +3,7 @@ namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use Sentry\Laravel\Integration; use Throwable; class Handler extends ExceptionHandler @@ -24,7 +25,7 @@ class Handler extends ExceptionHandler public function register(): void { $this->reportable(function (Throwable $e) { - // + Integration::captureUnhandledException($e); }); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 637878d..b4862fa 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; +use Sentry\State\Scope; class AppServiceProvider extends ServiceProvider { @@ -26,6 +27,18 @@ class AppServiceProvider extends ServiceProvider { Schema::defaultStringLength(191); + if (function_exists('tenant')) { + \Sentry\configureScope(function (Scope $scope): void { + if (tenant('id')) { + $scope->setTag('tenant_id', tenant('id')); + $scope->setContext('tenant', [ + 'id' => tenant('id'), + 'data' => tenant()->toArray(), + ]); + } + }); + } + $this->overrideCoreConfigDefaults(); } diff --git a/composer.json b/composer.json index 18bdb1a..78262b1 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "maatwebsite/excel": "^3.1", "mpdf/mpdf": "^8.2", "prettus/l5-repository": "^2.7.9", + "sentry/sentry-laravel": "*", "smalot/pdfparser": "^2.11", "stancl/tenancy": "^3.8", "webklex/laravel-imap": "^5.3" diff --git a/composer.lock b/composer.lock index ff3f83e..3d6b10c 100644 --- a/composer.lock +++ b/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": "623afca672b2112feb8f9c4a5dc491ce", + "content-hash": "0ffcefe86be77a7b4466dd6ccb5158a2", "packages": [ { "name": "barryvdh/laravel-dompdf", @@ -1923,6 +1923,65 @@ ], "time": "2025-02-03T10:55:03+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0" + }, + "time": "2024-11-18T16:19:46+00:00" + }, { "name": "khaled.alshamaa/ar-php", "version": "v6.3.4", @@ -4134,6 +4193,84 @@ ], "time": "2024-11-21T10:36:35+00:00" }, + { + "name": "nyholm/psr7", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/Nyholm/psr7.git", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "reference": "a71f2b11690f4b24d099d6b16690a90ae14fc6f3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0" + }, + "provide": { + "php-http/message-factory-implementation": "1.0", + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", + "php-http/psr7-integration-tests": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", + "symfony/error-handler": "^4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Nyholm\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + }, + { + "name": "Martijn van der Ven", + "email": "martijn@vanderven.se" + } + ], + "description": "A fast PHP7 implementation of PSR-7", + "homepage": "https://tnyholm.se", + "keywords": [ + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/Nyholm/psr7/issues", + "source": "https://github.com/Nyholm/psr7/tree/1.8.2" + }, + "funding": [ + { + "url": "https://github.com/Zegnat", + "type": "github" + }, + { + "url": "https://github.com/nyholm", + "type": "github" + } + ], + "time": "2024-09-09T07:06:30+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.100", @@ -5395,6 +5532,184 @@ }, "time": "2024-10-27T17:38:32+00:00" }, + { + "name": "sentry/sentry", + "version": "4.19.1", + "source": { + "type": "git", + "url": "https://github.com/getsentry/sentry-php.git", + "reference": "1c21d60bebe67c0122335bd3fe977990435af0a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/1c21d60bebe67c0122335bd3fe977990435af0a3", + "reference": "1c21d60bebe67c0122335bd3fe977990435af0a3", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/psr7": "^1.8.4|^2.1.1", + "jean85/pretty-package-versions": "^1.5|^2.0.4", + "php": "^7.2|^8.0", + "psr/log": "^1.0|^2.0|^3.0", + "symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0|^8.0" + }, + "conflict": { + "raven/raven": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^1.8.4|^2.1.1", + "monolog/monolog": "^1.6|^2.0|^3.0", + "phpbench/phpbench": "^1.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^8.5|^9.6", + "vimeo/psalm": "^4.17" + }, + "suggest": { + "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Sentry\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sentry", + "email": "accounts@sentry.io" + } + ], + "description": "PHP SDK for Sentry (http://sentry.io)", + "homepage": "http://sentry.io", + "keywords": [ + "crash-reporting", + "crash-reports", + "error-handler", + "error-monitoring", + "log", + "logging", + "profiling", + "sentry", + "tracing" + ], + "support": { + "issues": "https://github.com/getsentry/sentry-php/issues", + "source": "https://github.com/getsentry/sentry-php/tree/4.19.1" + }, + "funding": [ + { + "url": "https://sentry.io/", + "type": "custom" + }, + { + "url": "https://sentry.io/pricing/", + "type": "custom" + } + ], + "time": "2025-12-02T15:57:41+00:00" + }, + { + "name": "sentry/sentry-laravel", + "version": "4.20.1", + "source": { + "type": "git", + "url": "https://github.com/getsentry/sentry-laravel.git", + "reference": "503853fa7ee74b34b64e76f1373db86cd11afe72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/503853fa7ee74b34b64e76f1373db86cd11afe72", + "reference": "503853fa7ee74b34b64e76f1373db86cd11afe72", + "shasum": "" + }, + "require": { + "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", + "nyholm/psr7": "^1.0", + "php": "^7.2 | ^8.0", + "sentry/sentry": "^4.19.0", + "symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0 | ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11", + "guzzlehttp/guzzle": "^7.2", + "laravel/folio": "^1.1", + "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", + "laravel/pennant": "^1.0", + "livewire/livewire": "^2.0 | ^3.0", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.4 | ^9.3 | ^10.4 | ^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Sentry": "Sentry\\Laravel\\Facade" + }, + "providers": [ + "Sentry\\Laravel\\ServiceProvider", + "Sentry\\Laravel\\Tracing\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-0": { + "Sentry\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sentry", + "email": "accounts@sentry.io" + } + ], + "description": "Laravel SDK for Sentry (https://sentry.io)", + "homepage": "https://sentry.io", + "keywords": [ + "crash-reporting", + "crash-reports", + "error-handler", + "error-monitoring", + "laravel", + "log", + "logging", + "profiling", + "sentry", + "tracing" + ], + "support": { + "issues": "https://github.com/getsentry/sentry-laravel/issues", + "source": "https://github.com/getsentry/sentry-laravel/tree/4.20.1" + }, + "funding": [ + { + "url": "https://sentry.io/", + "type": "custom" + }, + { + "url": "https://sentry.io/pricing/", + "type": "custom" + } + ], + "time": "2026-01-07T08:53:19+00:00" + }, { "name": "setasign/fpdi", "version": "v2.6.4", @@ -6568,6 +6883,77 @@ ], "time": "2025-02-17T21:23:52+00:00" }, + { + "name": "symfony/options-resolver", + "version": "v8.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-11-12T15:55:31+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.31.0", @@ -7265,6 +7651,94 @@ ], "time": "2025-02-04T13:35:48+00:00" }, + { + "name": "symfony/psr-http-message-bridge", + "version": "v7.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "929ffe10bbfbb92e711ac3818d416f9daffee067" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/929ffe10bbfbb92e711ac3818d416f9daffee067", + "reference": "929ffe10bbfbb92e711ac3818d416f9daffee067", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", + "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", + "symfony/runtime": "^6.4.13|^7.1.6|^8.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "https://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-03T23:30:35+00:00" + }, { "name": "symfony/routing", "version": "v6.4.18", @@ -8708,65 +9182,6 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "jean85/pretty-package-versions", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.1.0", - "php": "^7.4|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.6", - "vimeo/psalm": "^4.3 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0" - }, - "time": "2024-11-18T16:19:46+00:00" - }, { "name": "krayin/krayin-package-generator", "version": "dev-master", diff --git a/config/sentry.php b/config/sentry.php new file mode 100644 index 0000000..abed25b --- /dev/null +++ b/config/sentry.php @@ -0,0 +1,135 @@ + env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')), + + // @see https://spotlightjs.com/ + // 'spotlight' => env('SENTRY_SPOTLIGHT', false), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#logger + // 'logger' => Sentry\Logger\DebugFileLogger::class, // By default this will log to `storage_path('logs/sentry.log')` + + // The release version of your application + // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) + 'release' => env('SENTRY_RELEASE'), + + // When left empty or `null` the Laravel environment will be used (usually discovered from `APP_ENV` in your `.env`) + 'environment' => env('SENTRY_ENVIRONMENT'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#sample_rate + 'sample_rate' => env('SENTRY_SAMPLE_RATE') === null ? 1.0 : (float) env('SENTRY_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces_sample_rate + 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_TRACES_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#profiles-sample-rate + 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float) env('SENTRY_PROFILES_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_logs + 'enable_logs' => env('SENTRY_ENABLE_LOGS', false), + + // The minimum log level that will be sent to Sentry as logs using the `sentry_logs` logging channel + 'logs_channel_level' => env('SENTRY_LOG_LEVEL', env('SENTRY_LOGS_LEVEL', env('LOG_LEVEL', 'debug'))), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send_default_pii + 'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore_exceptions + // 'ignore_exceptions' => [], + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore_transactions + 'ignore_transactions' => [ + // Ignore Laravel's default health URL + '/up', + ], + + // Breadcrumb specific configuration + 'breadcrumbs' => [ + // Capture Laravel logs as breadcrumbs + 'logs' => env('SENTRY_BREADCRUMBS_LOGS_ENABLED', true), + + // Capture Laravel cache events (hits, writes etc.) as breadcrumbs + 'cache' => env('SENTRY_BREADCRUMBS_CACHE_ENABLED', true), + + // Capture Livewire components like routes as breadcrumbs + 'livewire' => env('SENTRY_BREADCRUMBS_LIVEWIRE_ENABLED', true), + + // Capture SQL queries as breadcrumbs + 'sql_queries' => env('SENTRY_BREADCRUMBS_SQL_QUERIES_ENABLED', true), + + // Capture SQL query bindings (parameters) in SQL query breadcrumbs + 'sql_bindings' => env('SENTRY_BREADCRUMBS_SQL_BINDINGS_ENABLED', false), + + // Capture queue job information as breadcrumbs + 'queue_info' => env('SENTRY_BREADCRUMBS_QUEUE_INFO_ENABLED', true), + + // Capture command information as breadcrumbs + 'command_info' => env('SENTRY_BREADCRUMBS_COMMAND_JOBS_ENABLED', true), + + // Capture HTTP client request information as breadcrumbs + 'http_client_requests' => env('SENTRY_BREADCRUMBS_HTTP_CLIENT_REQUESTS_ENABLED', true), + + // Capture send notifications as breadcrumbs + 'notifications' => env('SENTRY_BREADCRUMBS_NOTIFICATIONS_ENABLED', true), + ], + + // Performance monitoring specific configuration + 'tracing' => [ + // Trace queue jobs as their own transactions (this enables tracing for queue jobs) + 'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', true), + + // Capture queue jobs as spans when executed on the sync driver + 'queue_jobs' => env('SENTRY_TRACE_QUEUE_JOBS_ENABLED', true), + + // Capture SQL queries as spans + 'sql_queries' => env('SENTRY_TRACE_SQL_QUERIES_ENABLED', true), + + // Capture SQL query bindings (parameters) in SQL query spans + 'sql_bindings' => env('SENTRY_TRACE_SQL_BINDINGS_ENABLED', false), + + // Capture where the SQL query originated from on the SQL query spans + 'sql_origin' => env('SENTRY_TRACE_SQL_ORIGIN_ENABLED', true), + + // Define a threshold in milliseconds for SQL queries to resolve their origin + 'sql_origin_threshold_ms' => env('SENTRY_TRACE_SQL_ORIGIN_THRESHOLD_MS', 100), + + // Capture views rendered as spans + 'views' => env('SENTRY_TRACE_VIEWS_ENABLED', true), + + // Capture Livewire components as spans + 'livewire' => env('SENTRY_TRACE_LIVEWIRE_ENABLED', true), + + // Capture HTTP client requests as spans + 'http_client_requests' => env('SENTRY_TRACE_HTTP_CLIENT_REQUESTS_ENABLED', true), + + // Capture Laravel cache events (hits, writes etc.) as spans + 'cache' => env('SENTRY_TRACE_CACHE_ENABLED', true), + + // Capture Redis operations as spans (this enables Redis events in Laravel) + 'redis_commands' => env('SENTRY_TRACE_REDIS_COMMANDS', false), + + // Capture where the Redis command originated from on the Redis command spans + 'redis_origin' => env('SENTRY_TRACE_REDIS_ORIGIN_ENABLED', true), + + // Capture send notifications as spans + 'notifications' => env('SENTRY_TRACE_NOTIFICATIONS_ENABLED', true), + + // Enable tracing for requests without a matching route (404's) + 'missing_routes' => env('SENTRY_TRACE_MISSING_ROUTES_ENABLED', false), + + // Configures if the performance trace should continue after the response has been sent to the user until the application terminates + // This is required to capture any spans that are created after the response has been sent like queue jobs dispatched using `dispatch(...)->afterResponse()` for example + 'continue_after_response' => env('SENTRY_TRACE_CONTINUE_AFTER_RESPONSE', true), + + // Enable the tracing integrations supplied by Sentry (recommended) + 'default_integrations' => env('SENTRY_TRACE_DEFAULT_INTEGRATIONS_ENABLED', true), + ], + +];