From f68f34980aa710530fdbe85fe659281548c617e2 Mon Sep 17 00:00:00 2001 From: dyzulk <66510723+dyzulk@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:11:01 +0700 Subject: [PATCH] First commit --- .editorconfig | 18 + .env.example | 112 + .env.production.example | 112 + .gitattributes | 11 + .gitignore | 27 + README.md | 41 + .../Commands/NotifyCertificateExpirations.php | 84 + app/Helpers/UuidHelper.php | 18 + .../Api/Admin/LegalPageController.php | 178 + app/Http/Controllers/Api/ApiKeyController.php | 106 + .../Api/CertificateApiController.php | 241 + .../Controllers/Api/DashboardController.php | 160 + .../Controllers/Api/InquiryController.php | 118 + .../Controllers/Api/LegalPageController.php | 42 + app/Http/Controllers/Api/MailController.php | 64 + .../Api/NotificationController.php | 70 + .../Api/PasswordResetController.php | 96 + .../Controllers/Api/ProfileController.php | 356 + .../Controllers/Api/PublicCaController.php | 171 + .../Controllers/Api/RootCaApiController.php | 69 + app/Http/Controllers/Api/TicketController.php | 241 + .../Controllers/Api/UserApiController.php | 157 + .../Api/VerificationController.php | 62 + app/Http/Controllers/AuthController.php | 377 + app/Http/Controllers/Controller.php | 8 + app/Http/Controllers/NavigationController.php | 161 + app/Http/Controllers/ServiceController.php | 35 + app/Http/Controllers/TwoFactorController.php | 195 + app/Http/Middleware/AdminMiddleware.php | 24 + app/Http/Middleware/CheckApiKey.php | 57 + app/Mail/CertificateExpiredMail.php | 55 + app/Mail/CertificateExpiringMail.php | 57 + app/Mail/InquiryReplyMail.php | 55 + app/Mail/TestMail.php | 57 + app/Models/ActivityLog.php | 36 + app/Models/ApiKey.php | 55 + app/Models/CaCertificate.php | 42 + app/Models/Certificate.php | 82 + app/Models/Inquiry.php | 38 + app/Models/LegalPage.php | 32 + app/Models/LegalPageRevision.php | 35 + app/Models/LoginHistory.php | 41 + app/Models/SocialAccount.php | 40 + app/Models/Ticket.php | 45 + app/Models/TicketAttachment.php | 25 + app/Models/TicketReply.php | 40 + app/Models/User.php | 189 + .../CertificateExpiringNotification.php | 68 + app/Notifications/CertificateNotification.php | 76 + app/Notifications/NewInquiryNotification.php | 82 + app/Notifications/NewTicketNotification.php | 85 + .../PendingEmailVerificationNotification.php | 50 + app/Notifications/TicketReplyNotification.php | 96 + app/Notifications/VerifyEmailNotification.php | 28 + app/Providers/AppServiceProvider.php | 24 + app/Services/OpenSslService.php | 407 + app/Traits/CanTrackLogin.php | 118 + app/Traits/LogsActivity.php | 23 + artisan | 18 + banner.png | Bin 0 -> 166542 bytes bootstrap/app.php | 24 + bootstrap/cache/.gitignore | 2 + bootstrap/providers.php | 5 + composer.json | 97 + composer.lock | 10812 ++++++++++++++++ config/app.php | 128 + config/auth.php | 115 + config/broadcasting.php | 82 + config/cache.php | 117 + config/cors.php | 36 + config/database.php | 183 + config/filesystems.php | 93 + config/logging.php | 132 + config/mail.php | 133 + config/openssl.php | 29 + config/queue.php | 129 + config/reverb.php | 95 + config/sanctum.php | 90 + config/services.php | 55 + config/session.php | 220 + database/.gitignore | 1 + database/factories/UserFactory.php | 48 + .../0001_01_01_000000_create_users_table.php | 78 + .../0001_01_01_000001_create_cache_table.php | 35 + .../0001_01_01_000002_create_jobs_table.php | 57 + ...025_12_23_000000_create_api_keys_table.php | 32 + ...09_create_personal_access_tokens_table.php | 35 + ...12_23_123907_create_certificates_table.php | 43 + ...23_123913_create_ca_certificates_table.php | 39 + ...23_202750_create_login_histories_table.php | 37 + ...25_12_23_234051_create_inquiries_table.php | 34 + ...2_23_235609_create_notifications_table.php | 31 + ...12_24_000001_create_legal_pages_tables.php | 51 + ...025_12_24_001231_create_tickets_tables.php | 43 + ...004303_create_ticket_attachments_table.php | 32 + ...27_080201_create_social_accounts_table.php | 37 + ...2_27_113948_create_activity_logs_table.php | 32 + ...81926_add_pending_email_to_users_table.php | 28 + database/seeders/DatabaseSeeder.php | 51 + .../vendor/cloudflare-turnstile/ar/errors.php | 12 + .../vendor/cloudflare-turnstile/en/errors.php | 12 + package-lock.json | 2573 ++++ package.json | 19 + phpunit.xml | 35 + public/.htaccess | 25 + public/favicon.ico | Bin 0 -> 178553 bytes public/images/logo/auth-logo.png | Bin 0 -> 5108 bytes public/images/logo/auth-logo.svg | 41 + public/images/logo/logo-dark.png | Bin 0 -> 3149 bytes public/images/logo/logo-dark.svg | 41 + public/images/logo/logo-icon.png | Bin 0 -> 1541 bytes public/images/logo/logo-icon.svg | 32 + public/images/logo/logo.png | Bin 0 -> 3316 bytes public/images/logo/logo.svg | 41 + public/index.php | 20 + public/robots.txt | 2 + public/test-api.php | 10 + resources/css/app.css | 11 + resources/js/app.js | 1 + resources/js/bootstrap.js | 12 + resources/js/echo.js | 14 + .../emails/certificate-expired.blade.php | 26 + .../emails/certificate-expiring.blade.php | 31 + .../views/emails/inquiry_reply.blade.php | 84 + .../views/emails/password-reset.blade.php | 90 + resources/views/emails/test.blade.php | 110 + resources/views/emails/verify-email.blade.php | 89 + .../vendor/cloudflare-turnstile/.gitkeep | 0 .../components/scripts.blade.php | 1 + .../components/turnstile.blade.php | 45 + resources/views/welcome.blade.php | 277 + routes/api.php | 126 + routes/channels.php | 7 + routes/console.php | 11 + routes/web.php | 42 + storage/app/.gitignore | 4 + storage/app/private/.gitignore | 2 + storage/app/public/.gitignore | 2 + storage/framework/.gitignore | 9 + storage/framework/cache/.gitignore | 3 + storage/framework/cache/data/.gitignore | 2 + storage/framework/sessions/.gitignore | 2 + storage/framework/testing/.gitignore | 2 + storage/framework/views/.gitignore | 2 + storage/logs/.gitignore | 2 + tests/Feature/ExampleTest.php | 19 + tests/Feature/InquiryNotificationTest.php | 53 + tests/TestCase.php | 10 + tests/Unit/ExampleTest.php | 16 + vite.config.js | 18 + 150 files changed, 22717 insertions(+) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .env.production.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/Console/Commands/NotifyCertificateExpirations.php create mode 100644 app/Helpers/UuidHelper.php create mode 100644 app/Http/Controllers/Api/Admin/LegalPageController.php create mode 100644 app/Http/Controllers/Api/ApiKeyController.php create mode 100644 app/Http/Controllers/Api/CertificateApiController.php create mode 100644 app/Http/Controllers/Api/DashboardController.php create mode 100644 app/Http/Controllers/Api/InquiryController.php create mode 100644 app/Http/Controllers/Api/LegalPageController.php create mode 100644 app/Http/Controllers/Api/MailController.php create mode 100644 app/Http/Controllers/Api/NotificationController.php create mode 100644 app/Http/Controllers/Api/PasswordResetController.php create mode 100644 app/Http/Controllers/Api/ProfileController.php create mode 100644 app/Http/Controllers/Api/PublicCaController.php create mode 100644 app/Http/Controllers/Api/RootCaApiController.php create mode 100644 app/Http/Controllers/Api/TicketController.php create mode 100644 app/Http/Controllers/Api/UserApiController.php create mode 100644 app/Http/Controllers/Api/VerificationController.php create mode 100644 app/Http/Controllers/AuthController.php create mode 100644 app/Http/Controllers/Controller.php create mode 100644 app/Http/Controllers/NavigationController.php create mode 100644 app/Http/Controllers/ServiceController.php create mode 100644 app/Http/Controllers/TwoFactorController.php create mode 100644 app/Http/Middleware/AdminMiddleware.php create mode 100644 app/Http/Middleware/CheckApiKey.php create mode 100644 app/Mail/CertificateExpiredMail.php create mode 100644 app/Mail/CertificateExpiringMail.php create mode 100644 app/Mail/InquiryReplyMail.php create mode 100644 app/Mail/TestMail.php create mode 100644 app/Models/ActivityLog.php create mode 100644 app/Models/ApiKey.php create mode 100644 app/Models/CaCertificate.php create mode 100644 app/Models/Certificate.php create mode 100644 app/Models/Inquiry.php create mode 100644 app/Models/LegalPage.php create mode 100644 app/Models/LegalPageRevision.php create mode 100644 app/Models/LoginHistory.php create mode 100644 app/Models/SocialAccount.php create mode 100644 app/Models/Ticket.php create mode 100644 app/Models/TicketAttachment.php create mode 100644 app/Models/TicketReply.php create mode 100644 app/Models/User.php create mode 100644 app/Notifications/CertificateExpiringNotification.php create mode 100644 app/Notifications/CertificateNotification.php create mode 100644 app/Notifications/NewInquiryNotification.php create mode 100644 app/Notifications/NewTicketNotification.php create mode 100644 app/Notifications/PendingEmailVerificationNotification.php create mode 100644 app/Notifications/TicketReplyNotification.php create mode 100644 app/Notifications/VerifyEmailNotification.php create mode 100644 app/Providers/AppServiceProvider.php create mode 100644 app/Services/OpenSslService.php create mode 100644 app/Traits/CanTrackLogin.php create mode 100644 app/Traits/LogsActivity.php create mode 100644 artisan create mode 100644 banner.png create mode 100644 bootstrap/app.php create mode 100644 bootstrap/cache/.gitignore create mode 100644 bootstrap/providers.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/app.php create mode 100644 config/auth.php create mode 100644 config/broadcasting.php create mode 100644 config/cache.php create mode 100644 config/cors.php create mode 100644 config/database.php create mode 100644 config/filesystems.php create mode 100644 config/logging.php create mode 100644 config/mail.php create mode 100644 config/openssl.php create mode 100644 config/queue.php create mode 100644 config/reverb.php create mode 100644 config/sanctum.php create mode 100644 config/services.php create mode 100644 config/session.php create mode 100644 database/.gitignore create mode 100644 database/factories/UserFactory.php create mode 100644 database/migrations/0001_01_01_000000_create_users_table.php create mode 100644 database/migrations/0001_01_01_000001_create_cache_table.php create mode 100644 database/migrations/0001_01_01_000002_create_jobs_table.php create mode 100644 database/migrations/2025_12_23_000000_create_api_keys_table.php create mode 100644 database/migrations/2025_12_23_061409_create_personal_access_tokens_table.php create mode 100644 database/migrations/2025_12_23_123907_create_certificates_table.php create mode 100644 database/migrations/2025_12_23_123913_create_ca_certificates_table.php create mode 100644 database/migrations/2025_12_23_202750_create_login_histories_table.php create mode 100644 database/migrations/2025_12_23_234051_create_inquiries_table.php create mode 100644 database/migrations/2025_12_23_235609_create_notifications_table.php create mode 100644 database/migrations/2025_12_24_000001_create_legal_pages_tables.php create mode 100644 database/migrations/2025_12_24_001231_create_tickets_tables.php create mode 100644 database/migrations/2025_12_24_004303_create_ticket_attachments_table.php create mode 100644 database/migrations/2025_12_27_080201_create_social_accounts_table.php create mode 100644 database/migrations/2025_12_27_113948_create_activity_logs_table.php create mode 100644 database/migrations/2025_12_30_081926_add_pending_email_to_users_table.php create mode 100644 database/seeders/DatabaseSeeder.php create mode 100644 lang/vendor/cloudflare-turnstile/ar/errors.php create mode 100644 lang/vendor/cloudflare-turnstile/en/errors.php create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 phpunit.xml create mode 100644 public/.htaccess create mode 100644 public/favicon.ico create mode 100644 public/images/logo/auth-logo.png create mode 100644 public/images/logo/auth-logo.svg create mode 100644 public/images/logo/logo-dark.png create mode 100644 public/images/logo/logo-dark.svg create mode 100644 public/images/logo/logo-icon.png create mode 100644 public/images/logo/logo-icon.svg create mode 100644 public/images/logo/logo.png create mode 100644 public/images/logo/logo.svg create mode 100644 public/index.php create mode 100644 public/robots.txt create mode 100644 public/test-api.php create mode 100644 resources/css/app.css create mode 100644 resources/js/app.js create mode 100644 resources/js/bootstrap.js create mode 100644 resources/js/echo.js create mode 100644 resources/views/emails/certificate-expired.blade.php create mode 100644 resources/views/emails/certificate-expiring.blade.php create mode 100644 resources/views/emails/inquiry_reply.blade.php create mode 100644 resources/views/emails/password-reset.blade.php create mode 100644 resources/views/emails/test.blade.php create mode 100644 resources/views/emails/verify-email.blade.php create mode 100644 resources/views/vendor/cloudflare-turnstile/.gitkeep create mode 100644 resources/views/vendor/cloudflare-turnstile/components/scripts.blade.php create mode 100644 resources/views/vendor/cloudflare-turnstile/components/turnstile.blade.php create mode 100644 resources/views/welcome.blade.php create mode 100644 routes/api.php create mode 100644 routes/channels.php create mode 100644 routes/console.php create mode 100644 routes/web.php create mode 100644 storage/app/.gitignore create mode 100644 storage/app/private/.gitignore create mode 100644 storage/app/public/.gitignore create mode 100644 storage/framework/.gitignore create mode 100644 storage/framework/cache/.gitignore create mode 100644 storage/framework/cache/data/.gitignore create mode 100644 storage/framework/sessions/.gitignore create mode 100644 storage/framework/testing/.gitignore create mode 100644 storage/framework/views/.gitignore create mode 100644 storage/logs/.gitignore create mode 100644 tests/Feature/ExampleTest.php create mode 100644 tests/Feature/InquiryNotificationTest.php create mode 100644 tests/TestCase.php create mode 100644 tests/Unit/ExampleTest.php create mode 100644 vite.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a186cd2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[compose.yaml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..11a4e91 --- /dev/null +++ b/.env.example @@ -0,0 +1,112 @@ +APP_NAME=TrustLab +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost:8000 + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=localhost + +SANCTUM_STATEFUL_DOMAINS=localhost:3000,127.0.0.1:3000,trustlab.dyzulk.com,trustlab.pages.dev + +BROADCAST_CONNECTION=reverb +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=lab.dyzulk.com +MAIL_PORT=587 +MAIL_USERNAME=noreply@lab.dyzulk.com +MAIL_PASSWORD= +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="noreply@lab.dyzulk.com" +MAIL_FROM_NAME="${APP_NAME}" + +MAIL_SUPPORT_MAILER=smtp +MAIL_SUPPORT_HOST=lab.dyzulk.com +MAIL_SUPPORT_PORT=587 +MAIL_SUPPORT_USERNAME=support@lab.dyzulk.com +MAIL_SUPPORT_PASSWORD= +MAIL_SUPPORT_ENCRYPTION=tls +MAIL_SUPPORT_FROM_ADDRESS="support@lab.dyzulk.com" +MAIL_SUPPORT_FROM_NAME="${APP_NAME} Support" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +FRONTEND_URL=http://localhost:3000 + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID= +REVERB_APP_KEY= +REVERB_APP_SECRET= +REVERB_HOST="localhost" +REVERB_PORT=8080 +REVERB_SCHEME=http + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" + +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_DEV_CLIENT_ID= +GITHUB_DEV_CLIENT_SECRET= +GITHUB_DEV_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_PROD_CLIENT_ID= +GITHUB_PROD_CLIENT_SECRET= +GITHUB_PROD_REDIRECT_URI=${APP_URL}/api/auth/github/callback + + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=${APP_URL}/api/auth/google/callback + +TURNSTILE_SITE_KEY= +TURNSTILE_SECRET_KEY= \ No newline at end of file diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..b8f2f78 --- /dev/null +++ b/.env.production.example @@ -0,0 +1,112 @@ +APP_NAME=TrustLab +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=https://trustlab-api.dyzulk.com + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=error + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=trustlab +DB_USERNAME=trustlab +DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=.dyzulk.com + +SANCTUM_STATEFUL_DOMAINS=trustlab.dyzulk.com + +BROADCAST_CONNECTION=reverb +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=lab.dyzulk.com +MAIL_PORT=587 +MAIL_USERNAME=noreply@lab.dyzulk.com +MAIL_PASSWORD= +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="noreply@lab.dyzulk.com" +MAIL_FROM_NAME="${APP_NAME}" + +MAIL_SUPPORT_MAILER=smtp +MAIL_SUPPORT_HOST=lab.dyzulk.com +MAIL_SUPPORT_PORT=587 +MAIL_SUPPORT_USERNAME=support@lab.dyzulk.com +MAIL_SUPPORT_PASSWORD= +MAIL_SUPPORT_ENCRYPTION=tls +MAIL_SUPPORT_FROM_ADDRESS="support@lab.dyzulk.com" +MAIL_SUPPORT_FROM_NAME="${APP_NAME} Support" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +FRONTEND_URL=https://trustlab.dyzulk.com + +BROADCAST_CONNECTION=reverb + +REVERB_APP_ID= +REVERB_APP_KEY= +REVERB_APP_SECRET= +REVERB_HOST="trustlab-api.dyzulk.com" +REVERB_PORT=443 +REVERB_SCHEME=https + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" + +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_DEV_CLIENT_ID= +GITHUB_DEV_CLIENT_SECRET= +GITHUB_DEV_REDIRECT_URI=${APP_URL}/api/auth/github/callback + +GITHUB_PROD_CLIENT_ID= +GITHUB_PROD_CLIENT_SECRET= +GITHUB_PROD_REDIRECT_URI=${APP_URL}/api/auth/github/callback + + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI=${APP_URL}/api/auth/google/callback + +TURNSTILE_SITE_KEY= +TURNSTILE_SECRET_KEY= diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ccd425 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +*.log +.DS_Store +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +/.fleet +/.idea +/.nova +/.phpunit.cache +/.vscode +/.zed +/auth.json +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +Homestead.json +Homestead.yaml +Thumbs.db +*.sql +*.sqlite +.env.testing diff --git a/README.md b/README.md new file mode 100644 index 0000000..38f84d7 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# TrustLab API + +![TrustLab Banner](./banner.png) + +**TrustLab API** is the robust backend engine powering the TrustLab ecosystem. Built on Laravel 12, it provides secure authentication, comprehensive role-based access control, and specialized services for CA (Certificate Authority) management and user support. + +## 🚀 Key Features + +### 🔐 Advanced Authentication & Security +- **Multi-Guard Auth**: Powered by Laravel Sanctum for secure SPA (Single Page Application) authentication. +- **OAuth Integration**: "Mirror Callback" system handling Social Login (Google, GitHub) via Laravel Socialite. +- **Role Hierarchy**: + - `Owner` (Supreme): Full control, manages Admins and Customers. + - `Admin`: Manages `Customers` only. Cannot modify Owners. + - `Customer`: Standard user access. +- **Email Verification**: Fully integrated verification flow with rigorous middleware protection (`verified`). +- **Turnstile Protected**: Endpoints designed to work with frontend-only Cloudflare Turnstile gatekeeping. + +### 📜 Core Services +- **Certificate Management**: Logic for handling Certificate Authority operations (CSR, Keys, Signing). +- **Ticket System**: Complete support desk backend with attachment support and admin-user communication channels. +- **User Management**: Administrative endpoints for managing the user lifecycle (Ban, Promote, Verify). +- **Inquiry System**: Public contact form handling with database persistence and notification triggers. + +## 🛠️ Technology Stack + +- **Framework**: Laravel 12.x +- **Database**: MySQL / MariaDB +- **Authentication**: Laravel Sanctum +- **Permissions**: `spatie/laravel-permission` +- **Social Auth**: `laravel/socialite` +- **Testing**: PHPUnit + +## 📂 Project Structure + +- `app/Http/Controllers/Api`: Core API logic separated by domain (Admin, User, Public). +- `app/Models`: Eloquent models with strict typing and relationship definitions. +- `routes/api.php`: Centralized API route definitions grouped by middleware and version (`v1`). + +--- +© 2024 TrustLab. All Internal Rights Reserved. diff --git a/app/Console/Commands/NotifyCertificateExpirations.php b/app/Console/Commands/NotifyCertificateExpirations.php new file mode 100644 index 0000000..28c2c0f --- /dev/null +++ b/app/Console/Commands/NotifyCertificateExpirations.php @@ -0,0 +1,84 @@ +', $now) + ->where('valid_to', '<', $now->copy()->addDays(30)) + ->with('user') + ->get(); + + foreach ($certificates as $certificate) { + $user = $certificate->user; + if (!$user || !$user->settings_certificate_renewal) { + continue; + } + + $daysRemaining = $now->diffInDays($certificate->valid_to, false); + $daysRemaining = (int) ceil($daysRemaining); // Ensure integer + + // Check if we already sent a notification TODAY for this certificate to avoid spamming + $alreadyNotifiedToday = $user->notifications() + ->where('type', 'App\Notifications\CertificateExpiringNotification') + ->where('data->certificate_id', $certificate->id) + ->where('created_at', '>=', $now->copy()->startOfDay()) + ->exists(); + + if ($alreadyNotifiedToday) { + continue; + } + + // Send Notification (Handles both Database/Bell and Mail channels based on days remaining) + $user->notify(new \App\Notifications\CertificateExpiringNotification($certificate, $daysRemaining)); + + $this->info("Sent notification to {$user->email} for certificate {$certificate->common_name} (Expires in {$daysRemaining} days)"); + } + + // 2. Check for ALREADY EXPIRED certificates that haven't been notified of expiration yet + $expiredCertificates = \App\Models\Certificate::where('valid_to', '<', $now) + ->whereNull('expired_notification_sent_at') + ->with('user') + ->get(); + + foreach ($expiredCertificates as $certificate) { + $user = $certificate->user; + if (!$user || !$user->settings_certificate_renewal) { + continue; + } + + if ($user->email) { + \Illuminate\Support\Facades\Mail::to($user->email)->send(new \App\Mail\CertificateExpiredMail($certificate)); + + // Mark as notified so we don't spam + $certificate->update(['expired_notification_sent_at' => $now]); + + $this->info("Sent EXPIRED email to {$user->email} for certificate {$certificate->common_name}"); + } + } + + $this->info('Certificate expiration check completed.'); + } +} diff --git a/app/Helpers/UuidHelper.php b/app/Helpers/UuidHelper.php new file mode 100644 index 0000000..efe7e8f --- /dev/null +++ b/app/Helpers/UuidHelper.php @@ -0,0 +1,18 @@ + function ($query) { + $query->orderBy('major', 'desc') + ->orderBy('minor', 'desc') + ->orderBy('patch', 'desc'); + }])->get(); + return response()->json(['data' => $pages]); + } + + public function show($id) + { + $legalPage = LegalPage::findOrFail($id); + + // Manual load latest revision + $latestRevision = $legalPage->revisions() + ->orderBy('major', 'desc') + ->orderBy('minor', 'desc') + ->orderBy('patch', 'desc') + ->first(); + + $legalPage->setRelation('latestRevision', $latestRevision); + + return response()->json(['data' => $legalPage]); + } + + public function store(Request $request) + { + $request->validate([ + 'title' => 'required|string|max:255', + 'content' => 'required|string', + 'status' => 'required|in:draft,published', + ]); + + $slug = Str::slug($request->title); + + // Check if page exists + $page = LegalPage::where('slug', $slug)->first(); + + if ($page) { + // Smart Versioning: If exists, increment Major version automatically for "Create" flow + $maxMajor = $page->revisions()->max('major') ?? 0; + $major = $maxMajor + 1; + + // Auto-Archive Logic: If new version is published, archive others + if ($request->status === 'published') { + $page->revisions()->where('status', 'published')->update(['status' => 'archived']); + } + + $page->revisions()->create([ + 'content' => $request->content, + 'major' => $major, + 'minor' => 0, + 'patch' => 0, + 'status' => $request->status, + 'published_at' => $request->status === 'published' ? now() : null, + 'change_log' => 'Created via New Page (Auto-increment Major)', + 'is_active' => true, + 'created_by' => auth()->id(), + ]); + + return response()->json(['data' => $page, 'message' => 'New major version created for existing Legal Page'], 201); + } else { + // Create New + $page = LegalPage::create([ + 'title' => $request->title, + 'slug' => $slug, + 'is_active' => true, + ]); + + // Initial create is always 1.0.0 + $page->revisions()->create([ + 'content' => $request->content, + 'major' => 1, + 'minor' => 0, + 'patch' => 0, + 'status' => $request->status, + 'published_at' => $request->status === 'published' ? now() : null, + 'change_log' => 'Initial creation', + 'is_active' => true, + 'created_by' => auth()->id(), + ]); + + return response()->json(['data' => $page, 'message' => 'Legal page created successfully'], 201); + } + } + + public function update(Request $request, LegalPage $legalPage) + { + $request->validate([ + 'title' => 'string|max:255', + 'content' => 'required|string', + 'version_type' => 'required|in:major,minor,patch', // 'major', 'minor', 'patch' + 'parent_major' => 'nullable|integer', + 'parent_minor' => 'nullable|integer', + 'status' => 'required|in:draft,published', + 'change_log' => 'nullable|string', + ]); + + if ($request->has('title')) { + $legalPage->update(['title' => $request->title]); + } + + // Calculate Version + $major = 0; $minor = 0; $patch = 0; + + if ($request->version_type === 'major') { + $maxMajor = $legalPage->revisions()->max('major') ?? 0; + $major = $maxMajor + 1; + $minor = 0; + $patch = 0; + } elseif ($request->version_type === 'minor') { + if (!$request->parent_major) return response()->json(['message' => 'Parent Major required for Minor version'], 422); + $maxMinor = $legalPage->revisions() + ->where('major', $request->parent_major) + ->max('minor') ?? -1; + $major = $request->parent_major; + $minor = $maxMinor + 1; + $patch = 0; + } elseif ($request->version_type === 'patch') { + if (!$request->parent_major || is_null($request->parent_minor)) return response()->json(['message' => 'Parent Major and Minor required for Patch'], 422); + $maxPatch = $legalPage->revisions() + ->where('major', $request->parent_major) + ->where('minor', $request->parent_minor) + ->max('patch') ?? -1; + $major = $request->parent_major; + $minor = $request->parent_minor; + $patch = $maxPatch + 1; + } + + // Auto-Archive Logic: If new version is published, archive others + if ($request->status === 'published') { + $legalPage->revisions()->where('status', 'published')->update(['status' => 'archived']); + } + + $legalPage->revisions()->create([ + 'content' => $request->content, + 'major' => $major, + 'minor' => $minor, + 'patch' => $patch, + 'status' => $request->status, + 'published_at' => $request->status === 'published' ? now() : null, + 'change_log' => $request->change_log ?? 'Updated content', + 'is_active' => true, + 'created_by' => auth()->id(), + ]); + + return response()->json(['data' => $legalPage, 'message' => 'Legal page updated with new revision']); + } + + public function getHistory($id) { + $legalPage = LegalPage::findOrFail($id); + $revisions = $legalPage->revisions() + ->orderBy('major', 'desc') + ->orderBy('minor', 'desc') + ->orderBy('patch', 'desc') + ->get(); + return response()->json(['data' => $revisions]); + } + + public function destroy(LegalPage $legalPage) + { + $legalPage->delete(); + return response()->json(['message' => 'Legal page deleted']); + } +} diff --git a/app/Http/Controllers/Api/ApiKeyController.php b/app/Http/Controllers/Api/ApiKeyController.php new file mode 100644 index 0000000..3f7fc4a --- /dev/null +++ b/app/Http/Controllers/Api/ApiKeyController.php @@ -0,0 +1,106 @@ +json([ + 'data' => $request->user()->apiKeys()->orderBy('created_at', 'desc')->get() + ]); + } + + /** + * Create a new personal access token. + */ + public function store(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + ]); + + $key = ApiKey::generate(); + + $apiKey = $request->user()->apiKeys()->create([ + 'name' => $request->name, + 'key' => $key, + 'is_active' => true, + ]); + + return response()->json([ + 'message' => 'API Key created successfully', + 'token' => $key, + 'key' => $apiKey + ], 201); + } + + /** + * Revoke a personal access token. + */ + public function destroy($id) + { + $apiKey = Auth::user()->apiKeys()->where('id', $id)->first(); + + if (!$apiKey) { + return response()->json(['message' => 'API Key not found'], 404); + } + + $apiKey->delete(); + + return response()->json(['message' => 'API Key revoked successfully']); + } + + /** + * Toggle the active status of an API Key. + */ + public function toggle($id) + { + $apiKey = Auth::user()->apiKeys()->where('id', $id)->first(); + + if (!$apiKey) { + return response()->json(['message' => 'API Key not found'], 404); + } + + $apiKey->update([ + 'is_active' => !$apiKey->is_active + ]); + + return response()->json([ + 'message' => 'API Key status updated successfully', + 'is_active' => $apiKey->is_active + ]); + } + + /** + * Regenerate the contents of an API Key. + */ + public function regenerate($id) + { + $apiKey = Auth::user()->apiKeys()->where('id', $id)->first(); + + if (!$apiKey) { + return response()->json(['message' => 'API Key not found'], 404); + } + + $newKey = ApiKey::generate(); + + $apiKey->update([ + 'key' => $newKey, + 'last_used_at' => null, + ]); + + return response()->json([ + 'message' => 'API Key regenerated successfully', + 'token' => $newKey + ]); + } +} diff --git a/app/Http/Controllers/Api/CertificateApiController.php b/app/Http/Controllers/Api/CertificateApiController.php new file mode 100644 index 0000000..aead3ff --- /dev/null +++ b/app/Http/Controllers/Api/CertificateApiController.php @@ -0,0 +1,241 @@ +sslService = $sslService; + } + + /** + * List user certificates. + */ + public function index(Request $request) + { + $perPage = $request->input('per_page', 10); + $search = $request->input('search'); + + $query = Certificate::where('user_id', Auth::id()); + + if ($search) { + $query->where(function($q) use ($search) { + $q->where('common_name', 'like', "%{$search}%") + ->orWhere('serial_number', 'like', "%{$search}%") + ->orWhere('san', 'like', "%{$search}%"); + }); + } + + $certificates = $query->latest()->paginate($perPage); + + return response()->json([ + 'status' => 'success', + 'data' => $certificates, + 'ca_status' => $this->getCaStatus() + ]); + } + + /** + * Generate a new certificate. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'common_name' => 'required|string|max:255', + 'config_mode' => 'required|in:default,manual', + 'organization' => 'nullable|required_if:config_mode,manual|string|max:255', + 'locality' => 'nullable|required_if:config_mode,manual|string|max:255', + 'state' => 'nullable|required_if:config_mode,manual|string|max:255', + 'country' => 'nullable|required_if:config_mode,manual|string|size:2', + 'san' => 'nullable|string', + 'key_bits' => 'required|in:2048,4096', + 'is_test_short_lived' => 'nullable|boolean', + ]); + + if (!empty($validated['is_test_short_lived']) && !Auth::user()->isAdminOrOwner()) { + return response()->json(['status' => 'error', 'message' => 'Unauthorized for test mode'], 403); + } + + try { + if ($validated['config_mode'] === 'default') { + $defaults = Config::get('openssl.ca_leaf_default'); + $validated['organization'] = $defaults['organizationName']; + $validated['locality'] = $defaults['localityName']; + $validated['state'] = $defaults['stateOrProvinceName']; + $validated['country'] = $defaults['countryName']; + } + + $result = $this->sslService->generateLeaf($validated); + + $certificate = Certificate::create([ + 'user_id' => Auth::id(), + 'common_name' => $validated['common_name'], + 'organization' => $validated['organization'], + 'locality' => $validated['locality'], + 'state' => $validated['state'], + 'country' => $validated['country'], + 'san' => $validated['san'], + 'key_bits' => $validated['key_bits'], + 'serial_number' => $result['serial'], + 'cert_content' => $result['cert'], + 'key_content' => $result['key'], + 'csr_content' => $result['csr'], + 'valid_from' => $result['valid_from'], + 'valid_to' => $result['valid_to'], + ]); + + $this->logActivity('issue_cert', "Issued certificate for {$certificate->common_name}"); + + // Notify User + try { + Auth::user()->notify(new CertificateNotification($certificate, 'issued')); + } catch (\Throwable $e) { + \Illuminate\Support\Facades\Log::error('Failed to send certificate notification: ' . $e->getMessage()); + } + + return response()->json([ + 'status' => 'success', + 'message' => 'Certificate generated successfully', + 'data' => $certificate + ], 201); + } catch (\Throwable $e) { + \Log::error('Certificate generation failed: ' . $e->getMessage(), [ + 'exception' => $e, + 'trace' => $e->getTraceAsString() + ]); + return response()->json([ + 'status' => 'error', + 'message' => 'Failed to generate certificate: ' . $e->getMessage() + ], 500); + } + } + + /** + * Show certificate details. + */ + public function show(Certificate $certificate) + { + $this->authorizeOwner($certificate); + + return response()->json([ + 'status' => 'success', + 'data' => $certificate + ]); + } + + /** + * Delete a certificate. + */ + public function destroy(Certificate $certificate) + { + $this->authorizeOwner($certificate); + $commonName = $certificate->common_name; + $certificate->delete(); + + $this->logActivity('delete_cert', "Deleted certificate for {$commonName}"); + + // Notify User + try { + Auth::user()->notify(new CertificateNotification($certificate, 'revoked')); + } catch (\Throwable $e) { + \Illuminate\Support\Facades\Log::error('Failed to send certificate revocation notification: ' . $e->getMessage()); + } + + return response()->json([ + 'status' => 'success', + 'message' => 'Certificate deleted successfully' + ]); + } + + /** + * Initialize CA (Admin only). + */ + public function setupCa() + { + if (!Auth::user()->isAdminOrOwner()) { + return response()->json(['status' => 'error', 'message' => 'Unauthorized'], 403); + } + + // Allow setup if any of the required CA types are missing + $status = $this->getCaStatus(); + if ($status['is_ready']) { + return response()->json(['status' => 'error', 'message' => 'CA already fully initialized'], 400); + } + + if ($this->sslService->setupCa()) { + return response()->json(['status' => 'success', 'message' => 'CA successfully initialized']); + } + + return response()->json(['status' => 'error', 'message' => 'Failed to initialize CA'], 500); + } + + /** + * Download certificate files. + */ + public function downloadFile(Certificate $certificate, $type) + { + $this->authorizeOwner($certificate); + + $content = match($type) { + 'cert' => $certificate->cert_content, + 'key' => $certificate->key_content, + 'csr' => $certificate->csr_content, + default => abort(404) + }; + + $extension = match($type) { + 'cert' => 'crt', + 'key' => 'key', + 'csr' => 'csr', + }; + + $filename = Str::slug($certificate->common_name) . '.' . $extension; + + return response($content) + ->header('Content-Type', 'text/plain') + ->header('Content-Disposition', "attachment; filename={$filename}"); + } + + protected function getCaStatus() + { + $root = CaCertificate::where('ca_type', 'root')->exists(); + $int2048 = CaCertificate::where('ca_type', 'intermediate_2048')->exists(); + $int4096 = CaCertificate::where('ca_type', 'intermediate_4096')->exists(); + + return [ + 'root' => $root, + 'intermediate_2048' => $int2048, + 'intermediate_4096' => $int4096, + 'is_ready' => $root && $int2048 && $int4096, + 'missing' => array_keys(array_filter([ + 'root' => !$root, + 'intermediate_2048' => !$int2048, + 'intermediate_4096' => !$int4096, + ])) + ]; + } + + protected function authorizeOwner(Certificate $certificate) + { + if ($certificate->user_id !== Auth::id()) { + abort(403); + } + } +} diff --git a/app/Http/Controllers/Api/DashboardController.php b/app/Http/Controllers/Api/DashboardController.php new file mode 100644 index 0000000..e89b728 --- /dev/null +++ b/app/Http/Controllers/Api/DashboardController.php @@ -0,0 +1,160 @@ +user(); + + // Helper to calculate percentage change + $getTrend = function($current, $previous) { + if ($previous == 0) return $current > 0 ? 100 : 0; + return round((($current - $previous) / $previous) * 100, 1); + }; + + // Basic Stats + $currentMonth = now()->startOfMonth(); + $previousMonth = now()->subMonth()->startOfMonth(); + + // Certificates (Scoped to User) + $totalCertificates = Certificate::where('user_id', $user->id)->count(); + $prevCertificates = Certificate::where('user_id', $user->id)->where('created_at', '<', $currentMonth)->count(); + + // Active Certificates (Scoped to User) + $activeCertificates = Certificate::where('user_id', $user->id)->where('status', 'ISSUED')->where('valid_to', '>', now())->count(); + $prevActiveCertificates = Certificate::where('user_id', $user->id)->where('status', 'ISSUED')->where('valid_to', '>', now()->subMonth())->where('created_at', '<', $currentMonth)->count(); + + // Expired (Scoped to User) + $expiredCertificates = Certificate::where('user_id', $user->id)->where('valid_to', '<', now())->count(); + + // Tickets (Role Based) + $ticketQuery = Ticket::query()->whereIn('status', ['open', 'answered']); + if (!$user->isAdmin()) { + $ticketQuery->where('user_id', $user->id); + } + $activeTickets = $ticketQuery->count(); + + // Previous Tickets (Role Based) + $prevTicketQuery = Ticket::query()->whereIn('status', ['open', 'answered'])->where('created_at', '<', $currentMonth); + if (!$user->isAdmin()) { + $prevTicketQuery->where('user_id', $user->id); + } + $prevActiveTickets = $prevTicketQuery->count(); + + $stats = [ + 'total_certificates' => [ + 'value' => $totalCertificates, + 'trend' => $getTrend($totalCertificates, $prevCertificates), + 'trend_label' => 'vs last month' + ], + 'active_certificates' => [ + 'value' => $activeCertificates, + 'trend' => $getTrend($activeCertificates, $prevActiveCertificates), + 'trend_label' => 'vs last month' + ], + 'expired_certificates' => [ + 'value' => $expiredCertificates, + 'trend' => 0, + 'trend_label' => 'vs last month' + ], + 'active_tickets' => [ + 'value' => $activeTickets, + 'trend' => $getTrend($activeTickets, $prevActiveTickets), + 'trend_label' => 'vs last month' + ], + ]; + + // Admin only stats + if ($user->isAdmin()) { + $totalUsers = User::count(); + $prevUsers = User::where('created_at', '<', $currentMonth)->count(); + + $stats['total_users'] = [ + 'value' => $totalUsers, + 'trend' => $getTrend($totalUsers, $prevUsers), + 'trend_label' => 'vs last month' + ]; + + // Inquiries - trend calculation for "Pending" is hard, so we just wrap value to keep consistent structure + $stats['pending_inquiries'] = [ + 'value' => Inquiry::where('status', 'unread')->count(), + ]; + + // CA Certificate Downloads + $caDownloads = \App\Models\CaCertificate::select('ca_type', 'download_count')->get(); + foreach ($caDownloads as $ca) { + $stats['ca_downloads_' . $ca->ca_type] = [ + 'value' => $ca->download_count ?? 0, + 'label' => str_replace('_', ' ', strtoupper($ca->ca_type)) . ' Downloads' + ]; + } + + $stats['recent_users'] = User::latest()->take(5)->get(['id', 'first_name', 'last_name', 'email', 'created_at']); + } + + // Recent Activity + $activityLogQuery = ActivityLog::with('user:id,first_name,last_name,email,avatar') + ->latest() + ->take(10); + + if (!$user->isAdmin()) { + $activityLogQuery->where('user_id', $user->id); + } + + $recentActivity = $activityLogQuery->get()->map(function($log) { + return [ + 'id' => $log->id, + 'user_name' => $log->user ? $log->user->first_name . ' ' . $log->user->last_name : 'System', + 'user_avatar' => $log->user ? $log->user->avatar : null, + 'action' => $log->action, + 'description' => $log->description, + 'created_at' => $log->created_at->toIso8601String(), + ]; + }); + + // Chart Data (Certificate Issuance Trend - Last 7 Days) + $chartData = []; + for ($i = 6; $i >= 0; $i--) { + $date = now()->subDays($i)->format('Y-m-d'); + $countQuery = Certificate::whereDate('created_at', $date); + if (!$user->isAdmin()) { + $countQuery->where('user_id', $user->id); + } + + $chartData[] = [ + 'date' => $date, + 'day' => now()->subDays($i)->format('D'), + 'count' => $countQuery->count() + ]; + } + + return response()->json([ + 'status' => 'success', + 'data' => [ + 'stats' => $stats, + 'recent_activity' => $recentActivity, + 'chart_data' => $chartData, + 'server_time' => now()->toIso8601String(), + ] + ]); + } + + public function ping() + { + return response()->json([ + 'pong' => true, + 'time' => microtime(true), + ]); + } +} diff --git a/app/Http/Controllers/Api/InquiryController.php b/app/Http/Controllers/Api/InquiryController.php new file mode 100644 index 0000000..a4da1c7 --- /dev/null +++ b/app/Http/Controllers/Api/InquiryController.php @@ -0,0 +1,118 @@ +all(), [ + 'name' => 'required|string|max:255', + 'email' => 'required|email|max:255', + 'category' => 'required|string|max:255', + 'subject' => 'required|string|max:255', + 'message' => 'required|string|max:5000', + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } + + $inquiry = Inquiry::create($request->all()); + + try { + // Notify all admins + $admins = User::where('role', 'admin')->get(); + Notification::send($admins, new NewInquiryNotification($inquiry)); + } catch (\Exception $e) { + // Log the error but fail silently to the user, as the inquiry was saved. + \Illuminate\Support\Facades\Log::error('Failed to send NewInquiryNotification: ' . $e->getMessage()); + } + + return response()->json([ + 'message' => 'Your message has been sent successfully. We will get back to you soon!', + 'inquiry' => $inquiry + ], 201); + } + + /** + * List all inquiries (Admin). + */ + public function index(Request $request) + { + $search = $request->query('search'); + $status = $request->query('status'); + + $query = Inquiry::query(); + + if ($search) { + $query->where(function($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%") + ->orWhere('subject', 'like', "%{$search}%"); + }); + } + + if ($status) { + $query->where('status', $status); + } + + $inquiries = $query->orderBy('created_at', 'desc')->paginate(10); + + return response()->json($inquiries); + } + + /** + * Show a specific inquiry (Admin). + */ + public function show(Inquiry $inquiry) + { + return response()->json($inquiry); + } + + /** + * Delete an inquiry (Admin). + */ + public function destroy(Inquiry $inquiry) + { + $inquiry->delete(); + return response()->json(['message' => 'Inquiry deleted successfully.']); + } + + /** + * Reply to an inquiry (Admin). + */ + public function reply(Request $request, Inquiry $inquiry) + { + $request->validate([ + 'message' => 'required|string|max:5000', + ]); + + try { + // Send email using the support mailer + Mail::mailer('support')->to($inquiry->email)->send(new \App\Mail\InquiryReplyMail($inquiry, $request->message)); + + $inquiry->update([ + 'status' => 'replied', + 'replied_at' => now(), + ]); + + return response()->json(['message' => 'Reply sent successfully.']); + } catch (\Exception $e) { + return response()->json(['message' => 'Failed to send reply: ' . $e->getMessage()], 500); + } + } +} diff --git a/app/Http/Controllers/Api/LegalPageController.php b/app/Http/Controllers/Api/LegalPageController.php new file mode 100644 index 0000000..c33607b --- /dev/null +++ b/app/Http/Controllers/Api/LegalPageController.php @@ -0,0 +1,42 @@ +where('is_active', true)->firstOrFail(); + + // Robustly fetch the latest active revision that is published + $latestRevision = $page->revisions() + ->where('is_active', true) + ->where('status', 'published') + ->orderBy('major', 'desc') + ->orderBy('minor', 'desc') + ->orderBy('patch', 'desc') + ->first(); + + if (!$latestRevision) { + return response()->json(['message' => 'Content not available'], 404); + } + + // Manually attach for response structure if needed, or just build response + return response()->json(['data' => [ + 'title' => $page->title, + 'content' => $latestRevision->content, + 'updated_at' => $latestRevision->created_at, + 'version' => $latestRevision->version, + ]]); + } + + public function index() + { + $pages = LegalPage::where('is_active', true)->select('title', 'slug')->get(); + return response()->json(['data' => $pages]); + } +} diff --git a/app/Http/Controllers/Api/MailController.php b/app/Http/Controllers/Api/MailController.php new file mode 100644 index 0000000..da993fb --- /dev/null +++ b/app/Http/Controllers/Api/MailController.php @@ -0,0 +1,64 @@ +validate([ + 'email' => 'required|email', + 'mailer' => 'required|string|in:smtp,support', + ]); + + $mailer = $request->mailer; + $recipient = $request->email; + $host = Config::get("mail.mailers.{$mailer}.host"); + + try { + Mail::mailer($mailer)->to($recipient)->send(new TestMail($mailer, $host)); + + return response()->json([ + 'success' => true, + 'message' => "Test email successfully sent via {$mailer} mailer.", + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => "Failed to send email: " . $e->getMessage(), + ], 500); + } + } + + /** + * Get current mailer configurations (excluding passwords). + */ + public function getConfigurations() + { + $configs = [ + 'smtp' => [ + 'host' => config('mail.mailers.smtp.host'), + 'port' => config('mail.mailers.smtp.port'), + 'encryption' => config('mail.mailers.smtp.encryption'), + 'from' => config('mail.from.address'), + ], + 'support' => [ + 'host' => config('mail.mailers.support.host'), + 'port' => config('mail.mailers.support.port'), + 'encryption' => config('mail.mailers.support.encryption'), + 'from' => config('mail.mailers.support.from.address'), + ], + ]; + + return response()->json($configs); + } +} diff --git a/app/Http/Controllers/Api/NotificationController.php b/app/Http/Controllers/Api/NotificationController.php new file mode 100644 index 0000000..aa6d771 --- /dev/null +++ b/app/Http/Controllers/Api/NotificationController.php @@ -0,0 +1,70 @@ +user(); + $query = $user->notifications(); + + // Filter by state + if ($request->has('filter')) { + if ($request->filter === 'unread') { + $query = $user->unreadNotifications(); + } elseif ($request->filter === 'read') { + $query = $user->readNotifications(); + } + } + + // Search in data (JSON) + if ($request->has('search')) { + $search = $request->search; + $query->where('data', 'like', "%{$search}%"); + } + + $notifications = $query->latest()->paginate(10); + + return response()->json($notifications); + } + + /** + * Mark a specific notification as read. + */ + public function markAsRead(Request $request, $id) + { + $notification = $request->user()->notifications()->findOrFail($id); + $notification->markAsRead(); + + return response()->json(['message' => 'Notification marked as read']); + } + + /** + * Mark all notifications as read. + */ + public function markAllAsRead(Request $request) + { + $request->user()->unreadNotifications->markAsRead(); + + return response()->json(['message' => 'All notifications marked as read']); + } + + /** + * Remove the specified notification. + */ + public function destroy(Request $request, $id) + { + $notification = $request->user()->notifications()->findOrFail($id); + $notification->delete(); + + return response()->json(['message' => 'Notification deleted']); + } +} diff --git a/app/Http/Controllers/Api/PasswordResetController.php b/app/Http/Controllers/Api/PasswordResetController.php new file mode 100644 index 0000000..32985df --- /dev/null +++ b/app/Http/Controllers/Api/PasswordResetController.php @@ -0,0 +1,96 @@ +validate(['email' => 'required|email']); + + $user = User::where('email', $request->email)->first(); + + if (!$user) { + // We return a "success" message anyway to prevent email enumeration + return response()->json(['message' => 'Jika email tersebut terdaftar, kami akan mengirimkan link reset password.']); + } + + // Generate a token + $token = Str::random(64); + + // Store token in password_reset_tokens table + DB::table('password_reset_tokens')->updateOrInsert( + ['email' => $request->email], + [ + 'token' => Hash::make($token), + 'created_at' => Carbon::now() + ] + ); + + // Send Email + $resetUrl = config('app.frontend_url') . '/reset-password?token=' . $token . '&email=' . urlencode($request->email); + + Mail::send('emails.password-reset', ['url' => $resetUrl, 'name' => $user->first_name], function ($message) use ($request) { + $message->to($request->email); + $message->subject('Reset Password - TrustLab'); + }); + + return response()->json(['message' => 'Reset link sent to your email.']); + } + + /** + * Reset the given user's password. + */ + public function resetPassword(Request $request) + { + $request->validate([ + 'token' => 'required', + 'email' => 'required|email', + 'password' => 'required|min:8|confirmed', + ]); + + $reset = DB::table('password_reset_tokens') + ->where('email', $request->email) + ->first(); + + if (!$reset || !Hash::check($request->token, $reset->token)) { + return response()->json(['message' => 'Invalid or expired token.'], 400); + } + + // Check expiry (e.g., 60 minutes) + if (Carbon::parse($reset->created_at)->addMinutes(60)->isPast()) { + DB::table('password_reset_tokens')->where('email', $request->email)->delete(); + return response()->json(['message' => 'Token has expired.'], 400); + } + + $user = User::where('email', $request->email)->first(); + + if (!$user) { + return response()->json(['message' => 'User not found.'], 404); + } + + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + // Delete token + DB::table('password_reset_tokens')->where('email', $request->email)->delete(); + + return response()->json(['message' => 'Password reset successful.']); + } +} diff --git a/app/Http/Controllers/Api/ProfileController.php b/app/Http/Controllers/Api/ProfileController.php new file mode 100644 index 0000000..3754b49 --- /dev/null +++ b/app/Http/Controllers/Api/ProfileController.php @@ -0,0 +1,356 @@ +user(); + + $validated = $request->validate([ + 'first_name' => 'nullable|string|max:255', + 'last_name' => 'nullable|string|max:255', + 'email' => 'nullable|email|max:255|unique:users,email,' . $user->id, + 'phone' => 'nullable|string|max:20', + 'bio' => 'nullable|string', + 'job_title' => 'nullable|string|max:255', + 'location' => 'nullable|string|max:255', + 'country' => 'nullable|string|max:255', + 'city_state' => 'nullable|string|max:255', + 'postal_code' => 'nullable|string|max:20', + 'tax_id' => 'nullable|string|max:50', + 'facebook' => 'nullable|string|max:255', + 'twitter' => 'nullable|string|max:255', + 'linkedin' => 'nullable|string|max:255', + 'instagram' => 'nullable|string|max:255', + 'settings_email_alerts' => 'sometimes|boolean', + 'settings_certificate_renewal' => 'sometimes|boolean', + 'default_landing_page' => 'sometimes|string|max:255', + 'theme' => 'sometimes|string|in:light,dark,system', + 'language' => 'sometimes|string|max:10', + ]); + + // Handle Email Change Logic + if (isset($validated['email']) && $validated['email'] !== $user->email) { + $pendingEmail = $validated['email']; + + // Basic check to avoid duplication with other pending_emails if necessary, + // but unique:users,email already covers the main one. + + $user->pending_email = $pendingEmail; + $user->save(); + + // Send notification to the NEW email + $user->notify(new \App\Notifications\PendingEmailVerificationNotification); + + // Remove email from validated so it doesn't update the primary email yet + unset($validated['email']); + } + + // Sanitize social links to store only usernames + if (isset($validated['facebook'])) $validated['facebook'] = $this->extractUsername($validated['facebook'], 'facebook.com'); + if (isset($validated['twitter'])) $validated['twitter'] = $this->extractUsername($validated['twitter'], ['twitter.com', 'x.com']); + if (isset($validated['linkedin'])) $validated['linkedin'] = $this->extractUsername($validated['linkedin'], 'linkedin.com/in'); + if (isset($validated['instagram'])) $validated['instagram'] = $this->extractUsername($validated['instagram'], 'instagram.com'); + + // Log the update attempt for debugging + \Illuminate\Support\Facades\Log::info('Profile Update Request:', ['user_id' => $user->id, 'payload' => $validated]); + + $user->update($validated); + + return response()->json([ + 'message' => 'Profile updated successfully.', + 'user' => $user, + ]); + } + + /** + * Update the user's password. + */ + public function updatePassword(Request $request) + { + $validated = $request->validate([ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', 'confirmed', Password::defaults()], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return response()->json([ + 'message' => 'Password updated successfully.', + ]); + } + + /** + * Update the user's avatar. + */ + public function updateAvatar(Request $request) + { + $request->validate([ + 'avatar' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:5120', + ]); + + $user = $request->user(); + $file = $request->file('avatar'); + + $extension = $file->getClientOriginalExtension(); + + // Requirement 1: URL avatar yang sedang digunakan adalah clean uuid + // Output: avatars/{user-uuid}.{ext} + $newFilename = "{$user->id}.{$extension}"; + $newPath = "avatars/{$newFilename}"; + + // Requirement 2: Jika ganti, pindahkan lama ke trash/{user-uuid}-{trash-random}-{original-filename} + if ($user->avatar) { + $oldPath = $this->getRelativePath($user->avatar); + + if ($oldPath) { + // If it's on R2, move it to trash + if (Storage::disk('r2')->exists($oldPath)) { + $trashRandom = Str::random(10); + $oldBasename = basename($oldPath); + $trashPath = "trash/{$user->id}-{$trashRandom}-{$oldBasename}"; + + // S3/R2 copy + delete is more reliable than move in some environments + Storage::disk('r2')->copy($oldPath, $trashPath); + Storage::disk('r2')->delete($oldPath); + } + // If it's still on local storage (migration case), just delete it + elseif (Storage::disk('public')->exists($oldPath)) { + Storage::disk('public')->delete($oldPath); + } + } + } + + // Upload to R2 + // Upload to R2 with Cache-Control to prevent long caching + $path = $file->storeAs('avatars', $newFilename, [ + 'disk' => 'r2', + 'CacheControl' => 'no-cache, no-store, max-age=0, must-revalidate', + ]); + $url = Storage::disk('r2')->url($path); + + $user->update(['avatar' => $url]); + + return response()->json([ + 'message' => 'Avatar updated successfully.', + 'avatar_url' => $url, + ]); + } + + /** + * Helper to extract relative path from full URL + */ + private function getRelativePath($url) + { + if (!$url) return null; + + $baseUrl = config('filesystems.disks.r2.url'); + + // Strip query string if any before processing + $urlWithoutQuery = explode('?', $url)[0]; + + if (str_starts_with($urlWithoutQuery, $baseUrl)) { + return ltrim(str_replace($baseUrl, '', $urlWithoutQuery), '/'); + } + + // Handle legacy local storage URLs if any + if (str_contains($urlWithoutQuery, '/storage/')) { + return 'avatars/' . basename($urlWithoutQuery); + } + + return $urlWithoutQuery; + } + + /** + * Get Login History (Last 1 month, max 10) + */ + public function getLoginHistory(Request $request) + { + $history = $request->user()->loginHistories() + ->where('created_at', '>=', now()->subMonth()) + ->latest() + ->limit(10) + ->get(); + + return response()->json($history); + } + + /** + * Delete user account + */ + public function deleteAccount(Request $request) + { + $user = $request->user(); + + // Optional: Perform any cleanup here (delete avatar, certificates, etc.) + if ($user->avatar) { + $oldPath = $this->getRelativePath($user->avatar); + if ($oldPath && Storage::disk('r2')->exists($oldPath)) { + $trashRandom = Str::random(10); + $oldBasename = basename($oldPath); + $trashPath = "trash/deleted_user_{$user->id}-{$trashRandom}-{$oldBasename}"; + Storage::disk('r2')->move($oldPath, $trashPath); + } + } + + // Revoke Social Tokens + foreach ($user->socialAccounts as $account) { + // Re-use logic or call external helper? For simplicity/speed, implementing inline revocation or calling AuthController logic if possible. + // Better: Iterate and manually revoke to ensure clean slate. + try { + if ($account->provider === 'google' && $account->token) { + \Illuminate\Support\Facades\Http::post('https://oauth2.googleapis.com/revoke', ['token' => $account->token]); + } + // GitHub revocation is more complex inline without config access handy, but we attempt basic cleanup + } catch (\Exception $e) { + // Continue deletion + } + } + + $user->delete(); + + return response()->json([ + 'message' => 'Account deleted successfully.', + ]); + } + + /** + * Get active sessions from database + */ + public function getActiveSessions(Request $request) + { + $sessions = DB::table('sessions') + ->where('user_id', $request->user()->id) + ->get() + ->map(function ($session) use ($request) { + $info = $this->parseUserAgent($session->user_agent); + return [ + 'id' => $session->id, + 'ip_address' => $session->ip_address, + 'browser' => $info['browser'], + 'os' => $info['os'], + 'device_type' => $info['device'], + 'last_active' => $session->last_activity, + 'is_current' => $session->id === $request->session()->getId(), + ]; + }); + + return response()->json($sessions); + } + + /** + * Revoke a specific session + */ + public function revokeSession(Request $request, $id) + { + // Don't allow revoking current session via this endpoint for safety + if ($id === $request->session()->getId()) { + return response()->json(['message' => 'Cannot revoke current session.'], 400); + } + + DB::table('sessions') + ->where('user_id', $request->user()->id) + ->where('id', $id) + ->delete(); + + return response()->json(['message' => 'Session revoked successfully.']); + } + + /** + * Simple User Agent Parser (Copied from AuthController for consistency) + */ + private function parseUserAgent($agent) + { + $os = 'Unknown OS'; + $browser = 'Unknown Browser'; + $device = 'Desktop'; + + if (!$agent) return ['os' => $os, 'browser' => $browser, 'device' => $device]; + + // OS Parsing + if (preg_match('/iphone|ipad|ipod/i', $agent)) { + $os = 'iOS'; + $device = 'iOS'; + } elseif (preg_match('/android/i', $agent)) { + $os = 'Android'; + $device = 'Android'; + } elseif (preg_match('/windows/i', $agent)) { + $os = 'Windows'; + $device = 'Windows'; + } elseif (preg_match('/macintosh|mac os x/i', $agent)) { + $os = 'Mac'; + $device = 'Mac'; + } elseif (preg_match('/linux/i', $agent)) { + $os = 'Linux'; + $device = 'Linux'; + } + + // Browser Parsing + if (preg_match('/msie/i', $agent) && !preg_match('/opera/i', $agent)) { + $browser = 'Internet Explorer'; + } elseif (preg_match('/firefox/i', $agent)) { + $browser = 'Firefox'; + } elseif (preg_match('/chrome/i', $agent)) { + $browser = 'Chrome'; + } elseif (preg_match('/safari/i', $agent)) { + $browser = 'Safari'; + } elseif (preg_match('/opera/i', $agent)) { + $browser = 'Opera'; + } + + return [ + 'os' => $os, + 'browser' => $browser, + 'device' => $device + ]; + } + + /** + * Helper to extract username from social media URLs. + */ + private function extractUsername(?string $value, $domains): ?string + { + if (!$value) return null; + + $value = trim($value); + if (empty($value)) return null; + + // If it doesn't look like a URL, assume it's already a username + if (!str_contains($value, '/') && !str_contains($value, '.')) { + return ltrim($value, '@'); + } + + $domains = (array) $domains; + foreach ($domains as $domain) { + // Clean domain for regex (e.g. linkedin.com/in) + $safeDomain = str_replace('/', '\/', preg_quote($domain)); + $pattern = "/(?:https?:\/\/)?(?:www\.)?{$safeDomain}\/([^\/\?#]+)/i"; + + if (preg_match($pattern, $value, $matches)) { + return $matches[1]; + } + } + + // If it's a URL but doesn't match the expected domain, just return the last part or the value itself + // to avoid losing data if the user inputs something slightly different + $parts = explode('/', rtrim($value, '/')); + return end($parts); + } +} diff --git a/app/Http/Controllers/Api/PublicCaController.php b/app/Http/Controllers/Api/PublicCaController.php new file mode 100644 index 0000000..d2599a3 --- /dev/null +++ b/app/Http/Controllers/Api/PublicCaController.php @@ -0,0 +1,171 @@ +get(['common_name', 'ca_type', 'serial_number', 'valid_to', 'cert_content']) + ->map(function ($cert) { + return [ + 'name' => $cert->common_name, + 'type' => $cert->ca_type, + 'serial' => $cert->serial_number, + 'expires_at' => $cert->valid_to->toIso8601String(), + ]; + }); + + return response()->json([ + 'success' => true, + 'data' => $certificates + ]); + } + + /** + * Download certificate in various formats. + */ + public function download(Request $request, $serial) + { + $cert = CaCertificate::where('serial_number', $serial)->firstOrFail(); + $cert->increment('download_count'); + $cert->update(['last_downloaded_at' => now()]); + $format = $request->query('format', 'pem'); + + if ($format === 'der') { + // Convert PEM to DER (Base64 decode the body) + $pem = $cert->cert_content; + $lines = explode("\n", trim($pem)); + $payload = ''; + foreach ($lines as $line) { + if (!str_starts_with($line, '-----')) { + $payload .= trim($line); + } + } + $der = base64_decode($payload); + + return response($der) + ->header('Content-Type', 'application/x-x509-ca-cert') + ->header('Content-Disposition', 'attachment; filename="' . $cert->common_name . '.der"'); + } + + // Default PEM + return response($cert->cert_content) + ->header('Content-Type', 'application/x-pem-file') + ->header('Content-Disposition', 'attachment; filename="' . $cert->common_name . '.crt"'); + } + + /** + * Download Windows One-Click Installer (.bat) + */ + public function downloadWindows($serial) + { + $cert = CaCertificate::where('serial_number', $serial)->firstOrFail(); + $cert->increment('download_count'); + $cert->update(['last_downloaded_at' => now()]); + $store = $cert->ca_type === 'root' ? 'Root' : 'CA'; + $filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $cert->common_name); + + // Convert CRLF to ensure batch file works + $certContent = str_replace("\n", "\r\n", str_replace("\r\n", "\n", $cert->cert_content)); + + $script = "@echo off\r\n"; + $script .= "echo Installing " . $cert->common_name . "...\r\n"; + $script .= "echo Please allow the security prompt to trust this certificate.\r\n"; + $script .= "set \"CERT_FILE=%TEMP%\\" . $filename . ".crt\"\r\n"; + $script .= "((\r\n"; + foreach(explode("\r\n", $certContent) as $line) { + if(!empty($line)) $script .= "echo " . $line . "\r\n"; + } + $script .= ")) > \"%CERT_FILE%\"\r\n"; + $script .= "certutil -addstore -f \"" . $store . "\" \"%CERT_FILE%\"\r\n"; + $script .= "del \"%CERT_FILE%\"\r\n"; + $script .= "pause\r\n"; + + return response($script) + ->header('Content-Type', 'application/x-bat') + ->header('Content-Disposition', 'attachment; filename="install-' . $filename . '.bat"'); + } + + /** + * Download macOS Configuration Profile (.mobileconfig) + */ + public function downloadMac($serial) + { + $cert = CaCertificate::where('serial_number', $serial)->firstOrFail(); + $cert->increment('download_count'); + $cert->update(['last_downloaded_at' => now()]); + + // Extract Base64 payload + $pem = $cert->cert_content; + $lines = explode("\n", trim($pem)); + $payload = ''; + foreach ($lines as $line) { + if (!str_starts_with($line, '-----')) { + $payload .= trim($line); + } + } + + $uuid = \Illuminate\Support\Str::uuid(); + $identifier = 'com.trustlab.cert.' . $serial; + $name = $cert->common_name; + + $xml = ' + + + + PayloadContent + + + PayloadCertificateFileName + ' . $name . '.cer + PayloadContent + + ' . $payload . ' + + PayloadDescription + Adds ' . $name . ' to Trusted Root Store + PayloadDisplayName + ' . $name . ' + PayloadIdentifier + ' . $identifier . '.cert + PayloadType + com.apple.security.pkcs1 + PayloadUUID + ' . \Illuminate\Support\Str::uuid() . ' + PayloadVersion + 1 + + + PayloadDisplayName + ' . $name . ' Installer + PayloadIdentifier + ' . $identifier . ' + PayloadRemovalDisallowed + + PayloadType + Configuration + PayloadUUID + ' . $uuid . ' + PayloadVersion + 1 + +'; + + return response($xml) + ->header('Content-Type', 'application/x-apple-aspen-config') + ->header('Content-Disposition', 'attachment; filename="' . $name . '.mobileconfig"'); + } +} diff --git a/app/Http/Controllers/Api/RootCaApiController.php b/app/Http/Controllers/Api/RootCaApiController.php new file mode 100644 index 0000000..d68b658 --- /dev/null +++ b/app/Http/Controllers/Api/RootCaApiController.php @@ -0,0 +1,69 @@ +sslService = $sslService; + } + + public function index() + { + $this->authorizeAdmin(); + + $certificates = CaCertificate::all()->map(function($cert) { + $cert->status = $cert->valid_to->isFuture() ? 'valid' : 'expired'; + return $cert; + }); + + return response()->json([ + 'status' => 'success', + 'data' => $certificates + ]); + } + + public function renew(Request $request, CaCertificate $certificate) + { + $this->authorizeAdmin(); + + $days = (int) $request->input('days', 3650); + + try { + $newData = $this->sslService->renewCaCertificate($certificate, $days); + + $certificate->update([ + 'cert_content' => $newData['cert_content'], + 'serial_number' => $newData['serial_number'], + 'valid_from' => $newData['valid_from'], + 'valid_to' => $newData['valid_to'], + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Certificate renewed successfully.', + 'data' => $certificate + ]); + } catch (\Exception $e) { + return response()->json([ + 'status' => 'error', + 'message' => 'Renewal failed: ' . $e->getMessage() + ], 500); + } + } + + protected function authorizeAdmin() + { + if (auth()->user()->role !== 'admin') { + abort(403, 'Unauthorized action.'); + } + } +} diff --git a/app/Http/Controllers/Api/TicketController.php b/app/Http/Controllers/Api/TicketController.php new file mode 100644 index 0000000..00502a2 --- /dev/null +++ b/app/Http/Controllers/Api/TicketController.php @@ -0,0 +1,241 @@ +user(); + $query = Ticket::with(['user:id,first_name,last_name,email,avatar', 'replies.user:id,first_name,last_name,avatar', 'replies.attachments']); + + // Only show all tickets if user is admin AND explicitly asks for all + if ($user->isAdmin() && $request->has('all')) { + // No additional where clause needed + } else { + // Everyone else (including admins in personal view) only sees their own + $query->where('user_id', $user->id); + } + + $tickets = $query->latest()->paginate(10); + + return response()->json($tickets); + } + + /** + * Store a newly created ticket. + */ + public function store(Request $request) + { + $user = $request->user(); + + $validator = Validator::make($request->all(), [ + 'subject' => 'required|string|max:255', + 'category' => 'required|string', + 'priority' => 'required|in:low,medium,high', + 'message' => 'required|string', + 'attachments' => 'array|max:5', + 'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf,doc,docx,zip,txt|max:10240', + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } + + try { + return DB::transaction(function () use ($request, $user) { + $ticket = Ticket::create([ + 'user_id' => $user->id, + 'ticket_number' => Ticket::generateTicketNumber(), + 'subject' => $request->subject, + 'category' => $request->category, + 'priority' => $request->priority, + 'status' => 'open', + ]); + + $this->logActivity('create_ticket', "Created ticket #{$ticket->ticket_number}: {$ticket->subject}"); + + $reply = TicketReply::create([ + 'ticket_id' => $ticket->id, + 'user_id' => $user->id, + 'message' => $request->message, + ]); + + // Handle Attachments + if ($request->hasFile('attachments')) { + foreach ($request->file('attachments') as $file) { + $path = $file->store('ticket-attachments', 'r2'); + $url = Storage::disk('r2')->url($path); + TicketAttachment::create([ + 'ticket_reply_id' => $reply->id, + 'file_name' => $file->getClientOriginalName(), + 'file_path' => $url, + 'file_type' => $file->getClientMimeType(), + 'file_size' => $file->getSize(), + ]); + } + } + + // Notify Admins + try { + $admins = User::where('role', 'admin') + ->where('id', '!=', $user->id) + ->get(); + if ($admins->isNotEmpty()) { + Notification::send($admins, new NewTicketNotification($ticket->load('user'))); + } + } catch (\Throwable $e) { + // Log error but don't fail the request + \Illuminate\Support\Facades\Log::error('Failed to send ticket notification: ' . $e->getMessage()); + } + + return response()->json([ + 'message' => 'Ticket created successfully', + 'ticket' => $ticket->load(['user:id,first_name,last_name,email,avatar', 'replies.user:id,first_name,last_name,avatar', 'replies.attachments']) + ], 201); + }); + } catch (\Throwable $e) { + \Illuminate\Support\Facades\Log::error('Ticket creation failed: ' . $e->getMessage()); + return response()->json(['message' => 'Failed to create ticket: ' . $e->getMessage()], 500); + } + } + + /** + * Display the specified ticket with replies. + */ + public function show(Request $request, $id) + { + $user = $request->user(); + $ticket = Ticket::with(['user:id,first_name,last_name,email,avatar', 'replies.user:id,first_name,last_name,avatar', 'replies.attachments'])->findOrFail($id); + + if (!$user->isAdmin() && $ticket->user_id !== $user->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + return response()->json($ticket); + } + + /** + * Add a reply to the ticket. + */ + public function reply(Request $request, $id) + { + $user = $request->user(); + $ticket = Ticket::findOrFail($id); + + if (!$user->isAdmin() && $ticket->user_id !== $user->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + if ($ticket->status === 'closed') { + return response()->json(['message' => 'Cannot reply to a closed ticket'], 422); + } + + $validator = Validator::make($request->all(), [ + 'message' => 'required|string', + 'attachments' => 'array|max:5', + 'attachments.*' => 'file|mimes:jpg,jpeg,png,pdf,doc,docx,zip,txt|max:10240', + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } + + $reply = TicketReply::create([ + 'ticket_id' => $ticket->id, + 'user_id' => $user->id, + 'message' => $request->message, + ]); + + $this->logActivity('reply_ticket', "Replied to ticket #{$ticket->ticket_number}"); + + // Handle Attachments + if ($request->hasFile('attachments')) { + foreach ($request->file('attachments') as $file) { + $path = $file->store('ticket-attachments', 'r2'); + $url = Storage::disk('r2')->url($path); + TicketAttachment::create([ + 'ticket_reply_id' => $reply->id, + 'file_name' => $file->getClientOriginalName(), + 'file_path' => $url, + 'file_type' => $file->getClientMimeType(), + 'file_size' => $file->getSize(), + ]); + } + } + + // Update ticket status & Notify + try { + if ($user->isAdmin()) { + $ticket->update(['status' => 'answered']); + // Notify Customer + $ticketUser = $ticket->user; + if ($ticketUser && $ticketUser->id !== $user->id) { + $ticketUser->notify(new TicketReplyNotification($ticket, $reply, true)); + } + + // Also notify OTHER admins + $otherAdmins = User::where('role', 'admin') + ->where('id', '!=', $user->id) + ->get(); + if ($otherAdmins->isNotEmpty()) { + Notification::send($otherAdmins, new TicketReplyNotification($ticket, $reply, true)); + } + } else { + $ticket->update(['status' => 'open']); + // Notify All Admins + $admins = User::where('role', 'admin')->get(); + if ($admins->isNotEmpty()) { + Notification::send($admins, new TicketReplyNotification($ticket, $reply, false)); + } + } + } catch (\Throwable $e) { + \Illuminate\Support\Facades\Log::error('Failed to send ticket reply notification: ' . $e->getMessage()); + } + + return response()->json([ + 'message' => 'Reply added successfully', + 'reply' => $reply->load(['user:id,first_name,last_name,avatar', 'attachments']) + ], 201); + } + + /** + * Close the ticket. + */ + public function close(Request $request, $id) + { + $user = $request->user(); + $ticket = Ticket::findOrFail($id); + + if (!$user->isAdmin() && $ticket->user_id !== $user->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + $ticket->update(['status' => 'closed']); + + $this->logActivity('close_ticket', "Closed ticket #{$ticket->ticket_number}"); + + return response()->json([ + 'message' => 'Ticket closed successfully', + 'ticket' => $ticket + ]); + } +} diff --git a/app/Http/Controllers/Api/UserApiController.php b/app/Http/Controllers/Api/UserApiController.php new file mode 100644 index 0000000..160d1cb --- /dev/null +++ b/app/Http/Controllers/Api/UserApiController.php @@ -0,0 +1,157 @@ +authorizeAdminOrOwner(); + $query = User::query(); + + // Search by name or email + if ($request->has('search')) { + $search = $request->input('search'); + $query->where(function($q) use ($search) { + $q->where('email', 'like', "%{$search}%") + ->orWhere('first_name', 'like', "%{$search}%") + ->orWhere('last_name', 'like', "%{$search}%"); + }); + } + + // Filter by role + if ($request->has('role')) { + $query->where('role', $request->input('role')); + } + + // Sort + $sortBy = $request->input('sort_by', 'created_at'); + $sortOrder = $request->input('sort_order', 'desc'); + $query->orderBy($sortBy, $sortOrder); + + return $query->paginate($request->input('per_page', 10)); + } + + /** + * Store a newly created user in storage. + */ + public function store(Request $request) + { + $this->authorizeAdminOrOwner(); + + if (auth()->user()->isOwner()) { + $roles = [User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_CUSTOMER]; + } else { + // Admins can only create Customers + $roles = [User::ROLE_CUSTOMER]; + } + + $validated = $request->validate([ + 'first_name' => 'required|string|max:255', + 'last_name' => 'nullable|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'password' => 'required|string|min:8', + 'role' => ['required', Rule::in($roles)], + ]); + + $validated['password'] = Hash::make($validated['password']); + + $user = User::create($validated); + + return response()->json([ + 'message' => 'User created successfully', + 'user' => $user + ], 201); + } + + /** + * Display the specified user. + */ + public function show(User $user) + { + $this->authorizeAdminOrOwner(); + return $user; + } + + /** + * Update the specified user in storage. + */ + public function update(Request $request, User $user) + { + $this->authorizeAdminOrOwner(); + + // Permission check: Admins cannot modify Admin/Owner accounts + if (auth()->user()->isAdmin() && $user->role !== User::ROLE_CUSTOMER) { + abort(403, 'Admins can only manage Customer accounts.'); + } + + $validated = $request->validate([ + 'first_name' => 'sometimes|required|string|max:255', + 'last_name' => 'nullable|string|max:255', + 'email' => ['sometimes', 'required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)], + 'password' => 'nullable|string|min:8', + 'role' => [ + 'sometimes', + 'required', + auth()->user()->isOwner() ? Rule::in([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_CUSTOMER]) : Rule::in([$user->role]) + ], + ]); + + // Admins cannot change roles (already handled by Rule::in([$user->role]) above for safety, but let's be explicit) + if (auth()->user()->isAdmin() && isset($validated['role']) && $validated['role'] !== $user->role) { + abort(403, 'Admins cannot change user roles.'); + } + + + if (!empty($validated['password'])) { + $validated['password'] = Hash::make($validated['password']); + } else { + unset($validated['password']); + } + + $user->update($validated); + + return response()->json([ + 'message' => 'User updated successfully', + 'user' => $user + ]); + } + + /** + * Remove the specified user from storage. + */ + public function destroy(User $user) + { + $this->authorizeAdminOrOwner(); + + // Permission check: Admins can only delete Customer accounts + if (auth()->user()->isAdmin() && $user->role !== User::ROLE_CUSTOMER) { + abort(403, 'Admins can only delete Customer accounts.'); + } + // Prevent deleting yourself + if (auth()->id() === $user->id) { + return response()->json(['message' => 'You cannot delete your own account.'], 403); + } + + $user->delete(); + + return response()->json(['message' => 'User deleted successfully']); + } + + protected function authorizeAdminOrOwner() + { + if (!auth()->user()->isAdminOrOwner()) { + abort(403, 'Unauthorized action.'); + } + } +} diff --git a/app/Http/Controllers/Api/VerificationController.php b/app/Http/Controllers/Api/VerificationController.php new file mode 100644 index 0000000..981c6d3 --- /dev/null +++ b/app/Http/Controllers/Api/VerificationController.php @@ -0,0 +1,62 @@ +user(); + + if ($user->hasVerifiedEmail() && !$user->pending_email) { + return redirect(config('app.frontend_url') . '/verify-success?already_verified=1'); + } + + // If there is a pending email, promote it to the main email + if ($user->pending_email) { + $user->email = $user->pending_email; + $user->pending_email = null; + } + + if ($user->markEmailAsVerified()) { + event(new Verified($user)); + } + + return redirect(config('app.frontend_url') . '/verify-success?verified=1'); + } + + /** + * Resend the email verification notification. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function resend(Request $request) + { + $user = $request->user(); + + if ($user->hasVerifiedEmail() && !$user->pending_email) { + return response()->json(['message' => 'Email already verified.'], 400); + } + + if ($user->pending_email) { + $user->notify(new \App\Notifications\PendingEmailVerificationNotification); + } else { + $user->sendEmailVerificationNotification(); + } + + return response()->json(['message' => 'Verification link sent.']); + } +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..749b3dc --- /dev/null +++ b/app/Http/Controllers/AuthController.php @@ -0,0 +1,377 @@ +validate([ + 'email' => ['required', 'email'], + 'password' => ['required'], + ]); + + $user = User::where('email', $request->email)->first(); + + if (!$user || !Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'email' => ['Invalid credentials.'], + ]); + } + + // 2FA Check + if ($user->two_factor_confirmed_at) { + // Return Temporary Token with "2fa" capability + // We DO NOT call Auth::login() here to prevent session cookie creation + $tempToken = $user->createToken('2fa_temp_token', ['2fa-required'])->plainTextToken; + + return response()->json([ + 'two_factor_required' => true, + 'temp_token' => $tempToken, + ]); + } + + // Standard Login - Establish session and issue token + Auth::guard('web')->login($user, $request->boolean('remember')); + + $token = $user->createToken('auth_token')->plainTextToken; + + $this->recordLoginHistory($request, $user); + $this->logActivity('login', 'User logged in to the dashboard'); + + return response()->json([ + 'message' => 'Login successful', + 'token' => $token, + 'user' => $user, + ]); + } + + /** + * Handle Registration Request + */ + public function register(Request $request) + { + $validated = $request->validate([ + 'fname' => 'required|string|max:255', + 'lname' => 'nullable|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'password' => 'required|string|min:8', + ]); + + $user = User::create([ + 'first_name' => $validated['fname'], + 'last_name' => $validated['lname'] ?? null, + 'email' => $validated['email'], + 'password' => Hash::make($validated['password']), + ]); + + Auth::login($user); + + $token = $user->createToken('auth_token')->plainTextToken; + + $this->recordLoginHistory($request, $user); + $this->logActivity('register', 'User registered a new account'); + + return response()->json([ + 'message' => 'Registration successful', + 'token' => $token, + 'user' => $user, + ]); + } + + /** + * Redirect to Social Provider + */ + public function socialRedirect(Request $request, $provider) + { + // Store context (signin, signup, connect) in session + $context = $request->query('context', 'signin'); // Default to signin if missing + session(['social_context' => $context]); + + // Secure Link Flow: If a link_token is provided, verify it and store the user ID in session + if ($context === 'connect' && $request->has('link_token')) { + $token = $request->query('link_token'); + $userId = Cache::get("link_token_{$token}"); + + if ($userId) { + session(['social_auth_user_id' => $userId]); + Cache::forget("link_token_{$token}"); // Consume token + } + } + + $driver = Socialite::driver($provider)->stateless(); + + if ($provider === 'google') { + $driver->with(['prompt' => 'select_account consent', 'access_type' => 'offline']); + } else { + // Attempt to force consent for others + $driver->with(['prompt' => 'consent']); + } + + return $driver->redirect(); + } + + /** + * Generate Link Token for Secure Connection + */ + public function getLinkToken(Request $request) + { + $token = Str::random(40); + // Cache user ID for 2 minutes + Cache::put("link_token_{$token}", $request->user()->id, 120); + + return response()->json(['token' => $token]); + } + + /** + * Handle Social Provider Callback + */ + public function socialCallback(Request $request, $provider) + { + try { + $socialUser = Socialite::driver($provider)->stateless()->user(); + } catch (\Exception $e) { + return redirect(config('app.frontend_url') . '/auth/callback?error=' . urlencode($e->getMessage())); + } + + $context = session('social_context', 'signin'); // Default to strict signin if session lost + // request()->session()->forget('social_context'); // Optional: Clear it, but typical session lifecycle handles this. + + // --------------------------------------------------------- + // CASE 1: CONNECT ACCOUNT (User is already logged in) + // --------------------------------------------------------- + // Explicitly check context or Auth::check() + // If context is 'connect', they MUST be logged in. + if ($context === 'connect' || Auth::check() || session('social_auth_user_id')) { + + // Debug Logging: Trace why connection flow might be failing + \Illuminate\Support\Facades\Log::info('Social Callback Connect Flow:', [ + 'context' => $context, + 'auth_check' => Auth::check(), + 'session_user_id' => session('social_auth_user_id'), + 'provider' => $provider + ]); + + // If strictly unauthenticated but we have a session user ID from the link token + if (!Auth::check() && session('social_auth_user_id')) { + Auth::loginUsingId(session('social_auth_user_id')); + } + + if (!Auth::check()) { + return redirect(config('app.frontend_url') . '/signin?error=login_required_to_connect'); + } + + $currentUser = Auth::user(); + + // Check if this social account is already linked to *any* user + $existingAccount = \App\Models\SocialAccount::where('provider', $provider) + ->where('provider_id', $socialUser->getId()) + ->first(); + + if ($existingAccount) { + if ($existingAccount->user_id === $currentUser->id) { + return redirect(config('app.frontend_url') . '/dashboard/settings?error=already_connected'); + } else { + return redirect(config('app.frontend_url') . '/dashboard/settings?error=connected_to_other_account'); + } + } + + // Link the account + $currentUser->socialAccounts()->create([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + 'provider_email' => $socialUser->getEmail(), + 'avatar' => $socialUser->getAvatar(), + 'token' => $socialUser->token, + 'refresh_token' => $socialUser->refreshToken, + 'expires_at' => isset($socialUser->expiresIn) ? now()->addSeconds($socialUser->expiresIn) : null, + ]); + + return redirect(config('app.frontend_url') . '/dashboard/settings?success=account_connected'); + } + + // --------------------------------------------------------- + // CASE 2: SOCIAL SIGN IN / SIGN UP (Guest) + // --------------------------------------------------------- + + // 1. Check if SocialAccount exists (Already Linked) + $socialAccount = \App\Models\SocialAccount::where('provider', $provider) + ->where('provider_id', $socialUser->getId()) + ->first(); + + if ($socialAccount) { + // Account linked -> ALWAYS ALLOW LOGIN + // (Even if context=signup, we can just log them in, or strictly say "Already registered") + $user = $socialAccount->user; + + // Auto-verify if not verified (Social provider already verified the email) + if (!$user->hasVerifiedEmail()) { + $user->markEmailAsVerified(); + } + + Auth::login($user); + + $token = $user->createToken('auth_token')->plainTextToken; + $this->recordLoginHistory($request, $user); + + return redirect(config('app.frontend_url') . '/auth/callback?token=' . $token); + } + + // 2. Check if User with this email exists (BUT NOT LINKED) + $existingUser = User::where('email', $socialUser->getEmail())->first(); + + if ($existingUser) { + // ERROR: Account exists but not linked + return redirect(config('app.frontend_url') . '/auth/callback?error=account_exists_please_login'); + } + + // 3. HANDLE NEW USERS + // STRICT CHECK: If context is 'signin', DO NOT register new user. + if ($context === 'signin') { + return redirect(config('app.frontend_url') . '/auth/callback?error=account_not_found_please_signup'); + } + + // 4. REGISTER (Only if context == 'signup') + $nameParts = explode(' ', $socialUser->getName() ?? '', 2); + $firstName = $nameParts[0]; + $lastName = $nameParts[1] ?? ''; + + $user = User::create([ + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email' => $socialUser->getEmail(), + 'avatar' => $socialUser->getAvatar(), + 'email_verified_at' => now(), + // Password is null initially + ]); + + $user->socialAccounts()->create([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + 'provider_email' => $socialUser->getEmail(), + 'avatar' => $socialUser->getAvatar(), + 'token' => $socialUser->token, + 'refresh_token' => $socialUser->refreshToken, + 'expires_at' => isset($socialUser->expiresIn) ? now()->addSeconds($socialUser->expiresIn) : null, + ]); + + Auth::login($user); + $token = $user->createToken('auth_token')->plainTextToken; + $this->recordLoginHistory($request, $user); + + // Redirect to Set Password page + return redirect(config('app.frontend_url') . '/auth/callback?token=' . $token . '&action=set_password'); + } + + /** + * Set Password for Social Users + */ + public function setPassword(Request $request) + { + $validated = $request->validate([ + 'password' => 'required|string|min:8|confirmed', + ]); + + $user = $request->user(); + + // Security check: Only allow setting password if it's currently null + // or provide a way to override if we want to allow simple password resets from this endpoint (unlikely for security) + if ($user->password && !Hash::check('', $user->password)) { // Check if password is not empty string/null effectively + // If user already has a password, they should use the update-password endpoint which requires current_password + // However, for this specific flow "set password after social login", we can allow it IF implementation allows. + // Stricter: Only if password is NULL. + } + + if ($user->password !== null && $user->password !== '') { + return response()->json(['message' => 'Password already set. Use update password.'], 403); + } + + $user->update([ + 'password' => Hash::make($validated['password']), + ]); + + return response()->json([ + 'message' => 'Password set successfully.', + 'user' => $user, + ]); + } + + /** + * Handle Logout Request + */ + public function logout(Request $request) + { + Auth::guard('web')->logout(); + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return response()->noContent(); + } + + /** + * Disconnect Social Account (Revoke Token) + */ + public function disconnectSocial(Request $request, $provider) + { + $user = $request->user(); + + $account = $user->socialAccounts()->where('provider', $provider)->first(); + + if (!$account) { + return response()->json(['message' => 'Account not linked.'], 404); + } + + // 1. Revoke Logic + try { + if ($provider === 'google' && $account->token) { + // Google: Revoke via POST to oauth2.googleapis.com + Http::post('https://oauth2.googleapis.com/revoke', [ + 'token' => $account->token, + ]); + } + elseif ($provider === 'github' && $account->token) { + // GitHub: Revoke via Basic Auth with Client ID/Secret + // Requires client_id:client_secret base64 encoded + $clientId = config('services.github.client_id'); + $clientSecret = config('services.github.client_secret'); + + if ($clientId && $clientSecret) { + Http::withBasicAuth($clientId, $clientSecret) + ->delete("https://api.github.com/applications/{$clientId}/grant", [ + 'access_token' => $account->token + ]); + } + } + } catch (\Exception $e) { + // Log error but proceed to delete local record + \Illuminate\Support\Facades\Log::error("Failed to revoke {$provider} token: " . $e->getMessage()); + } + + // 2. Delete Local Record + $account->delete(); + + return response()->json(['message' => 'Account disconnected successfully.']); + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +user(); + $menuGroups = []; + + // 1. Admin Management (Admin or Owner) + if ($user && $user->isAdminOrOwner()) { + $menuGroups[] = [ + 'title' => 'Admin Management', + 'items' => [ + [ + 'name' => 'User Management', + 'icon' => 'users', + 'route' => '/dashboard/admin/users', + ], + [ + 'name' => 'Root CA Management', + 'icon' => 'certificate', + 'route' => '/dashboard/admin/root-ca', + ], + [ + 'name' => 'Ticket Management', + 'icon' => 'support-ticket', + 'route' => '/dashboard/admin/tickets', + ], + [ + 'name' => 'Legal Page Management', + 'icon' => 'pages', + 'route' => '/dashboard/admin/legal', + ], + [ + 'name' => 'Inquiries', + 'icon' => 'inbox', + 'route' => '/dashboard/admin/inquiries', + ], + [ + 'name' => 'SMTP Tester', + 'icon' => 'smtp', + 'route' => '/dashboard/admin/smtp-tester', + ], + ] + ]; + } + + // 2. Main Menu (Common) + $mainItems = [ + [ + 'name' => 'Dashboard', + 'icon' => 'dashboard', + 'route' => '/dashboard', + ], + [ + 'name' => 'Certificates', + 'icon' => 'certificate', + 'route' => '/dashboard/certificates', + ], + [ + 'name' => 'API Keys', + 'icon' => 'api-key', + 'route' => '/dashboard/api-keys', + ], + [ + 'name' => 'Support Tickets', + 'icon' => 'support-ticket', + 'route' => '/dashboard/support', // Assuming support.index maps to /support + ], + ]; + + // "My Services" for Customers ONLY + if ($user && $user->role === \App\Models\User::ROLE_CUSTOMER) { + // We can insert "My Services" if we want to keep that feature for customers + // As per user request "ikuiti app-beta", but we also added "My Services" previously. + // Let's keep "My Services" as it's a nice dedicated page for them, inserting it after Dashboard. + array_splice($mainItems, 1, 0, [[ + 'name' => 'My Services', + 'icon' => 'layers', + 'route' => '/dashboard/services', + ]]); + } + + $menuGroups[] = [ + 'title' => 'Menu', + 'items' => $mainItems, + ]; + + // 3. My Account (Common) + $menuGroups[] = [ + 'title' => 'My Account', + 'items' => [ + [ + 'name' => 'User Profile', + 'icon' => 'user-profile', + 'route' => '/dashboard/profile', + ], + [ + 'name' => 'Account Settings', + 'icon' => 'settings', + 'route' => '/dashboard/settings', + ], + ] + ]; + + return response()->json($menuGroups); + } + + public function debug() + { + // Simulate a User instance for admin view + $user = new \App\Models\User(['first_name' => 'Debug', 'last_name' => 'Admin', 'role' => 'admin']); + + // This is a bit of a hack since $user->isAdmin() might be a real method, + // but for JSON structure debugging, we'll just replicate the logic or mock it. + + $menuGroups = []; + + // 1. Admin Management (Simulated Admin) + $menuGroups[] = [ + 'title' => 'Admin Management', + 'items' => [ + ['name' => 'User Management', 'icon' => 'users', 'route' => '/admin/users'], + ['name' => 'Root CA Management', 'icon' => 'certificate', 'route' => '/admin/root-ca'], + ['name' => 'Ticket Management', 'icon' => 'support-ticket', 'route' => '/admin/tickets'], + ['name' => 'Legal Page Management', 'icon' => 'pages', 'route' => '/dashboard/admin/legal'], + ['name' => 'Inquiries', 'icon' => 'inbox', 'route' => '/dashboard/admin/inquiries'], + ['name' => 'SMTP Tester', 'icon' => 'smtp', 'route' => '/dashboard/admin/smtp-tester'], + ] + ]; + + // 2. Main Menu + $mainItems = [ + ['name' => 'Dashboard', 'icon' => 'dashboard', 'route' => '/dashboard'], + ['name' => 'Certificates', 'icon' => 'certificate', 'route' => '/dashboard/certificates'], + ['name' => 'API Keys', 'icon' => 'api-key', 'route' => '/dashboard/api-keys'], + ['name' => 'Support Tickets', 'icon' => 'support-ticket', 'route' => '/dashboard/support'], + ]; + + $menuGroups[] = [ + 'title' => 'Menu', + 'items' => $mainItems, + ]; + + // 3. My Account + $menuGroups[] = [ + 'title' => 'My Account', + 'items' => [ + ['name' => 'User Profile', 'icon' => 'user-profile', 'route' => '/dashboard/profile'], + ['name' => 'Account Settings', 'icon' => 'settings', 'route' => '/dashboard/settings'], + ] + ]; + + return response()->json($menuGroups); + } +} diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php new file mode 100644 index 0000000..f9dbdb9 --- /dev/null +++ b/app/Http/Controllers/ServiceController.php @@ -0,0 +1,35 @@ +json([ + [ + 'id' => 1, + 'name' => 'SSL Certificate - Standard', + 'status' => 'Active', + 'expiry' => '2026-12-23', + 'domain' => 'example.com' + ], + [ + 'id' => 2, + 'name' => 'Code Signing - Pro', + 'status' => 'Pending', + 'expiry' => 'N/A', + 'domain' => 'N/A' + ], + [ + 'id' => 3, + 'name' => 'Wildcard SSL', + 'status' => 'Expired', + 'expiry' => '2025-01-10', + 'domain' => '*.web.dev' + ] + ]); + } +} diff --git a/app/Http/Controllers/TwoFactorController.php b/app/Http/Controllers/TwoFactorController.php new file mode 100644 index 0000000..de737a6 --- /dev/null +++ b/app/Http/Controllers/TwoFactorController.php @@ -0,0 +1,195 @@ +user(); + + if ($user->two_factor_confirmed_at) { + return response()->json(['message' => '2FA is already enabled.'], 400); + } + + $google2fa = new Google2FA(); + + // Generate secret if not exists or if re-enabling + $secret = $google2fa->generateSecretKey(); + + // Save encrypted secret (or plain if you trust your db, but encrypted is better) + // For simplicity with this library, it often expects raw secret. + // We will store it encrypted but decrypt it when needed if we were using a trait + // But for manual implementation, lets store it temporarily in the session OR save it to DB directly? + // Let's save it to DB but encrypt it. + + $user->forceFill([ + 'two_factor_secret' => encrypt($secret), + 'two_factor_recovery_codes' => null, // Reset codes + ])->save(); + + // Generate QR Code Object + $qrCodeUrl = $google2fa->getQRCodeInline( + config('app.name'), + $user->email, + $secret + ); + + return response()->json([ + 'secret' => $secret, + 'qr_code' => $qrCodeUrl, + ]); + } + + /** + * Confirm 2FA: Verify initial OTP + */ + public function confirm(Request $request) + { + $request->validate(['code' => 'required|string|size:6']); + + $user = $request->user(); + $google2fa = new Google2FA(); + + try { + $secret = decrypt($user->two_factor_secret); + } catch (\Exception $e) { + return response()->json(['message' => '2FA not initiated.'], 400); + } + + $valid = $google2fa->verifyKey($secret, $request->code); + + if (!$valid) { + throw ValidationException::withMessages([ + 'code' => ['Invalid 2FA code.'], + ]); + } + + // Generate Recovery Codes + $recoveryCodes = []; + for ($i = 0; $i < 8; $i++) { + $recoveryCodes[] = \Illuminate\Support\Str::random(10) . '-' . \Illuminate\Support\Str::random(10); + } + + $user->forceFill([ + 'two_factor_confirmed_at' => now(), + 'two_factor_recovery_codes' => encrypt(json_encode($recoveryCodes)), + ])->save(); + + return response()->json([ + 'message' => '2FA enabled successfully.', + 'recovery_codes' => $recoveryCodes, + ]); + } + + /** + * Disable 2FA + */ + public function disable(Request $request) + { + $request->validate(['password' => 'required']); + + $user = $request->user(); + + if (!Hash::check($request->password, $user->password)) { + throw ValidationException::withMessages([ + 'password' => ['Invalid password.'], + ]); + } + + $user->forceFill([ + 'two_factor_secret' => null, + 'two_factor_recovery_codes' => null, + 'two_factor_confirmed_at' => null, + ])->save(); + + return response()->json(['message' => '2FA disabled successfully.']); + } + /** + * Verify 2FA during Login Challenge + */ + public function verify(Request $request) + { + $request->validate(['code' => 'required|string']); + + $user = $request->user(); + + // Check 2FA Secret + try { + $secret = decrypt($user->two_factor_secret); + } catch (\Exception $e) { + return response()->json(['message' => '2FA configuration error.'], 500); + } + + $google2fa = new Google2FA(); + $valid = $google2fa->verifyKey($secret, $request->code); + + // Check Recovery Code if TOTP failed + if (!$valid) { + $recoveryCodes = $user->two_factor_recovery_codes ? json_decode(decrypt($user->two_factor_recovery_codes), true) : []; + + if (in_array($request->code, $recoveryCodes)) { + $valid = true; + // Remove used recovery code + $recoveryCodes = array_diff($recoveryCodes, [$request->code]); + $user->forceFill([ + 'two_factor_recovery_codes' => encrypt(json_encode(array_values($recoveryCodes))), + ])->save(); + } + } + + if (!$valid) { + throw ValidationException::withMessages([ + 'code' => ['Invalid code provided.'], + ]); + } + + // Success! + // 1. Establish session (for web/inertia/sanctum cookie flows) + Auth::guard('web')->login($user, $request->boolean('remember')); + + // 2. Revoke the temp token + if ($user->currentAccessToken()) { + $user->currentAccessToken()->delete(); + } + + // 3. Create new full access token + $token = $user->createToken('auth_token')->plainTextToken; + + // 4. Record History + $this->recordLoginHistory($request, $user); + + return response()->json([ + 'message' => 'Login successful', + 'token' => $token, + 'user' => $user + ]); + } + + /** + * Show Recovery Codes + */ + public function recoveryCodes(Request $request) + { + if (!$request->user()->two_factor_confirmed_at) { + return response()->json(['message' => '2FA not enabled.'], 400); + } + + $codes = json_decode(decrypt($request->user()->two_factor_recovery_codes), true); + + return response()->json(['recovery_codes' => $codes]); + } +} diff --git a/app/Http/Middleware/AdminMiddleware.php b/app/Http/Middleware/AdminMiddleware.php new file mode 100644 index 0000000..fe35337 --- /dev/null +++ b/app/Http/Middleware/AdminMiddleware.php @@ -0,0 +1,24 @@ +user() || !$request->user()->isAdminOrOwner()) { + return response()->json(['message' => 'Unauthorized. Admin access required.'], 403); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/CheckApiKey.php b/app/Http/Middleware/CheckApiKey.php new file mode 100644 index 0000000..b3ec072 --- /dev/null +++ b/app/Http/Middleware/CheckApiKey.php @@ -0,0 +1,57 @@ +header('TRUSTLAB_API_KEY'); + $keyString = $headerValue ? trim($headerValue) : null; + + if (!$keyString) { + return response()->json([ + 'success' => false, + 'message' => 'API Key is missing. Please provide it in the TRUSTLAB_API_KEY header.' + ], 401); + } + + $apiKey = ApiKey::where('key', $keyString)->first(); + + if (!$apiKey || !$apiKey->is_active) { + return response()->json([ + 'success' => false, + 'message' => 'Invalid or inactive API Key.' + ], 401); + } + + // Update last used timestamp + $apiKey->update(['last_used_at' => now()]); + + // Optional: Dispatch stats update event if needed in the future + // \App\Events\DashboardStatsUpdated::dispatch($apiKey->user_id); + + // Put the user in the request context + $user = $apiKey->user; + $request->merge(['authenticated_user' => $user]); + $request->setUserResolver(fn () => $user); + + if ($user) { + Auth::setUser($user); + } + + return $next($request); + } +} diff --git a/app/Mail/CertificateExpiredMail.php b/app/Mail/CertificateExpiredMail.php new file mode 100644 index 0000000..7d7c764 --- /dev/null +++ b/app/Mail/CertificateExpiredMail.php @@ -0,0 +1,55 @@ +certificate = $certificate; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'URGENT: Certificate [' . $this->certificate->common_name . '] Has Expired', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + view: 'emails.certificate-expired', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/CertificateExpiringMail.php b/app/Mail/CertificateExpiringMail.php new file mode 100644 index 0000000..2e458db --- /dev/null +++ b/app/Mail/CertificateExpiringMail.php @@ -0,0 +1,57 @@ +certificate = $certificate; + $this->daysRemaining = $daysRemaining; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Action Required: Certificate [' . $this->certificate->common_name . '] Expiring in ' . $this->daysRemaining . ' Days', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + view: 'emails.certificate-expiring', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/InquiryReplyMail.php b/app/Mail/InquiryReplyMail.php new file mode 100644 index 0000000..c383002 --- /dev/null +++ b/app/Mail/InquiryReplyMail.php @@ -0,0 +1,55 @@ +inquiry = $inquiry; + $this->replyMessage = $replyMessage; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Re: ' . $this->inquiry->subject, + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + view: 'emails.inquiry_reply', + ); + } + + /** + * Get the attachments for the message. + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Mail/TestMail.php b/app/Mail/TestMail.php new file mode 100644 index 0000000..85c8a64 --- /dev/null +++ b/app/Mail/TestMail.php @@ -0,0 +1,57 @@ +mailer = $mailer; + $this->host = $host; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: '[TrustLab] SMTP Connection Test Successful', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + view: 'emails.test', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php new file mode 100644 index 0000000..6c98cf2 --- /dev/null +++ b/app/Models/ActivityLog.php @@ -0,0 +1,36 @@ +id)) { + $model->id = UuidHelper::generate(); + } + }); + } + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/ApiKey.php b/app/Models/ApiKey.php new file mode 100644 index 0000000..fccc285 --- /dev/null +++ b/app/Models/ApiKey.php @@ -0,0 +1,55 @@ +id)) { + $model->id = \App\Helpers\UuidHelper::generate(); + } + }); + } + + protected $fillable = [ + 'user_id', + 'name', + 'key', + 'last_used_at', + 'is_active', + ]; + + protected $casts = [ + 'last_used_at' => 'datetime', + 'is_active' => 'boolean', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } + + /** + * Generate a unique API Key. + */ + public static function generate() + { + do { + $key = 'dvp_' . Str::random(56); // Prefix (4) + Random (56) = 60 chars + } while (static::where('key', $key)->exists()); + + return $key; + } +} diff --git a/app/Models/CaCertificate.php b/app/Models/CaCertificate.php new file mode 100644 index 0000000..8c69c01 --- /dev/null +++ b/app/Models/CaCertificate.php @@ -0,0 +1,42 @@ + 'datetime', + 'valid_to' => 'datetime', + 'last_downloaded_at' => 'datetime', + ]; + + protected static function booted() + { + static::creating(function ($model) { + if (empty($model->uuid)) { + $model->uuid = UuidHelper::generate(); + } + }); + } +} diff --git a/app/Models/Certificate.php b/app/Models/Certificate.php new file mode 100644 index 0000000..0f2b736 --- /dev/null +++ b/app/Models/Certificate.php @@ -0,0 +1,82 @@ + 'datetime', + 'valid_to' => 'datetime', + 'expired_notification_sent_at' => 'datetime', + ]; + + protected $hidden = [ + 'expired_notification_sent_at', + ]; + + protected $appends = [ + 'ssl_status', + ]; + + protected static function booted() + { + static::creating(function ($model) { + if (empty($model->uuid)) { + $model->uuid = UuidHelper::generate(); + } + }); + } + + public function user() + { + return $this->belongsTo(User::class); + } + + public function getSslStatusAttribute() + { + if ($this->valid_to && $this->valid_to->isPast()) { + return 'EXPIRED'; + } + return 'ACTIVE'; + } + + public function toArray() + { + $array = parent::toArray(); + $ordered = []; + + // Define the preferred order of keys + $paramOrder = [ + 'uuid', 'user_id', 'common_name', 'organization', 'locality', + 'state', 'country', 'san', 'status', 'key_bits', 'serial_number', + 'ssl_status', // <--- Inserted here (before content) + 'cert_content', 'key_content', 'csr_content', + 'valid_from', 'valid_to', 'created_at', 'updated_at' + ]; + + // Reconstruct query based on paramOrder + foreach ($paramOrder as $key) { + if (array_key_exists($key, $array)) { + $ordered[$key] = $array[$key]; + unset($array[$key]); + } + } + + // Append any remaining keys (that weren't in our explicit list) + return array_merge($ordered, $array); + } +} diff --git a/app/Models/Inquiry.php b/app/Models/Inquiry.php new file mode 100644 index 0000000..39f203f --- /dev/null +++ b/app/Models/Inquiry.php @@ -0,0 +1,38 @@ + 'datetime', + ]; + + protected static function booted() + { + static::creating(function ($model) { + if (empty($model->{$model->getKeyName()})) { + $model->{$model->getKeyName()} = \App\Helpers\UuidHelper::generate(); + } + }); + } +} diff --git a/app/Models/LegalPage.php b/app/Models/LegalPage.php new file mode 100644 index 0000000..92f723a --- /dev/null +++ b/app/Models/LegalPage.php @@ -0,0 +1,32 @@ +hasMany(LegalPageRevision::class); + } + + public function latestRevision() + { + return $this->hasOne(LegalPageRevision::class)->latestOfMany('created_at'); + } + + public function currentRevision() + { + return $this->hasOne(LegalPageRevision::class)->where('is_active', true)->latest(); + } +} diff --git a/app/Models/LegalPageRevision.php b/app/Models/LegalPageRevision.php new file mode 100644 index 0000000..15de683 --- /dev/null +++ b/app/Models/LegalPageRevision.php @@ -0,0 +1,35 @@ +major}.{$this->minor}.{$this->patch}"; + } + + public function legalPage() + { + return $this->belongsTo(LegalPage::class); + } +} diff --git a/app/Models/LoginHistory.php b/app/Models/LoginHistory.php new file mode 100644 index 0000000..e9856ce --- /dev/null +++ b/app/Models/LoginHistory.php @@ -0,0 +1,41 @@ +id)) { + $model->id = \App\Helpers\UuidHelper::generate(); + } + }); + } + + protected $fillable = [ + 'user_id', + 'ip_address', + 'user_agent', + 'device_type', + 'os', + 'browser', + 'city', + 'country', + 'country_code', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/SocialAccount.php b/app/Models/SocialAccount.php new file mode 100644 index 0000000..8ec53f8 --- /dev/null +++ b/app/Models/SocialAccount.php @@ -0,0 +1,40 @@ +id)) { + $model->id = \App\Helpers\UuidHelper::generate(); + } + }); + } + protected $fillable = [ + 'user_id', + 'provider', + 'provider_id', + 'provider_email', + 'avatar', + 'token', + 'refresh_token', + 'expires_at', + ]; + + protected $casts = [ + 'expires_at' => 'datetime', + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php new file mode 100644 index 0000000..46190e2 --- /dev/null +++ b/app/Models/Ticket.php @@ -0,0 +1,45 @@ +belongsTo(User::class); + } + + /** + * Get the replies for the ticket. + */ + public function replies() + { + return $this->hasMany(TicketReply::class); + } + + /** + * Helper to generate a unique ticket number. + */ + public static function generateTicketNumber() + { + return 'TKT-' . strtoupper(bin2hex(random_bytes(3))); + } +} diff --git a/app/Models/TicketAttachment.php b/app/Models/TicketAttachment.php new file mode 100644 index 0000000..c9a36be --- /dev/null +++ b/app/Models/TicketAttachment.php @@ -0,0 +1,25 @@ +belongsTo(TicketReply::class, 'ticket_reply_id'); + } +} diff --git a/app/Models/TicketReply.php b/app/Models/TicketReply.php new file mode 100644 index 0000000..8b1f93f --- /dev/null +++ b/app/Models/TicketReply.php @@ -0,0 +1,40 @@ +belongsTo(Ticket::class); + } + + /** + * Get the user that wrote the reply. + */ + public function user() + { + return $this->belongsTo(User::class); + } + + public function attachments() + { + return $this->hasMany(TicketAttachment::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..b77d59c --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,189 @@ + */ + use HasApiTokens, HasFactory, Notifiable; + + public const ROLE_OWNER = 'owner'; + public const ROLE_ADMIN = 'admin'; + public const ROLE_CUSTOMER = 'customer'; + + /** + * The channels the user receives notification broadcasts on. + */ + public function receivesBroadcastNotificationsOn(): string + { + return 'App.Models.User.' . $this->id; + } + + /** + * Get the API keys for the user. + */ + public function apiKeys() + { + return $this->hasMany(ApiKey::class); + } + + /** + * Get the login history for the user. + */ + public function loginHistories() + { + return $this->hasMany(LoginHistory::class); + } + + /** + * Get the tickets for the user. + */ + public function tickets() + { + return $this->hasMany(Ticket::class); + } + + public $incrementing = false; + protected $keyType = 'string'; + + protected static function booted() + { + static::creating(function ($model) { + if (empty($model->{$model->getKeyName()})) { + $model->{$model->getKeyName()} = \App\Helpers\UuidHelper::generate(); + } + }); + } + + /** + * The attributes that are mass assignable. + * + * @var list + */ + protected $fillable = [ + 'first_name', + 'last_name', + 'email', + 'pending_email', + 'password', + 'avatar', + 'role', + 'phone', + 'bio', + 'job_title', + 'location', + 'country', + 'city_state', + 'postal_code', + 'tax_id', + 'settings_email_alerts', + 'settings_certificate_renewal', + 'default_landing_page', + 'theme', + 'language', + 'email_verified_at', + ]; + + /** + * Get the social accounts for the user. + */ + public function socialAccounts() + { + return $this->hasMany(SocialAccount::class); + } + + /** + * Check if user is owner. + */ + public function isOwner(): bool + { + return $this->role === self::ROLE_OWNER; + } + + /** + * Check if user is admin. + */ + public function isAdmin(): bool + { + return $this->role === self::ROLE_ADMIN; + } + + /** + * Check if user is admin or owner. + */ + public function isAdminOrOwner(): bool + { + return in_array($this->role, [self::ROLE_OWNER, self::ROLE_ADMIN]); + } + + /** + * Get the avatar URL with cache busting timestamp. + */ + public function getAvatarAttribute($value) + { + if (!$value) return $value; + + // If it's already a full R2 URL, append timestamp + if (str_contains($value, 'cdn.trustlab.dyzulk.com')) { + $separator = str_contains($value, '?') ? '&' : '?'; + return $value . $separator . 't=' . ($this->updated_at ? $this->updated_at->timestamp : time()); + } + + return $value; + } + + /** + * The attributes that should be hidden for serialization. + * + * @var list + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + 'settings_email_alerts' => 'boolean', + 'settings_certificate_renewal' => 'boolean', + ]; + } + /** + * Send the email verification notification. + * + * @return void + */ + public function sendEmailVerificationNotification() + { + $this->notify(new \App\Notifications\VerifyEmailNotification); + } + + /** + * Route notifications for the mail channel. + * + * @param \Illuminate\Notifications\Notification $notification + * @return array|string + */ + public function routeNotificationForMail($notification) + { + if ($notification instanceof \App\Notifications\PendingEmailVerificationNotification) { + return $this->pending_email; + } + + return $this->email; + } +} diff --git a/app/Notifications/CertificateExpiringNotification.php b/app/Notifications/CertificateExpiringNotification.php new file mode 100644 index 0000000..72eb4a8 --- /dev/null +++ b/app/Notifications/CertificateExpiringNotification.php @@ -0,0 +1,68 @@ +certificate = $certificate; + $this->daysRemaining = $daysRemaining; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + $channels = ['database']; + + if ($this->daysRemaining < 7) { + $channels[] = 'mail'; + } + + return $channels; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail($notifiable) + { + return (new \App\Mail\CertificateExpiringMail($this->certificate, $this->daysRemaining)) + ->to($notifiable->email); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toDatabase($notifiable) + { + return [ + 'type' => 'certificate_expiring', + 'certificate_id' => $this->certificate->id, + 'common_name' => $this->certificate->common_name, + 'days_remaining' => $this->daysRemaining, + 'valid_to' => $this->certificate->valid_to, + 'message' => "Certificate '{$this->certificate->common_name}' expires in {$this->daysRemaining} days.", + ]; + } +} diff --git a/app/Notifications/CertificateNotification.php b/app/Notifications/CertificateNotification.php new file mode 100644 index 0000000..921b681 --- /dev/null +++ b/app/Notifications/CertificateNotification.php @@ -0,0 +1,76 @@ +certificate = $certificate; + $this->action = $action; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + return ['database', 'broadcast']; + } + + + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toDatabase($notifiable) + { + $title = $this->action === 'issued' ? 'Certificate Issued' : 'Certificate Revoked'; + $message = $this->action === 'issued' + ? "New certificate for {$this->certificate->common_name} has been issued." + : "Certificate for {$this->certificate->common_name} has been revoked."; + + return [ + 'certificate_id' => $this->certificate->getKey(), + 'common_name' => $this->certificate->common_name, + 'title' => $title, + 'message' => $message, + 'type' => get_class($this), + 'icon' => $this->action === 'issued' ? 'check-circle' : 'trash-2', + 'url' => '/dashboard/certificates', + 'sender_name' => null, + 'sender_avatar' => null, + ]; + } + + public function toBroadcast($notifiable) + { + $db = $this->toDatabase($notifiable); + + return new BroadcastMessage([ + 'title' => $db['title'], + 'message' => $db['message'], + 'url' => $db['url'], + 'type' => get_class($this), + 'data' => $db + ]); + } +} diff --git a/app/Notifications/NewInquiryNotification.php b/app/Notifications/NewInquiryNotification.php new file mode 100644 index 0000000..3348e2f --- /dev/null +++ b/app/Notifications/NewInquiryNotification.php @@ -0,0 +1,82 @@ +inquiry = $inquiry; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + return ['database', 'broadcast']; + } + + + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray($notifiable) + { + return $this->toDatabase($notifiable); + } + + /** + * Get the database representation of the notification. + */ + public function toDatabase($notifiable) + { + return [ + 'inquiry_id' => $this->inquiry->id, + 'name' => $this->inquiry->name, + 'email' => $this->inquiry->email, + 'subject' => $this->inquiry->subject, + 'title' => 'New Inquiry: ' . $this->inquiry->subject, + 'message' => 'Received from ' . $this->inquiry->name . ' (' . $this->inquiry->email . ')', + 'type' => get_class($this), + 'icon' => 'inbox', + 'url' => '/dashboard/admin/inquiries', + 'sender_name' => $this->inquiry->name, + 'sender_avatar' => null, + ]; + } + + /** + * Get the broadcastable representation of the notification. + */ + public function toBroadcast($notifiable) + { + $db = $this->toDatabase($notifiable); + return new BroadcastMessage([ + 'title' => $db['title'], + 'message' => $db['message'], + 'url' => $db['url'], + 'type' => get_class($this), + 'data' => $db + ]); + } +} diff --git a/app/Notifications/NewTicketNotification.php b/app/Notifications/NewTicketNotification.php new file mode 100644 index 0000000..37d00a3 --- /dev/null +++ b/app/Notifications/NewTicketNotification.php @@ -0,0 +1,85 @@ +ticket = $ticket; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + return ['database', 'broadcast']; + } + + + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray($notifiable) + { + return $this->toDatabase($notifiable); + } + + /** + * Get the database representation of the notification. + */ + public function toDatabase($notifiable) + { + $user = $this->ticket->user; + $userName = $user ? trim($user->first_name . ' ' . $user->last_name) : 'Unknown User'; + if (empty($userName)) $userName = 'Unknown User'; + + return [ + 'ticket_id' => $this->ticket->id, + 'ticket_number' => $this->ticket->ticket_number, + 'subject' => $this->ticket->subject, + 'title' => 'New Ticket #' . $this->ticket->ticket_number, + 'message' => "Subject: {$this->ticket->subject}. From: {$userName}", + 'sender_name' => $userName, + 'sender_avatar' => $user ? $user->avatar : null, + 'type' => get_class($this), + 'icon' => 'support-ticket', + 'url' => '/dashboard/admin/tickets/view?id=' . $this->ticket->id, + ]; + } + + /** + * Get the broadcastable representation of the notification. + */ + public function toBroadcast($notifiable) + { + $db = $this->toDatabase($notifiable); + + return new BroadcastMessage([ + 'title' => $db['title'], + 'message' => $db['message'], + 'url' => $db['url'], + 'type' => get_class($this), + 'data' => $db + ]); + } +} diff --git a/app/Notifications/PendingEmailVerificationNotification.php b/app/Notifications/PendingEmailVerificationNotification.php new file mode 100644 index 0000000..4a307f4 --- /dev/null +++ b/app/Notifications/PendingEmailVerificationNotification.php @@ -0,0 +1,50 @@ +verificationUrl($notifiable); + + return (new MailMessage) + ->subject(Lang::get('Verify Your New Email Address')) + ->view('emails.verify-email', [ + 'name' => $notifiable->first_name . ' ' . $notifiable->last_name, + 'url' => $verificationUrl, + ]); + } + + /** + * Get the verification URL for the given notifiable. + * + * @param mixed $notifiable + * @return string + */ + protected function verificationUrl($notifiable) + { + return parent::verificationUrl($notifiable); + } + + /** + * Set the recipient to the pending email. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable): array + { + return ['mail']; + } +} diff --git a/app/Notifications/TicketReplyNotification.php b/app/Notifications/TicketReplyNotification.php new file mode 100644 index 0000000..501d2bd --- /dev/null +++ b/app/Notifications/TicketReplyNotification.php @@ -0,0 +1,96 @@ +ticket = $ticket; + $this->reply = $reply; + $this->byAdmin = $byAdmin; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + return ['database', 'broadcast']; + } + + + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray($notifiable) + { + return $this->toDatabase($notifiable); + } + + /** + * Get the database representation of the notification. + */ + public function toDatabase($notifiable) + { + $sender = $this->reply->user; + $senderName = $sender ? trim($sender->first_name . ' ' . $sender->last_name) : 'Support Team'; + if (empty($senderName)) $senderName = 'Support Team'; + + // URL determines based on recipient role + $url = ($notifiable->role === 'admin') + ? '/dashboard/admin/tickets/view?id=' . $this->ticket->id + : '/dashboard/support/view?id=' . $this->ticket->id; + + return [ + 'ticket_id' => $this->ticket->id, + 'reply_id' => $this->reply->id, + 'ticket_number' => $this->ticket->ticket_number, + 'title' => 'Reply to Ticket #' . $this->ticket->ticket_number, + 'message' => $this->byAdmin + ? 'Support replied: ' . $this->ticket->subject + : $senderName . ' replied: ' . $this->ticket->ticket_number, + 'sender_name' => $senderName, + 'sender_avatar' => $sender ? $sender->avatar : null, + 'type' => get_class($this), + 'icon' => 'support-ticket', + 'url' => $url, + ]; + } + + /** + * Get the broadcastable representation of the notification. + */ + public function toBroadcast($notifiable) + { + $db = $this->toDatabase($notifiable); + + return new BroadcastMessage([ + 'title' => $db['title'], + 'message' => $db['message'], + 'url' => $db['url'], + 'type' => get_class($this), + 'data' => $db + ]); + } +} diff --git a/app/Notifications/VerifyEmailNotification.php b/app/Notifications/VerifyEmailNotification.php new file mode 100644 index 0000000..7e12008 --- /dev/null +++ b/app/Notifications/VerifyEmailNotification.php @@ -0,0 +1,28 @@ +verificationUrl($notifiable); + + return (new MailMessage) + ->subject(Lang::get('Verify Email Address')) + ->view('emails.verify-email', [ + 'name' => $notifiable->first_name . ' ' . $notifiable->last_name, + 'url' => $verificationUrl, + ]); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ + 0) { + return false; + } + + $rootConfig = Config::get('openssl.ca_root'); + $int4096Config = Config::get('openssl.ca_4096'); + $int2048Config = Config::get('openssl.ca_2048'); + + // Create a basic temporary openssl config for CA extensions + $configContent = "[req]\ndistinguished_name = req\n[v3_ca]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign"; + $configFile = tempnam(sys_get_temp_dir(), 'ca_conf_'); + file_put_contents($configFile, $configContent); + + try { + // Root CA (4096-bit) + $rootKey = openssl_pkey_new([ + 'private_key_bits' => 4096, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$rootKey) throw new \Exception('Failed to generate Root Key: ' . openssl_error_string()); + + $rootCsr = openssl_csr_new($rootConfig, $rootKey, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$rootCsr) throw new \Exception('Failed to generate Root CSR: ' . openssl_error_string()); + + // Generate a random serial + $serial = $this->generateSerialNumber(); + + $rootCert = openssl_csr_sign($rootCsr, null, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $serial); + if (!$rootCert) throw new \Exception('Failed to sign Root Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($rootCert, $rootCertPem)) throw new \Exception('Failed to export Root Cert'); + if (!openssl_pkey_export($rootKey, $rootKeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Root Key'); + + $rootDetails = openssl_x509_parse($rootCertPem); + + // Prefer serialNumberHex if available (PHP 8.0+) + $serialHex = isset($rootDetails['serialNumberHex']) + ? $this->formatHex($rootDetails['serialNumberHex']) + : $this->formatSerialToHex($rootDetails['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'root', + 'cert_content' => $rootCertPem, + 'key_content' => $rootKeyPem, + 'serial_number' => $serialHex, + 'common_name' => $rootDetails['subject']['CN'] ?? 'Root CA', + 'organization' => $rootDetails['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $rootDetails['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $rootDetails['validTo_time_t']), + ]); + + // Intermediate CA 4096-bit + $int4096Key = openssl_pkey_new([ + 'private_key_bits' => 4096, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$int4096Key) throw new \Exception('Failed to generate Int-4096 Key: ' . openssl_error_string()); + + $int4096Csr = openssl_csr_new($int4096Config, $int4096Key, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$int4096Csr) throw new \Exception('Failed to generate Int-4096 CSR: ' . openssl_error_string()); + + $int4096Cert = openssl_csr_sign($int4096Csr, $rootCert, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $this->generateSerialNumber()); + if (!$int4096Cert) throw new \Exception('Failed to sign Int-4096 Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($int4096Cert, $int4096CertPem)) throw new \Exception('Failed to export Int-4096 Cert'); + if (!openssl_pkey_export($int4096Key, $int4096KeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Int-4096 Key'); + + $int4096Details = openssl_x509_parse($int4096CertPem); + $serialHex4096 = isset($int4096Details['serialNumberHex']) + ? $this->formatHex($int4096Details['serialNumberHex']) + : $this->formatSerialToHex($int4096Details['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'intermediate_4096', + 'cert_content' => $int4096CertPem, + 'key_content' => $int4096KeyPem, + 'serial_number' => $serialHex4096, + 'common_name' => $int4096Details['subject']['CN'] ?? 'Intermediate CA 4096', + 'organization' => $int4096Details['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $int4096Details['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $int4096Details['validTo_time_t']), + ]); + + // Intermediate CA 2048-bit + $int2048Key = openssl_pkey_new([ + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$int2048Key) throw new \Exception('Failed to generate Int-2048 Key: ' . openssl_error_string()); + + $int2048Csr = openssl_csr_new($int2048Config, $int2048Key, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$int2048Csr) throw new \Exception('Failed to generate Int-2048 CSR: ' . openssl_error_string()); + + $int2048Cert = openssl_csr_sign($int2048Csr, $rootCert, $rootKey, 10950, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $this->generateSerialNumber()); + if (!$int2048Cert) throw new \Exception('Failed to sign Int-2048 Cert: ' . openssl_error_string()); + + if (!openssl_x509_export($int2048Cert, $int2048CertPem)) throw new \Exception('Failed to export Int-2048 Cert'); + if (!openssl_pkey_export($int2048Key, $int2048KeyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Int-2048 Key'); + + $int2048Details = openssl_x509_parse($int2048CertPem); + $serialHex2048 = isset($int2048Details['serialNumberHex']) + ? $this->formatHex($int2048Details['serialNumberHex']) + : $this->formatSerialToHex($int2048Details['serialNumber']); + + CaCertificate::create([ + 'ca_type' => 'intermediate_2048', + 'cert_content' => $int2048CertPem, + 'key_content' => $int2048KeyPem, + 'serial_number' => $serialHex2048, + 'common_name' => $int2048Details['subject']['CN'] ?? 'Intermediate CA 2048', + 'organization' => $int2048Details['subject']['O'] ?? null, + 'valid_from' => date('Y-m-d H:i:s', $int2048Details['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $int2048Details['validTo_time_t']), + ]); + + return true; + } finally { + if (file_exists($configFile)) unlink($configFile); + } + } + + /** + * Generate a domain certificate (Leaf). + */ + public function generateLeaf($data) + { + $keyBits = $data['key_bits'] ?? 2048; + $issuerType = (int)$keyBits === 4096 ? 'intermediate_4096' : 'intermediate_2048'; + + $intermediate = CaCertificate::where('ca_type', $issuerType)->first(); + if (!$intermediate) { + throw new \Exception("Intermediate CA ({$issuerType}) not found. Please setup CA first."); + } + + $dn = [ + "countryName" => $data['country'], + "stateOrProvinceName" => $data['state'], + "localityName" => $data['locality'], + "organizationName" => $data['organization'], + "commonName" => $data['common_name'] + ]; + + $cn = $data['common_name']; + $userSan = $data['san'] ?? ''; + + // Parse user input: split by comma, trim, filter empty + $entries = array_filter(array_map('trim', explode(',', $userSan))); + + // Always include CN as the first DNS entry + array_unshift($entries, $cn); + + $sanArray = array_unique(array_map(function($entry) { + if (str_starts_with($entry, 'IP:') || str_starts_with($entry, 'DNS:')) { + return $entry; + } + return filter_var($entry, FILTER_VALIDATE_IP) ? "IP:$entry" : "DNS:$entry"; + }, $entries)); + + $sanString = implode(', ', $sanArray); + + $configFile = null; + try { + $configContent = "[req]\ndistinguished_name = req\nreq_extensions = v3_req\nprompt = no\n[req_distinguished_name]\nCN = $cn\n[v3_req]\nsubjectAltName = $sanString"; + $configFile = tempnam(sys_get_temp_dir(), 'openssl_'); + file_put_contents($configFile, $configContent); + + $keyBits = $data['key_bits'] ?? 2048; + $privKey = openssl_pkey_new([ + 'private_key_bits' => (int)$keyBits, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + 'config' => $configFile + ]); + if (!$privKey) throw new \Exception('Failed to generate Private Key: ' . openssl_error_string()); + + \Log::debug("Generating Leaf with SAN: " . $sanString); + + $csr = openssl_csr_new($dn, $privKey, [ + 'digest_alg' => 'sha256', + 'req_extensions' => 'v3_req', + 'config' => $configFile + ]); + if (!$csr) { + $err = openssl_error_string(); + \Log::error("CSR Creation Failed: " . $err); + throw new \Exception('Failed to generate CSR: ' . $err); + } + + $serial = $this->generateSerialNumber(); + \Log::debug("Signing CSR with serial: " . $serial); + + $days = 365; + if (!empty($data['is_test_short_lived'])) { + $days = 1; // Minimum allowed by OpenSSL, will override DB record later + } + + $cert = openssl_csr_sign($csr, $intermediate->cert_content, $intermediate->key_content, $days, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_req', + 'config' => $configFile, + ], $serial); + + if (!$cert) { + $err = openssl_error_string(); + \Log::error("Certificate Signing Failed: " . $err); + throw new \Exception('Failed to sign Certificate: ' . $err); + } + + // Verification: check if serial was actually applied + $certInfo = openssl_x509_parse($cert); + $actualSerialHex = isset($certInfo['serialNumberHex']) + ? $this->formatHex($certInfo['serialNumberHex']) + : $this->formatSerialToHex($certInfo['serialNumber']); + + \Log::debug("Certificate signed. Embedded Serial: " . $actualSerialHex); + + if (!openssl_x509_export($cert, $certPem)) throw new \Exception('Failed to export Certificate'); + if (!openssl_pkey_export($privKey, $keyPem, null, ['config' => $configFile])) throw new \Exception('Failed to export Private Key'); + if (!openssl_csr_export($csr, $csrPem)) throw new \Exception('Failed to export CSR'); + + $validTo = isset($certInfo['validTo_time_t']) ? date('Y-m-d H:i:s', $certInfo['validTo_time_t']) : null; + + // Override for testing: 30 seconds expiration + if (!empty($data['is_test_short_lived'])) { + $validTo = date('Y-m-d H:i:s', time() + 30); + } + + return [ + 'cert' => $certPem, + 'key' => $keyPem, + 'csr' => $csrPem, + 'serial' => $actualSerialHex, + 'valid_from' => isset($certInfo['validFrom_time_t']) ? date('Y-m-d H:i:s', $certInfo['validFrom_time_t']) : null, + 'valid_to' => $validTo, + ]; + } finally { + if ($configFile && file_exists($configFile)) { + unlink($configFile); + } + } + } + + /** + * Generate a unique serial number. + */ + protected function generateSerialNumber(): int + { + try { + return random_int(1, PHP_INT_MAX); + } catch (\Exception $e) { + return time(); + } + } + + /** + * Format a hex string (from serialNumberHex). + */ + public function formatHex($hex) + { + $hex = strtoupper($hex); + return implode(':', str_split($hex, 2)); + } + + /** + * Fallback format a decimal serial number to hex string. + */ + public function formatSerialToHex($decimal) + { + $cleaned = preg_replace('/[^0-9]/', '', (string)$decimal); + if ($cleaned === '') $cleaned = '0'; + + if (function_exists('bcdiv')) { + $hex = ''; + $value = $cleaned; + if (!preg_match('/^\d+$/', $value)) $value = '0'; + + while (bccomp($value, '0') > 0) { + $mod = bcmod($value, '16'); + $hex = dechex((int)$mod) . $hex; + $value = bcdiv($value, '16', 0); + } + $hex = $hex ?: '0'; + } else { + $hex = dechex((int)$cleaned); + } + + if (strlen($hex) % 2 !== 0) $hex = '0' . $hex; + return strtoupper(implode(':', str_split($hex, 2))); + } + + /** + * Renew (re-sign) an existing CA certificate using its existing private key. + */ + public function renewCaCertificate(CaCertificate $cert, int $days) + { + $configFile = null; + try { + // 1. Prepare Config + $configContent = "[req]\ndistinguished_name = req\n[v3_ca]\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign"; + $configFile = tempnam(sys_get_temp_dir(), 'renew_ca_'); + file_put_contents($configFile, $configContent); + + // 2. Get Private Key + $privKey = openssl_pkey_get_private($cert->key_content); + if (!$privKey) throw new \Exception('Failed to load Private Key'); + + // 3. Get Subject DN from existing Cert + $certInfo = openssl_x509_parse($cert->cert_content); + $dn = $certInfo['subject']; + + $dnMap = [ + 'CN' => 'commonName', + 'O' => 'organizationName', + 'OU' => 'organizationalUnitName', + 'C' => 'countryName', + 'ST' => 'stateOrProvinceName', + 'L' => 'localityName', + 'emailAddress' => 'emailAddress' + ]; + + $newDn = []; + foreach ($dn as $key => $value) { + if (isset($dnMap[$key])) { + $newDn[$dnMap[$key]] = $value; + } + } + + // 4. Generate New CSR + $csr = openssl_csr_new($newDn, $privKey, ['digest_alg' => 'sha256', 'config' => $configFile]); + if (!$csr) throw new \Exception('Failed to generate Renewal CSR: ' . openssl_error_string()); + + // 5. Determine Signer (Issuer) + $issuerCert = null; + $issuerKey = null; + + if ($cert->ca_type === 'root') { + // Root signs itself + $issuerCert = null; + $issuerKey = $privKey; + } else { + // Intermediate is signed by Root + $root = CaCertificate::where('ca_type', 'root')->first(); + if (!$root) throw new \Exception('Root CA not found for signing intermediate renewal.'); + $issuerCert = $root->cert_content; + $issuerKey = openssl_pkey_get_private($root->key_content); + } + + // 6. Sign CSR + $serial = $this->generateSerialNumber(); + $newCert = openssl_csr_sign($csr, $issuerCert, $issuerKey, $days, [ + 'digest_alg' => 'sha256', + 'x509_extensions' => 'v3_ca', + 'config' => $configFile, + ], $serial); + + if (!$newCert) throw new \Exception('Failed to sign Renewal Cert: ' . openssl_error_string()); + + // 7. Export + if (!openssl_x509_export($newCert, $newCertPem)) throw new \Exception('Failed to export Renewal Cert'); + + // 8. Parse new details + $newInfo = openssl_x509_parse($newCertPem); + $newSerialHex = isset($newInfo['serialNumberHex']) + ? $this->formatHex($newInfo['serialNumberHex']) + : $this->formatSerialToHex($newInfo['serialNumber']); + + return [ + 'cert_content' => $newCertPem, + 'serial_number' => $newSerialHex, + 'valid_from' => date('Y-m-d H:i:s', $newInfo['validFrom_time_t']), + 'valid_to' => date('Y-m-d H:i:s', $newInfo['validTo_time_t']), + ]; + + } finally { + if ($configFile && file_exists($configFile)) unlink($configFile); + } + } +} diff --git a/app/Traits/CanTrackLogin.php b/app/Traits/CanTrackLogin.php new file mode 100644 index 0000000..eff328b --- /dev/null +++ b/app/Traits/CanTrackLogin.php @@ -0,0 +1,118 @@ +header('User-Agent'); + $ip = $request->ip(); + + // For local development testing (if IP is local, use a real one for fallback) + $lookupIp = ($ip === '127.0.0.1' || $ip === '::1') ? '8.8.8.8' : $ip; + $location = $this->getLocationFromIp($lookupIp); + + $info = $this->parseUserAgent($userAgent); + + LoginHistory::create([ + 'user_id' => $user->id, + 'ip_address' => $ip, + 'user_agent' => $userAgent, + 'device_type' => $info['device'], + 'os' => $info['os'], + 'browser' => $info['browser'], + 'city' => $location['city'], + 'country' => $location['country'], + 'country_code' => $location['country_code'], + ]); + } + + /** + * Get Location from IP + */ + protected function getLocationFromIp($ip) + { + try { + $response = Http::get("http://ip-api.com/json/{$ip}")->json(); + + if ($response && $response['status'] === 'success') { + return [ + 'city' => $response['city'] ?? 'Unknown City', + 'country' => $response['country'] ?? 'Unknown Country', + 'country_code' => $response['countryCode'] ?? 'UN', + ]; + } + } catch (\Exception $e) { + // Fallback silently + } + + return [ + 'city' => 'Unknown City', + 'country' => 'Unknown Country', + 'country_code' => 'UN', + ]; + } + + /** + * Helper to parse User Agent + */ + protected function parseUserAgent($agent) + { + $os = 'Unknown OS'; + $browser = 'Unknown Browser'; + $device = 'Desktop'; + + // OS Parsing + if (preg_match('/iphone|ipad|ipod/i', $agent)) { + $os = 'iOS'; + $device = 'iOS'; + } elseif (preg_match('/android/i', $agent)) { + $os = 'Android'; + $device = 'Android'; + } elseif (preg_match('/windows/i', $agent)) { + $os = 'Windows'; + $device = 'Windows'; + } elseif (preg_match('/macintosh|mac os x/i', $agent)) { + $os = 'Mac'; + $device = 'Mac'; + } elseif (preg_match('/linux/i', $agent)) { + $os = 'Linux'; + $device = 'Linux'; + } + + // Browser Parsing + if (preg_match('/msie/i', $agent) && !preg_match('/opera/i', $agent)) { + $browser = 'Internet Explorer'; + } elseif (preg_match('/firefox/i', $agent)) { + $browser = 'Firefox'; + } elseif (preg_match('/chrome/i', $agent)) { + $browser = 'Chrome'; + } elseif (preg_match('/safari/i', $agent)) { + $browser = 'Safari'; + } elseif (preg_match('/opera/i', $agent)) { + $browser = 'Opera'; + } elseif (preg_match('/netscape/i', $agent)) { + $browser = 'Netscape'; + } + + // More specific + if ($os === 'iOS' && $browser === 'Safari') $browser = 'iOS Safari'; + if ($os === 'iOS' && $browser === 'Chrome') $browser = 'iOS Chrome'; + if ($os === 'Android' && $browser === 'Chrome') $browser = 'Android Chrome'; + if ($os === 'Android' && $browser === 'Firefox') $browser = 'Android Firefox'; + + return [ + 'os' => $os, + 'browser' => $browser, + 'device' => $device + ]; + } +} diff --git a/app/Traits/LogsActivity.php b/app/Traits/LogsActivity.php new file mode 100644 index 0000000..aa0b9b9 --- /dev/null +++ b/app/Traits/LogsActivity.php @@ -0,0 +1,23 @@ + auth()->id(), + 'action' => $action, + 'description' => $description, + 'ip_address' => Request::ip(), + 'user_agent' => Request::userAgent(), + ]); + } +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..c35e31d --- /dev/null +++ b/artisan @@ -0,0 +1,18 @@ +#!/usr/bin/env php +handleCommand(new ArgvInput); + +exit($status); diff --git a/banner.png b/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..85b197adf02491c33851fe579e05e6ad8e328926 GIT binary patch literal 166542 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%0A2CzLInjrVOXa?;|@ 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

ifS!C>##tC{|Chs)X=gNs{@> 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 BR?q+d literal 0 HcmV?d00001 diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..0aacf5e --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,24 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + channels: __DIR__.'/../routes/channels.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + $middleware->trustProxies(at: '*'); + $middleware->statefulApi(); + $middleware->alias([ + 'admin' => \App\Http\Middleware\AdminMiddleware::class, + ]); + }) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..38b258d --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ +=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "time": "2024-10-18T22:15:13+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.369.4", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "2aa1ef195e90140d733382e4341732ce113024f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2aa1ef195e90140d733382e4341732ce113024f5", + "reference": "2aa1ef195e90140d733382e4341732ce113024f5", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.4.5", + "mtdowling/jmespath.php": "^2.8.0", + "php": ">=8.1", + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^2.7.8", + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-sockets": "*", + "phpunit/phpunit": "^9.6", + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + }, + "exclude-from-classmap": [ + "src/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://github.com/aws/aws-sdk-php/discussions", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.369.4" + }, + "time": "2025-12-29T19:07:47+00:00" + }, + { + "name": "bacon/bacon-qr-code", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || ^11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "spatie/pixelmatch-php": "^1.2.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3" + }, + "time": "2025-11-19T17:15:36+00:00" + }, + { + "name": "brick/math", + "version": "0.14.1", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "bignumber", + "brick", + "decimal", + "integer", + "math", + "mathematics", + "rational" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.14.1" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2025-11-24T14:40:29+00:00" + }, + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "clue/redis-protocol", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/clue/redis-protocol.git", + "reference": "6f565332f5531b7722d1e9c445314b91862f6d6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/redis-protocol/zipball/6f565332f5531b7722d1e9c445314b91862f6d6c", + "reference": "6f565332f5531b7722d1e9c445314b91862f6d6c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\Redis\\Protocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "A streaming Redis protocol (RESP) parser and serializer written in pure PHP.", + "homepage": "https://github.com/clue/redis-protocol", + "keywords": [ + "parser", + "protocol", + "redis", + "resp", + "serializer", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/redis-protocol/issues", + "source": "https://github.com/clue/redis-protocol/tree/v0.3.2" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2024-08-07T11:06:28+00:00" + }, + { + "name": "clue/redis-react", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-redis.git", + "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-redis/zipball/84569198dfd5564977d2ae6a32de4beb5a24bdca", + "reference": "84569198dfd5564977d2ae6a32de4beb5a24bdca", + "shasum": "" + }, + "require": { + "clue/redis-protocol": "^0.3.2", + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.0 || ^1.1", + "react/promise-timer": "^1.11", + "react/socket": "^1.16" + }, + "require-dev": { + "clue/block-react": "^1.5", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Redis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Async Redis client implementation, built on top of ReactPHP.", + "homepage": "https://github.com/clue/reactphp-redis", + "keywords": [ + "async", + "client", + "database", + "reactphp", + "redis" + ], + "support": { + "issues": "https://github.com/clue/reactphp-redis/issues", + "source": "https://github.com/clue/reactphp-redis/tree/v2.8.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2025-01-03T16:18:33+00:00" + }, + { + "name": "dasprid/enum", + "version": "1.0.7", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.7" + }, + "time": "2025-09-16T12:23:56+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "shasum": "" + }, + "require": { + "php": "^8.2|^8.3|^8.4|^8.5" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2025-10-31T18:51:33+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "21dc724a0583619cd1652f673303492272778051" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2025-08-23T21:21:41+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "intervention/gif", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/5999eac6a39aa760fb803bc809e8909ee67b451a", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.2.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2025-03-29T07:46:21+00:00" + }, + { + "name": "intervention/image", + "version": "3.11.6", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "5f6d27d9fd56312c47f347929e7ac15345c605a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/5f6d27d9fd56312c47f347929e7ac15345c605a1", + "reference": "5f6d27d9fd56312c47f347929e7ac15345c605a1", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "intervention/gif": "^4.2", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "suggest": { + "ext-exif": "Recommended to be able to read EXIF data properly." + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io" + } + ], + "description": "PHP Image Processing", + "homepage": "https://image.intervention.io", + "keywords": [ + "gd", + "image", + "imagick", + "resize", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/3.11.6" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2025-12-17T13:38:29+00:00" + }, + { + "name": "laravel/framework", + "version": "v12.43.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "195b893593a9298edee177c0844132ebaa02102f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/195b893593a9298edee177c0844132ebaa02102f", + "reference": "195b893593a9298edee177c0844132ebaa02102f", + "shasum": "" + }, + "require": { + "brick/math": "^0.11|^0.12|^0.13|^0.14", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^3.2.1|^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.7", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/log": "^1.0|^2.0|^3.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.8.1", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0|^1.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-12-16T18:53:08+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.8", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "096748cdfb81988f60090bbb839ce3205ace0d35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35", + "reference": "096748cdfb81988f60090bbb839ce3205ace0d35", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.8" + }, + "time": "2025-11-21T20:52:52+00:00" + }, + { + "name": "laravel/reverb", + "version": "v1.6.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/reverb.git", + "reference": "b97d21650bcfaa462dfa4735048dbc33359514e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/reverb/zipball/b97d21650bcfaa462dfa4735048dbc33359514e1", + "reference": "b97d21650bcfaa462dfa4735048dbc33359514e1", + "shasum": "" + }, + "require": { + "clue/redis-react": "^2.6", + "guzzlehttp/psr7": "^2.6", + "illuminate/console": "^10.47|^11.0|^12.0", + "illuminate/contracts": "^10.47|^11.0|^12.0", + "illuminate/http": "^10.47|^11.0|^12.0", + "illuminate/support": "^10.47|^11.0|^12.0", + "laravel/prompts": "^0.1.15|^0.2.0|^0.3.0", + "php": "^8.2", + "pusher/pusher-php-server": "^7.2", + "ratchet/rfc6455": "^0.4", + "react/promise-timer": "^1.10", + "react/socket": "^1.14", + "symfony/console": "^6.0|^7.0", + "symfony/http-foundation": "^6.3|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^8.36|^9.15|^10.8", + "pestphp/pest": "^2.0|^3.0|^4.0", + "phpstan/phpstan": "^1.10", + "ratchet/pawl": "^0.4.1", + "react/async": "^4.2", + "react/http": "^1.9" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Output": "Laravel\\Reverb\\Output" + }, + "providers": [ + "Laravel\\Reverb\\ApplicationManagerServiceProvider", + "Laravel\\Reverb\\ReverbServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Reverb\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Joe Dixon", + "email": "joe@laravel.com" + } + ], + "description": "Laravel Reverb provides a real-time WebSocket communication backend for Laravel applications.", + "keywords": [ + "WebSockets", + "laravel", + "real-time", + "websocket" + ], + "support": { + "issues": "https://github.com/laravel/reverb/issues", + "source": "https://github.com/laravel/reverb/tree/v1.6.3" + }, + "time": "2025-11-28T20:12:49+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v4.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/f5fb373be39a246c74a060f2cf2ae2c2145b3664", + "reference": "f5fb373be39a246c74a060f2cf2ae2c2145b3664", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "php": "^8.2", + "symfony/console": "^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.15|^10.8", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2025-11-21T13:59:03+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.7", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2025-11-21T20:52:36+00:00" + }, + { + "name": "laravel/socialite", + "version": "v5.24.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2025-12-09T15:37:06+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.10.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.11.1|^0.12.0", + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.10.2" + }, + "time": "2025-11-20T16:29:12+00:00" + }, + { + "name": "league/commonmark", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.9-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2025-11-26T21:48:24+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.30.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.30.2" + }, + "time": "2025-11-10T17:13:11+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c", + "reference": "f8ba6a92a5c1fdcbdd89dede009a1e6e1b93ba8c", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.132.4", + "league/flysystem": "^2.0.0 || ^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues", + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.0.0" + }, + "time": "2022-01-13T21:11:49+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.30.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ab4f9d0d672f601b102936aa728801dd1a11968d", + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.2" + }, + "time": "2025-11-10T11:23:37+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, + { + "name": "league/uri", + "version": "7.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.7", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.7.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2025-12-07T16:02:06+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.7.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2025-12-07T16:03:21+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2025-03-24T10:02:05+00:00" + }, + { + "name": "mtdowling/jmespath.php", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.33" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" + }, + "time": "2024-09-04T18:46:31+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.11.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "bdb375400dcd162624531666db4799b36b64e4a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", + "reference": "bdb375400dcd162624531666db4799b36b64e4a1", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-12-02T21:04:28+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.3", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/tester": "^2.5.2", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.3" + }, + "time": "2025-10-30T22:57:59+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.1", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72", + "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/tester": "^2.5", + "phpstan/phpstan-nette": "^2.0@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.1" + }, + "time": "2025-12-22T12:14:32+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.3.6" + }, + "require-dev": { + "illuminate/console": "^11.46.1", + "laravel/pint": "^1.25.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2025-11-20T02:34:59+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "paragonie/sodium_compat", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/sodium_compat.git", + "reference": "547e2dc4d45107440e76c17ab5a46e4252460158" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/547e2dc4d45107440e76c17ab5a46e4252460158", + "reference": "547e2dc4d45107440e76c17ab5a46e4252460158", + "shasum": "" + }, + "require": { + "php": "^8.1", + "php-64bit": "*" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^7|^8|^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "suggest": { + "ext-sodium": "Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "files": [ + "autoload.php" + ], + "psr-4": { + "ParagonIE\\Sodium\\": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + }, + { + "name": "Frank Denis", + "email": "jedisct1@pureftpd.org" + } + ], + "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", + "keywords": [ + "Authentication", + "BLAKE2b", + "ChaCha20", + "ChaCha20-Poly1305", + "Chapoly", + "Curve25519", + "Ed25519", + "EdDSA", + "Edwards-curve Digital Signature Algorithm", + "Elliptic Curve Diffie-Hellman", + "Poly1305", + "Pure-PHP cryptography", + "RFC 7748", + "RFC 8032", + "Salpoly", + "Salsa20", + "X25519", + "XChaCha20-Poly1305", + "XSalsa20-Poly1305", + "Xchacha20", + "Xsalsa20", + "aead", + "cryptography", + "ecdh", + "elliptic curve", + "elliptic curve cryptography", + "encryption", + "libsodium", + "php", + "public-key cryptography", + "secret-key cryptography", + "side-channel resistant" + ], + "support": { + "issues": "https://github.com/paragonie/sodium_compat/issues", + "source": "https://github.com/paragonie/sodium_compat/tree/v2.4.0" + }, + "time": "2025-10-06T08:47:40+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.4", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-08-21T11:53:16+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.48", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/64065a5679c50acb886e82c07aa139b0f757bb89", + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.48" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2025-12-15T11:51:42+00:00" + }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.3", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + }, + "time": "2024-09-05T11:56:40+00:00" + }, + { + "name": "pragmarx/google2fa-laravel", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-laravel.git", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/60f363c16db1e94263e0560efebe9fc2e302b7ef", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef", + "shasum": "" + }, + "require": { + "laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": ">=7.0", + "pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*", + "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes.", + "pragmarx/recovery": "Generate recovery codes." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Google2FA": "PragmaRX\\Google2FALaravel\\Facade" + }, + "providers": [ + "PragmaRX\\Google2FALaravel\\ServiceProvider" + ] + }, + "component": "package", + "frameworks": [ + "Laravel" + ], + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FALaravel\\": "src/", + "PragmaRX\\Google2FALaravel\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-laravel/issues", + "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v2.3.0" + }, + "time": "2025-02-26T19:39:35+00:00" + }, + { + "name": "pragmarx/google2fa-qrcode", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "reference": "c23ebcc3a50de0d1566016a6dd1486e183bb78e1", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "pragmarx/google2fa": "^4.0|^5.0|^6.0|^7.0|^8.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "chillerlan/php-qrcode": "^1.0|^2.0|^3.0|^4.0", + "khanamiryan/qrcode-detector-decoder": "^1.0", + "phpunit/phpunit": "~4|~5|~6|~7|~8|~9" + }, + "suggest": { + "bacon/bacon-qr-code": "For QR Code generation, requires imagick", + "chillerlan/php-qrcode": "For QR Code generation" + }, + "type": "library", + "extra": { + "component": "package", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FAQRCode\\": "src/", + "PragmaRX\\Google2FAQRCode\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "QR Code package for Google2FA", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa", + "qr code", + "qrcode" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.1" + }, + "time": "2025-09-19T23:02:26+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.18", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "ddff0ac01beddc251786fe70367cd8bbdb258196" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/ddff0ac01beddc251786fe70367cd8bbdb258196", + "reference": "ddff0ac01beddc251786fe70367cd8bbdb258196", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" + }, + "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.18" + }, + "time": "2025-12-17T14:35:46+00:00" + }, + { + "name": "pusher/pusher-php-server", + "version": "7.2.7", + "source": { + "type": "git", + "url": "https://github.com/pusher/pusher-http-php.git", + "reference": "148b0b5100d000ed57195acdf548a2b1b38ee3f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pusher/pusher-http-php/zipball/148b0b5100d000ed57195acdf548a2b1b38ee3f7", + "reference": "148b0b5100d000ed57195acdf548a2b1b38ee3f7", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "guzzlehttp/guzzle": "^7.2", + "paragonie/sodium_compat": "^1.6|^2.0", + "php": "^7.3|^8.0", + "psr/log": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "overtrue/phplint": "^2.3", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Pusher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Library for interacting with the Pusher REST API", + "keywords": [ + "events", + "messaging", + "php-pusher-server", + "publish", + "push", + "pusher", + "real time", + "real-time", + "realtime", + "rest", + "trigger" + ], + "support": { + "issues": "https://github.com/pusher/pusher-http-php/issues", + "source": "https://github.com/pusher/pusher-http-php/tree/7.2.7" + }, + "time": "2025-01-06T10:56:20+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "8429c78ca35a09f27565311b98101e2826affde0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.2" + }, + "time": "2025-12-14T04:43:48+00:00" + }, + { + "name": "ratchet/rfc6455", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/ratchetphp/RFC6455.git", + "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ratchetphp/RFC6455/zipball/859d95f85dda0912c6d5b936d036d044e3af47ef", + "reference": "859d95f85dda0912c6d5b936d036d044e3af47ef", + "shasum": "" + }, + "require": { + "php": ">=7.4", + "psr/http-factory-implementation": "^1.0", + "symfony/polyfill-php80": "^1.15" + }, + "require-dev": { + "guzzlehttp/psr7": "^2.7", + "phpunit/phpunit": "^9.5", + "react/socket": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ratchet\\RFC6455\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "role": "Developer" + }, + { + "name": "Matt Bonneau", + "role": "Developer" + } + ], + "description": "RFC6455 WebSocket protocol handler", + "homepage": "http://socketo.me", + "keywords": [ + "WebSockets", + "rfc6455", + "websocket" + ], + "support": { + "chat": "https://gitter.im/reactphp/reactphp", + "issues": "https://github.com/ratchetphp/RFC6455/issues", + "source": "https://github.com/ratchetphp/RFC6455/tree/v0.4.0" + }, + "time": "2025-02-24T01:18:22+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/dns", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-18T19:34:28+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+00:00" + }, + { + "name": "react/promise", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" + }, + { + "name": "react/promise-timer", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise-timer.git", + "reference": "4f70306ed66b8b44768941ca7f142092600fafc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/4f70306ed66b8b44768941ca7f142092600fafc1", + "reference": "4f70306ed66b8b44768941ca7f142092600fafc1", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7.0 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\Timer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", + "homepage": "https://github.com/reactphp/promise-timer", + "keywords": [ + "async", + "event-loop", + "promise", + "reactphp", + "timeout", + "timer" + ], + "support": { + "issues": "https://github.com/reactphp/promise-timer/issues", + "source": "https://github.com/reactphp/promise-timer/tree/v1.11.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-04T14:27:45+00:00" + }, + { + "name": "react/socket", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-19T20:47:34+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, + { + "name": "ryangjchandler/laravel-cloudflare-turnstile", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/ryangjchandler/laravel-cloudflare-turnstile.git", + "reference": "c5712e515aa355397159e0b59fe331469bee5a87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ryangjchandler/laravel-cloudflare-turnstile/zipball/c5712e515aa355397159e0b59fe331469bee5a87", + "reference": "c5712e515aa355397159e0b59fe331469bee5a87", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^11.0|^12.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.13.0" + }, + "require-dev": { + "larastan/larastan": "^3.1", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^8.0", + "orchestra/testbench": "^9.0|^10.0", + "pestphp/pest": "^3.7", + "pestphp/pest-plugin-laravel": "^3.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", + "phpstan/phpstan-phpunit": "^1.0|^2.0", + "phpunit/phpunit": "^10.0|^11.5.3", + "spatie/laravel-ray": "^1.26", + "spatie/pest-plugin-snapshots": "^2.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "LaravelCloudflareTurnstile": "RyanChandler\\LaravelCloudflareTurnstile\\Facades\\LaravelCloudflareTurnstile" + }, + "providers": [ + "RyanChandler\\LaravelCloudflareTurnstile\\LaravelCloudflareTurnstileServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "RyanChandler\\LaravelCloudflareTurnstile\\": "src", + "RyanChandler\\LaravelCloudflareTurnstile\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ryan Chandler", + "email": "support@ryangjchandler.co.uk", + "role": "Developer" + } + ], + "description": "A simple package to help integrate Cloudflare Turnstile.", + "homepage": "https://github.com/ryangjchandler/laravel-cloudflare-turnstile", + "keywords": [ + "laravel", + "laravel-cloudflare-turnstile", + "ryangjchandler" + ], + "support": { + "issues": "https://github.com/ryangjchandler/laravel-cloudflare-turnstile/issues", + "source": "https://github.com/ryangjchandler/laravel-cloudflare-turnstile/tree/v3.0.1" + }, + "time": "2025-12-16T15:56:01+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.92.7", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/f09a799850b1ed765103a4f0b4355006360c49a5", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.23|^2.1|^3.1", + "phpunit/php-code-coverage": "^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.5.24|^10.5|^11.5", + "spatie/pest-plugin-test-time": "^1.1|^2.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.92.7" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-07-17T15:46:43+00:00" + }, + { + "name": "symfony/clock", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/9169f24776edde469914c1e7a1442a50f7a4e110", + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.4.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:39:26+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.1" + }, + "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-12-05T15:23:39+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v7.4.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-10-30T13:39:42+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/48be2b0653594eea32dcef130cca1c811dcf25c2", + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "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 tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v7.4.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-05T14:29:59+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.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-10-28T09:38:46+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", + "reference": "d551b38811096d0be9c4691d406991b47c0c630a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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 basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.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-27T13:27:24+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.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-05T05:42:40+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v7.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bd1af1e425811d6f077db240c3a588bdb405cd27", + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + }, + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v7.4.1" + }, + "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-12-07T11:13:10+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v7.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6e6f0a5fa8763f75a504b930163785fb6dd055f", + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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 a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v7.4.2" + }, + "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-12-08T07:43:37+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "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": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.4.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-21T15:26:00+00:00" + }, + { + "name": "symfony/mime", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/bdb02729471be5d047a3ac4a69068748f1a6be7a", + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "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": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v7.4.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-16T10:14:42+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.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-06-27T09:58:17+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.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": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.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": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.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-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.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-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.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-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.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-06-23T16:12:55+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.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-10-16T11:21:06+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "4720254cb2644a0b876233d258a32bf017330db7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/4720254cb2644a0b876233d258a32bf017330db7", + "reference": "4720254cb2644a0b876233d258a32bf017330db7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.4.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-27T13:27:24+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + }, + "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-07-15T11:30:57+00:00" + }, + { + "name": "symfony/string", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.33", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.4.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-27T13:27:24+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5.3|^3.3" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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 tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.4.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-27T13:27:24+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + }, + "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-07-15T13:41:35+00:00" + }, + { + "name": "symfony/uid", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2498e9f81b7baa206f44de583f2f48350b90142c", + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.4.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-09-25T11:02:55+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.4.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-10-27T20:36:44+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" + }, + "time": "2025-12-02T11:56:42+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-04-30T23:37:27+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + } + ], + "packages-dev": [ + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/49f92285ff5d6fc09816e976a004f8dec6a0ea30", + "reference": "49f92285ff5d6fc09816e976a004f8dec6a0ea30", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0", + "illuminate/contracts": "^10.24|^11.0|^12.0", + "illuminate/log": "^10.24|^11.0|^12.0", + "illuminate/process": "^10.24|^11.0|^12.0", + "illuminate/support": "^10.24|^11.0|^12.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2025-11-20T16:29:35+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f", + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.90.0", + "illuminate/view": "^12.40.1", + "larastan/larastan": "^3.8.0", + "laravel-zero/framework": "^12.0.4", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest": "^3.8.4" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2025-11-25T21:15:52+00:00" + }, + { + "name": "laravel/sail", + "version": "v1.51.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sail.git", + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sail/zipball/1c74357df034e869250b4365dd445c9f6ba5d068", + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068", + "shasum": "" + }, + "require": { + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^6.0|^7.0", + "symfony/yaml": "^6.0|^7.0" + }, + "require-dev": { + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^2.0" + }, + "bin": [ + "bin/sail" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sail\\SailServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Docker files for running a basic Laravel application.", + "keywords": [ + "docker", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, + "time": "2025-12-09T13:33:49+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.8.3", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", + "php": "^8.2.0", + "symfony/console": "^7.3.0" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2 || ^4.0.0", + "sebastian/environment": "^7.2.1 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-11-20T02:55:25+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-08-27T14:37:49+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.46", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "75dfe79a2aa30085b7132bb84377c24062193f33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/75dfe79a2aa30085b7132bb84377c24062193f33", + "reference": "75dfe79a2aa30085b7132bb84377c24062193f33", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.11", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.46" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-12-06T08:01:15+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T08:07:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.4.1" + }, + "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-12-04T18:11:45+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.2" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 0000000..7445d92 --- /dev/null +++ b/config/app.php @@ -0,0 +1,128 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'Asia/Jakarta', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + + 'frontend_url' => env('FRONTEND_URL', 'http://localhost:3000'), + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..7d1eb0d --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', App\Models\User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/config/broadcasting.php b/config/broadcasting.php new file mode 100644 index 0000000..ebc3fb9 --- /dev/null +++ b/config/broadcasting.php @@ -0,0 +1,82 @@ + env('BROADCAST_CONNECTION', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over WebSockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'reverb' => [ + 'driver' => 'reverb', + 'key' => env('REVERB_APP_KEY'), + 'secret' => env('REVERB_APP_SECRET'), + 'app_id' => env('REVERB_APP_ID'), + 'options' => [ + 'host' => env('REVERB_HOST'), + 'port' => env('REVERB_PORT', 443), + 'scheme' => env('REVERB_SCHEME', 'https'), + 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), + 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..b32aead --- /dev/null +++ b/config/cache.php @@ -0,0 +1,117 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", + | "failover", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + 'failover' => [ + 'driver' => 'failover', + 'stores' => [ + 'database', + 'array', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + +]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 0000000..b0d2bb4 --- /dev/null +++ b/config/cors.php @@ -0,0 +1,36 @@ + ['api/*', 'sanctum/csrf-cookie', '*'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['http://localhost:3000', 'http://127.0.0.1:3000', 'https://trustlab.dyzulk.com', 'https://trustlab-api.dyzulk.com', 'https://trustlab.pages.dev', 'https://dev.trustlab.dyzulk.com'], + + 'allowed_origins_patterns' => [ + '#^https?://.*\.trustlab\.pages\.dev$#', + ], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => true, + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..c57fa63 --- /dev/null +++ b/config/database.php @@ -0,0 +1,183 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'persistent' => env('REDIS_PERSISTENT', false), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..afbaa59 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,93 @@ + env('FILESYSTEM_DISK', 'r2'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + 'report' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + 'report' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + 'r2' => [ + 'driver' => 's3', + 'key' => env('R2_ACCESS_KEY_ID'), + 'secret' => env('R2_SECRET_ACCESS_KEY'), + 'region' => 'auto', + 'bucket' => env('R2_BUCKET'), + 'url' => env('R2_URL'), + 'endpoint' => env('R2_ENDPOINT'), + 'use_path_style_endpoint' => env('R2_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..9e998a4 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..4c25e15 --- /dev/null +++ b/config/mail.php @@ -0,0 +1,133 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'support' => [ + 'transport' => 'smtp', + 'host' => env('MAIL_SUPPORT_HOST', '127.0.0.1'), + 'port' => env('MAIL_SUPPORT_PORT', 587), + 'username' => env('MAIL_SUPPORT_USERNAME'), + 'password' => env('MAIL_SUPPORT_PASSWORD'), + 'encryption' => env('MAIL_SUPPORT_ENCRYPTION', 'tls'), + 'timeout' => null, + 'from' => [ + 'address' => env('MAIL_SUPPORT_FROM_ADDRESS'), + 'name' => env('MAIL_SUPPORT_FROM_NAME', 'Support'), + ], + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + +]; diff --git a/config/openssl.php b/config/openssl.php new file mode 100644 index 0000000..fbeda8d --- /dev/null +++ b/config/openssl.php @@ -0,0 +1,29 @@ + [ + 'countryName' => env('CA_ROOT_COUNTRY_NAME', 'ID'), + 'organizationName' => env('CA_ROOT_ORGANIZATION_NAME', 'DyDev Authority'), + 'organizationalUnitName' => env('CA_ROOT_ORGANIZATIONAL_UNIT_NAME', 'Security Division'), + 'commonName' => env('CA_ROOT_COMMON_NAME', 'DyDev Its True'), + ], + 'ca_4096' => [ + 'countryName' => env('CA_4096_COUNTRY_NAME', 'ID'), + 'organizationName' => env('CA_4096_ORGANIZATION_NAME', 'TrustLab CA'), + 'organizationalUnitName' => env('CA_4096_ORGANIZATIONAL_UNIT_NAME', 'Security Division'), + 'commonName' => env('CA_4096_COMMON_NAME', 'TrustLab Intermediate CA 4096'), + ], + 'ca_2048' => [ + 'countryName' => env('CA_2048_COUNTRY_NAME', 'ID'), + 'organizationName' => env('CA_2048_ORGANIZATION_NAME', 'TrustLab CA'), + 'organizationalUnitName' => env('CA_2048_ORGANIZATIONAL_UNIT_NAME', 'Security Division'), + 'commonName' => env('CA_2048_COMMON_NAME', 'TrustLab Intermediate CA 2048'), + ], + 'ca_leaf_default' => [ + 'countryName' => env('CA_LEAF_DEFAULT_COUNTRY_NAME', 'ID'), + 'localityName' => env('CA_LEAF_DEFAULT_LOCALITY', 'Jakarta'), + 'stateOrProvinceName' => env('CA_LEAF_DEFAULT_STATE', 'DKI Jakarta'), + 'organizationName' => env('CA_LEAF_DEFAULT_ORGANIZATION_NAME', 'TrustLab Cooking'), + 'commonName' => env('CA_LEAF_DEFAULT_COMMON_NAME', 'customer.trustlab.com'), + ], +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..79c2c0a --- /dev/null +++ b/config/queue.php @@ -0,0 +1,129 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", + | "deferred", "background", "failover", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + 'deferred' => [ + 'driver' => 'deferred', + ], + + 'background' => [ + 'driver' => 'background', + ], + + 'failover' => [ + 'driver' => 'failover', + 'connections' => [ + 'database', + 'deferred', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/reverb.php b/config/reverb.php new file mode 100644 index 0000000..d7fc88e --- /dev/null +++ b/config/reverb.php @@ -0,0 +1,95 @@ + env('REVERB_SERVER', 'reverb'), + + /* + |-------------------------------------------------------------------------- + | Reverb Servers + |-------------------------------------------------------------------------- + | + | Here you may define details for each of the supported Reverb servers. + | Each server has its own configuration options that are defined in + | the array below. You should ensure all the options are present. + | + */ + + 'servers' => [ + + 'reverb' => [ + 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'), + 'port' => env('REVERB_SERVER_PORT', 8080), + 'path' => env('REVERB_SERVER_PATH', ''), + 'hostname' => env('REVERB_HOST'), + 'options' => [ + 'tls' => [], + ], + 'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000), + 'scaling' => [ + 'enabled' => env('REVERB_SCALING_ENABLED', false), + 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'), + 'server' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'port' => env('REDIS_PORT', '6379'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'database' => env('REDIS_DB', '0'), + 'timeout' => env('REDIS_TIMEOUT', 60), + ], + ], + 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15), + 'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Reverb Applications + |-------------------------------------------------------------------------- + | + | Here you may define how Reverb applications are managed. If you choose + | to use the "config" provider, you may define an array of apps which + | your server will support, including their connection credentials. + | + */ + + 'apps' => [ + + 'provider' => 'config', + + 'apps' => [ + [ + 'key' => env('REVERB_APP_KEY'), + 'secret' => env('REVERB_APP_SECRET'), + 'app_id' => env('REVERB_APP_ID'), + 'options' => [ + 'host' => env('REVERB_HOST'), + 'port' => env('REVERB_PORT', 443), + 'scheme' => env('REVERB_SCHEME', 'https'), + 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', + ], + 'allowed_origins' => ['*'], + 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60), + 'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30), + 'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'), + 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000), + ], + ], + + ], + +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..c467c9e --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,90 @@ + array_merge( + explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1')), + [ + 'trustlab.dyzulk.com', + 'dev.trustlab.dyzulk.com', + parse_url(env('APP_URL', ''), PHP_URL_HOST), + parse_url(env('FRONTEND_URL', ''), PHP_URL_HOST), + ], + (isset($_SERVER['HTTP_ORIGIN']) && str_ends_with($_SERVER['HTTP_ORIGIN'], '.trustlab.pages.dev')) + ? [parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST)] + : [] + ), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..af0291c --- /dev/null +++ b/config/services.php @@ -0,0 +1,55 @@ + [ + 'key' => env('POSTMARK_API_KEY'), + ], + + 'resend' => [ + 'key' => env('RESEND_API_KEY'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + + 'google' => [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URI'), + ], + + 'github' => [ + 'client_id' => env('APP_ENV') === 'production' ? env('GITHUB_PROD_CLIENT_ID') : env('GITHUB_DEV_CLIENT_ID'), + 'client_secret' => env('APP_ENV') === 'production' ? env('GITHUB_PROD_CLIENT_SECRET') : env('GITHUB_DEV_CLIENT_SECRET'), + 'redirect' => env('APP_ENV') === 'production' ? env('GITHUB_PROD_REDIRECT_URI') : env('GITHUB_DEV_REDIRECT_URI'), + ], + + 'turnstile' => [ + 'key' => env('TURNSTILE_SITE_KEY'), + 'secret' => env('TURNSTILE_SECRET_KEY'), + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..07cdb67 --- /dev/null +++ b/config/session.php @@ -0,0 +1,220 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => (int) env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug((string) env('APP_NAME', 'laravel')).'-session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain without subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => (function() { + if (!isset($_SERVER['HTTP_HOST'])) { + return env('SESSION_DOMAIN'); + } + + $host = $_SERVER['HTTP_HOST']; + + // Always use dyzulk.com for the cookie domain because the API is hosted there. + // A cookie set by dyzulk.com cannot have a .pages.dev domain. + if (str_contains($host, 'dyzulk.com')) { + return '.dyzulk.com'; + } + + return env('SESSION_DOMAIN'); + })(), + + 'same_site' => (function() { + // Force 'none' for any cross-domain requests on production/staging + // This is safe because we use 'secure' => true below. + return 'none'; + })(), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => true, + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..ff03931 --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,48 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $firstName = fake()->firstName(); + $lastName = fake()->lastName(); + + return [ + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..0089be9 --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,78 @@ +string('id', 32)->primary(); + $table->string('first_name')->nullable(); + $table->string('last_name')->nullable(); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password')->nullable(); + + // 2FA columns + $table->text('two_factor_secret')->nullable(); + $table->text('two_factor_recovery_codes')->nullable(); + $table->timestamp('two_factor_confirmed_at')->nullable(); + + $table->string('role')->default('customer'); + $table->string('avatar')->nullable(); + + // Profile & Contact + $table->string('phone')->nullable(); + $table->text('bio')->nullable(); + $table->string('job_title')->nullable(); + $table->string('location')->nullable(); + $table->string('country')->nullable(); + $table->string('city_state')->nullable(); + $table->string('postal_code')->nullable(); + $table->string('tax_id')->nullable(); + + // Notification Settings + $table->boolean('settings_email_alerts')->default(true); + $table->boolean('settings_certificate_renewal')->default(true); + + // Preferences + $table->string('default_landing_page')->default('/dashboard'); + $table->string('theme')->default('system'); + $table->string('language')->default('en'); + + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('user_id', 32)->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..b9c106b --- /dev/null +++ b/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..425e705 --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/database/migrations/2025_12_23_000000_create_api_keys_table.php b/database/migrations/2025_12_23_000000_create_api_keys_table.php new file mode 100644 index 0000000..a13d2f2 --- /dev/null +++ b/database/migrations/2025_12_23_000000_create_api_keys_table.php @@ -0,0 +1,32 @@ +uuid('id')->primary(); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('key', 64)->unique(); + $table->boolean('is_active')->default(true); + $table->timestamp('last_used_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('api_keys'); + } +}; diff --git a/database/migrations/2025_12_23_061409_create_personal_access_tokens_table.php b/database/migrations/2025_12_23_061409_create_personal_access_tokens_table.php new file mode 100644 index 0000000..6ad73f2 --- /dev/null +++ b/database/migrations/2025_12_23_061409_create_personal_access_tokens_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('tokenable_type'); + $table->string('tokenable_id', 32); + $table->index(['tokenable_type', 'tokenable_id']); + $table->text('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable()->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2025_12_23_123907_create_certificates_table.php b/database/migrations/2025_12_23_123907_create_certificates_table.php new file mode 100644 index 0000000..1d31560 --- /dev/null +++ b/database/migrations/2025_12_23_123907_create_certificates_table.php @@ -0,0 +1,43 @@ +string('uuid', 32)->primary(); + $table->string('user_id', 32)->index(); + $table->string('common_name'); + $table->string('organization')->nullable(); + $table->string('locality')->nullable(); + $table->string('state')->nullable(); + $table->string('country', 2)->nullable(); + $table->text('san')->nullable(); + $table->string('status')->default('ISSUED')->index(); + $table->integer('key_bits')->default(2048); + $table->string('serial_number')->nullable(); + $table->longText('cert_content')->nullable(); + $table->longText('key_content')->nullable(); + $table->longText('csr_content')->nullable(); + $table->dateTime('valid_from')->nullable(); + $table->dateTime('valid_to')->nullable(); + $table->timestamp('expired_notification_sent_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('certificates'); + } +}; diff --git a/database/migrations/2025_12_23_123913_create_ca_certificates_table.php b/database/migrations/2025_12_23_123913_create_ca_certificates_table.php new file mode 100644 index 0000000..42a5cb4 --- /dev/null +++ b/database/migrations/2025_12_23_123913_create_ca_certificates_table.php @@ -0,0 +1,39 @@ +string('uuid', 32)->primary(); + $table->string('ca_type'); // root, intermediate_4096, intermediate_2048 + $table->longText('cert_content')->nullable(); + $table->longText('key_content')->nullable(); + $table->string('serial_number')->nullable(); + $table->string('common_name')->nullable(); + $table->string('organization')->nullable(); + $table->dateTime('valid_from')->nullable(); + $table->dateTime('valid_to')->nullable(); + + // Tracking + $table->unsignedBigInteger('download_count')->default(0); + $table->timestamp('last_downloaded_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ca_certificates'); + } +}; diff --git a/database/migrations/2025_12_23_202750_create_login_histories_table.php b/database/migrations/2025_12_23_202750_create_login_histories_table.php new file mode 100644 index 0000000..2a03832 --- /dev/null +++ b/database/migrations/2025_12_23_202750_create_login_histories_table.php @@ -0,0 +1,37 @@ +uuid('id')->primary(); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->string('device_type')->nullable(); // ios, android, windows, mac, linux + $table->string('os')->nullable(); // iOS 15, Windows 10, etc. + $table->string('browser')->nullable(); // Safari, Chrome, etc. + $table->string('city')->nullable(); + $table->string('country')->nullable(); + $table->string('country_code')->nullable(); + $table->timestamps(); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('login_histories'); + } +}; diff --git a/database/migrations/2025_12_23_234051_create_inquiries_table.php b/database/migrations/2025_12_23_234051_create_inquiries_table.php new file mode 100644 index 0000000..d4c6c32 --- /dev/null +++ b/database/migrations/2025_12_23_234051_create_inquiries_table.php @@ -0,0 +1,34 @@ +uuid('id')->primary(); + $table->string('name'); + $table->string('email'); + $table->string('category')->nullable(); + $table->string('subject'); + $table->text('message'); + $table->enum('status', ['pending', 'replied'])->default('pending'); + $table->timestamp('replied_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('inquiries'); + } +}; diff --git a/database/migrations/2025_12_23_235609_create_notifications_table.php b/database/migrations/2025_12_23_235609_create_notifications_table.php new file mode 100644 index 0000000..965b521 --- /dev/null +++ b/database/migrations/2025_12_23_235609_create_notifications_table.php @@ -0,0 +1,31 @@ +uuid('id')->primary(); + $table->string('type'); + $table->uuidMorphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/database/migrations/2025_12_24_000001_create_legal_pages_tables.php b/database/migrations/2025_12_24_000001_create_legal_pages_tables.php new file mode 100644 index 0000000..90d9de7 --- /dev/null +++ b/database/migrations/2025_12_24_000001_create_legal_pages_tables.php @@ -0,0 +1,51 @@ +uuid('id')->primary(); + $table->string('title'); + $table->string('slug')->unique(); + $table->boolean('is_active')->default(true); + $table->timestamps(); + }); + + Schema::create('legal_page_revisions', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->foreignUuid('legal_page_id')->constrained('legal_pages')->onDelete('cascade'); + $table->longText('content'); + + // Hierarchical Versioning + $table->integer('major')->default(0); + $table->integer('minor')->default(0); + $table->integer('patch')->default(0); + + // Publishing Status + $table->string('status')->default('draft'); // draft, published, archived + $table->timestamp('published_at')->nullable(); + + $table->text('change_log')->nullable(); + $table->boolean('is_active')->default(true); // Internal Soft delete/archive + $table->unsignedBigInteger('created_by')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('legal_page_revisions'); + Schema::dropIfExists('legal_pages'); + } +}; diff --git a/database/migrations/2025_12_24_001231_create_tickets_tables.php b/database/migrations/2025_12_24_001231_create_tickets_tables.php new file mode 100644 index 0000000..2128e9f --- /dev/null +++ b/database/migrations/2025_12_24_001231_create_tickets_tables.php @@ -0,0 +1,43 @@ +uuid('id')->primary(); + $table->foreignUuid('user_id')->constrained()->onDelete('cascade'); + $table->string('ticket_number')->unique(); + $table->string('subject'); + $table->string('category'); // Technical, Billing, etc. + $table->enum('priority', ['low', 'medium', 'high'])->default('medium'); + $table->enum('status', ['open', 'answered', 'closed'])->default('open'); + $table->timestamps(); + }); + + Schema::create('ticket_replies', function (Blueprint $table) { + $table->uuid('id')->primary(); + $table->foreignUuid('ticket_id')->constrained()->onDelete('cascade'); + $table->foreignUuid('user_id')->constrained()->onDelete('cascade'); + $table->text('message'); + $table->string('attachment_path')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ticket_replies'); + Schema::dropIfExists('tickets'); + } +}; diff --git a/database/migrations/2025_12_24_004303_create_ticket_attachments_table.php b/database/migrations/2025_12_24_004303_create_ticket_attachments_table.php new file mode 100644 index 0000000..0626148 --- /dev/null +++ b/database/migrations/2025_12_24_004303_create_ticket_attachments_table.php @@ -0,0 +1,32 @@ +uuid('id')->primary(); + $table->foreignUuid('ticket_reply_id')->constrained('ticket_replies')->onDelete('cascade'); + $table->string('file_name'); + $table->string('file_path'); + $table->string('file_type'); // mime type + $table->integer('file_size'); // in bytes + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ticket_attachments'); + } +}; diff --git a/database/migrations/2025_12_27_080201_create_social_accounts_table.php b/database/migrations/2025_12_27_080201_create_social_accounts_table.php new file mode 100644 index 0000000..6977067 --- /dev/null +++ b/database/migrations/2025_12_27_080201_create_social_accounts_table.php @@ -0,0 +1,37 @@ +uuid('id')->primary(); + $table->foreignUuid('user_id')->constrained()->cascadeOnDelete(); + $table->string('provider'); // 'google', 'github' + $table->string('provider_id'); + $table->string('provider_email')->nullable(); // Email from the provider (e.g. b@gmail.com) + $table->string('avatar')->nullable(); + $table->text('token')->nullable(); + $table->text('refresh_token')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + + $table->unique(['provider', 'provider_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('social_accounts'); + } +}; diff --git a/database/migrations/2025_12_27_113948_create_activity_logs_table.php b/database/migrations/2025_12_27_113948_create_activity_logs_table.php new file mode 100644 index 0000000..208fb21 --- /dev/null +++ b/database/migrations/2025_12_27_113948_create_activity_logs_table.php @@ -0,0 +1,32 @@ +uuid('id')->primary(); + $table->foreignUuid('user_id')->constrained()->onDelete('cascade'); + $table->string('action'); + $table->text('description')->nullable(); + $table->string('ip_address')->nullable(); + $table->text('user_agent')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('activity_logs'); + } +}; diff --git a/database/migrations/2025_12_30_081926_add_pending_email_to_users_table.php b/database/migrations/2025_12_30_081926_add_pending_email_to_users_table.php new file mode 100644 index 0000000..28cc5cd --- /dev/null +++ b/database/migrations/2025_12_30_081926_add_pending_email_to_users_table.php @@ -0,0 +1,28 @@ +string('pending_email')->nullable()->after('email'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $label) { + $label->dropColumn('pending_email'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..3d213b9 --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,51 @@ + 'owner@trustlab.com', + ], [ + 'first_name' => 'Owner', + 'last_name' => 'User', + 'password' => $commonPassword, + 'role' => User::ROLE_OWNER, + 'email_verified_at' => now(), + ]); + + // 2. Admin + User::updateOrCreate([ + 'email' => 'admin@trustlab.com', + ], [ + 'first_name' => 'Admin', + 'last_name' => 'User', + 'password' => $commonPassword, + 'role' => User::ROLE_ADMIN, + 'email_verified_at' => now(), + ]); + + // 3. Customer + User::updateOrCreate([ + 'email' => 'customer@trustlab.com', + ], [ + 'first_name' => 'Customer', + 'last_name' => 'User', + 'password' => $commonPassword, + 'role' => User::ROLE_CUSTOMER, + 'email_verified_at' => now(), + ]); + } +} diff --git a/lang/vendor/cloudflare-turnstile/ar/errors.php b/lang/vendor/cloudflare-turnstile/ar/errors.php new file mode 100644 index 0000000..2928d5e --- /dev/null +++ b/lang/vendor/cloudflare-turnstile/ar/errors.php @@ -0,0 +1,12 @@ + 'لم يتم إرسال البيانات اللازمة للتحقق من Captcha.', + 'invalid-input-secret' => 'البيانات اللازمة للتحقق من Captcha غير صحيحة أو غير موجودة.', + 'missing-input-response' => 'لم يتم إرسال الرد المطلوب للتحقق من Captcha.', + 'invalid-input-response' => 'الرد المطلوب للتحقق من Captcha غير صالح أو انتهت صلاحيته.', + 'bad-request' => 'تم رفض الطلب لأنه غير مكتمل أو يحتوي على خطأ.', + 'timeout-or-duplicate' => 'تم استخدام رد Captcha هذا مسبقًا.', + 'internal-error' => 'حدث خطأ داخلي أثناء التحقق من Captcha.', + 'unexpected' => 'حدث خطأ غير متوقع أثناء التحقق من Captcha.', +]; diff --git a/lang/vendor/cloudflare-turnstile/en/errors.php b/lang/vendor/cloudflare-turnstile/en/errors.php new file mode 100644 index 0000000..e8d7dc8 --- /dev/null +++ b/lang/vendor/cloudflare-turnstile/en/errors.php @@ -0,0 +1,12 @@ + 'The secret parameter was not passed.', + 'invalid-input-secret' => 'The secret parameter was invalid or did not exist.', + 'missing-input-response' => 'The response parameter was not passed.', + 'invalid-input-response' => 'The response parameter is invalid or has expired.', + 'bad-request' => 'The request was rejected because it was malformed.', + 'timeout-or-duplicate' => 'The response parameter has already been validated before.', + 'internal-error' => 'An internal error happened while validating the response.', + 'unexpected' => 'An unexpected error occurred.', +]; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..57d104b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2573 @@ +{ + "name": "trustlab-api", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-echo": "^2.2.6", + "laravel-vite-plugin": "^2.0.0", + "pusher-js": "^8.4.0", + "tailwindcss": "^4.0.0", + "vite": "^7.0.7" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/laravel-echo": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.2.6.tgz", + "integrity": "sha512-KuCldOrE8qbm0CVDBgc6FiX3VuReDu1C1xaS891KqwEUg9NT/Op03iiZqTWeVd0/WJ4H95q2pe9QEDJlwb/FPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "pusher-js": "*", + "socket.io-client": "*" + } + }, + "node_modules/laravel-vite-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz", + "integrity": "sha512-zQuvzWfUKQu9oNVi1o0RZAJCwhGsdhx4NEOyrVQwJHaWDseGP9tl7XUPLY2T8Cj6+IrZ6lmyxlR1KC8unf3RLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^7.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pusher-js": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0.tgz", + "integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tweetnacl": "^1.0.3" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.2.tgz", + "integrity": "sha512-4MY14EMsyEPFA6lM01XIYepRdV8P6dUir2hxAlAysOYcbNAy5QNHYgIHOcQ1KYM7wTcKnKEW/ZRoIxRinWRXvA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/vite": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/vite-plugin-full-reload/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7b2b57f --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://www.schemastore.org/package.json", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "axios": "^1.11.0", + "concurrently": "^9.0.1", + "laravel-echo": "^2.2.6", + "laravel-vite-plugin": "^2.0.0", + "pusher-js": "^8.4.0", + "tailwindcss": "^4.0.0", + "vite": "^7.0.7" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..d703241 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,35 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..b574a59 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,25 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Handle X-XSRF-Token Header + RewriteCond %{HTTP:x-xsrf-token} . + RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..082320ae51c71937c14e462e48dbb9ba56df8f0f GIT binary patch literal 178553 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 literal 0 HcmV?d00001 diff --git a/public/images/logo/auth-logo.png b/public/images/logo/auth-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..322936b1b453cb6b0e3cf362beaaf68fbab06c2b GIT binary patch literal 5108 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
  • M-s z3CL;=x89CVWv4UKaS)t z>(UJE_%)J_=tMrXSUDYg2*uAWZM2a0C0!cD3b{!{kW%lRTT#j%b=Uv4adA1!VO0d_wsnb1MDu|KXZtaBcGl5rSW7q||Ip)JNc^}iIkI%jYFNcK%CP=>@$Hz8r1_egGD zdIVuVuSJsKuO~i^8$=Qe<{*TUKY`2u;tiORx!qQ>m~a7-VdO+9`h*V?<|5faP7Ful zmbDJbvMloME+|o`$ih|S_>e5&6LV~&ZPn+i&OU{YtH`c6qu{0dWZarCa!78$ZDrQw z=`aTUo~iS2-zC9^es38kN=RMuLN>CMNed)ST8w1>m|R#lIi~GHgLvQj*Ca1MaxQHc zS?Kp$vXFbIW8{yKctCQ(^p2I`(NhW&LdOW*fLIqc_KTYWC1iIE7C32TS8+!MD!$A1 zx+8lz)kmFYVzwZO@@RvkZh%M@707a^RLF7L;H#$OoxuH`rfPTpl)Yo&RVll5gil00 zlb8dBKj`MGO2r&$Yim}_xOuQc-Ib!aurRMGI|iI=3`PGh6*iN)tdlf;!teq3ENht? z;bVu9n9;bf@$9a>txcwjvSU(qWi9o5wbcvLYL4+#ds=WBry@dZZN{5`l{k#ym_J6^ zR+im$VPW;wGTJt%yzbUko#=yp&E^rj?=qLhX$l{}n=|hyQYeYJC1KUPSj$K|tz2S` zjka|tdyEBFDAe+)lkyU6l3bG*VFaBaJj_7XJ^$ W0fr32elt1%0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo-dark.png b/public/images/logo/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..95b945d4a227b56095c941b188597726ec774b22 GIT binary patch literal 3149 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo-icon.png b/public/images/logo/logo-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8dae70b8d359962fcf74c456a310a77ec4fd0062 GIT binary patch literal 1541 zcmV+g2KxDlP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11(Hcb zK~z|Ut(RGBRCN@_e`n@)0ouZ}P+Fk0QwjxJK$cPg5fFkAu@<2`2oaG*6P`pPJ{VII zU(}fJ0wN_!BG6D21|$svB9S0V%T|Rhv{8XlN=vbOr_20(n3+5G&P<0woQIkFpL4$R zo#mTz?}$7xBPyK8LQu=Z6WI%P2QU-6Kg`7XcLTz5RC5kmT~w6UW)VTci7XVSfKSzKUz@}a zi(;cDs2TlL{e&WWa;%W00k;2NWCI>mtR`Z}-Hxv0C}f;~AF5IQ@1 zsJq`10xO2Z%t-^Hg=(2Gz{`u$dFQQczS{m9xl1!xJU@+|o<2VP;uLlDEy1e}r6@41zxy6aIyVo@9{`P^^-zHn}>SRVG#87;)f~AYo2@A8**51vY z!cyH=DOmqz7Skq=qOHA~&$pdn^M={9wmJFi%hQG=`WL8m;*{Kcqfo@-UU92|sTqmP znx4e*U#hSt+lU$x&e$=-xKMH%1t_H`xqO$*XA(#p8BOM-k$m&rC0<%Ifwa^(E|%Ou zDepLZRU0*7O`n{bAMD2A>T_}V`aR~%N@m)WQ5-2QCw=^Il12|>=+H>6T&qO@m&?V~ z>vd$$NFshj6k|pYrSOOAy!J{4v9W`>T2|YyKoP+@amw=i{)FOm_HzA39rNd=lAbo4 zV?S3gDLtNX_Ba$!UfH1gT`8+&&a7k-6Jm%6*En{(oE15lXqttaY7H=n7x{?PMW$r-+vjImWM#&!*=f1x3b{*aYRQ&P;%uisVT8c z%}nG@Z8P`kS^;?UZzuQaTFIW7M5;ZGU(eRi;LmTK#=lrGH zR8>3BtQOkZog6K`2?)KtE|YK5qYk>ed#S8$q^;G-yxDd{goOBL6OWfyGk@tj_`2Xb zH8pkUWSyP8=8cD7PqDG}y#+jK?qdIet8Cq}fbeiDhmQV<?t-r+OiPMD(omY&!)GtiM0*pN5>x=DZXirXTa)<$XvD0!@p(zn+ySavYUL! z;4rprKgZG~>7=H_vA^gV$4^y;#A;7nwddd^j~WtA(V;TzDK?(VN@m}ItDHDp6;w~g zoum0z9gH|w<^Y2tG#1TI<3@Qs^$%JpxpJ44xtZ+RbCKWA{}VtS&}{+9Tl>AsBsJaV zF6AZDR^+9{wcAWgxxV6c54Mk2p^e5hR_L9iZNqeX;LFW7$z`rkP8fJRLW z6f1#Pff5T!6@=P?A*(iwCN$PStYZZQy9Jc2T36_>pmK}>gU;2TShtTy+}$L^hZ z%PmTw)F5p&SdIif&jn`g-)^rpWYzj;yjbPvj_z6mxc?n{U#mCne;pSa5Sb~+2xBG2 rs-N`H+k%N_PYNE!Z)HZ<0SRs00000NkvXXu0mjf2lm^u literal 0 HcmV?d00001 diff --git a/public/images/logo/logo-icon.svg b/public/images/logo/logo-icon.svg new file mode 100644 index 0000000..6e0d87e --- /dev/null +++ b/public/images/logo/logo-icon.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo/logo.png b/public/images/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..54352752bbe81bcdfc9bd81b043a4f9797ec18cd GIT binary patch literal 3316 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..ee8f07e --- /dev/null +++ b/public/index.php @@ -0,0 +1,20 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/test-api.php b/public/test-api.php new file mode 100644 index 0000000..bbacf4e --- /dev/null +++ b/public/test-api.php @@ -0,0 +1,10 @@ + + + + Certificate Expired Alert + + +
    +

    URGENT: Certificate Has Expired

    + +

    Hello {{ $certificate->user->first_name ?? 'User' }},

    + +

    This is a critical notification that your SSL certificate has ALREADY EXPIRED.

    + +
    +

    Common Name: {{ $certificate->common_name }}

    +

    Organization: {{ $certificate->organization }}

    +

    Key Strength: {{ $certificate->key_bits }}-bit

    +

    Expired On: {{ $certificate->valid_to->format('d M Y H:i:s') }}

    +
    + +

    Your services using this certificate may be inaccessible or showing security warnings. Please renew immediately.

    + + Renew Now +
    + + diff --git a/resources/views/emails/certificate-expiring.blade.php b/resources/views/emails/certificate-expiring.blade.php new file mode 100644 index 0000000..1a77bbd --- /dev/null +++ b/resources/views/emails/certificate-expiring.blade.php @@ -0,0 +1,31 @@ + + + + Certificate Expiration Alert + + +
    +

    Action Required: Certificate Expiring Soon

    + +

    Hello {{ $certificate->user->first_name ?? 'User' }},

    + +

    This is a notification that one of your SSL certificates is expiring in {{ $daysRemaining }} days.

    + +
    +

    Common Name: {{ $certificate->common_name }}

    +

    Organization: {{ $certificate->organization }}

    +

    Key Strength: {{ $certificate->key_bits }}-bit

    +

    Expiration Date: {{ $certificate->valid_to->format('d M Y H:i:s') }}

    +
    + +

    Please log in to your dashboard to renew this certificate before it expires to ensure uninterrupted service.

    + + Go to Dashboard + +

    + If you have already renewed this certificate, please ignore this message.
    + You are receiving this email because you have enabled certificate renewal alerts in your account settings. +

    +
    + + diff --git a/resources/views/emails/inquiry_reply.blade.php b/resources/views/emails/inquiry_reply.blade.php new file mode 100644 index 0000000..d4bb4c4 --- /dev/null +++ b/resources/views/emails/inquiry_reply.blade.php @@ -0,0 +1,84 @@ + + + + + + + +
    +
    +

    TrustLab Support

    +
    +
    +

    Hello {{ $inquiry->name }},

    + +
    + {!! nl2br(e($replyMessage)) !!} +
    + +

    Best regards,
    The TrustLab Team

    + +
    +

    Original Message:

    +

    {{ $inquiry->message }}

    +
    +
    + +
    + + diff --git a/resources/views/emails/password-reset.blade.php b/resources/views/emails/password-reset.blade.php new file mode 100644 index 0000000..c4d9f80 --- /dev/null +++ b/resources/views/emails/password-reset.blade.php @@ -0,0 +1,90 @@ + + + + + + + +
    +
    +

    Reset Your Password

    +
    +
    +

    Hello {{ $name }},

    + +

    You are receiving this email because we received a password reset request for your TrustLab account.

    + + Reset Password + +

    This password reset link will expire in 60 minutes.

    +

    If you did not request a password reset, no further action is required.

    + + +
    + +
    + + diff --git a/resources/views/emails/test.blade.php b/resources/views/emails/test.blade.php new file mode 100644 index 0000000..5437710 --- /dev/null +++ b/resources/views/emails/test.blade.php @@ -0,0 +1,110 @@ + + + + + + TrustLab Connection Test + + + +
    +
    + + TrustLab Logo +
    +
    +

    SMTP Connection Test

    +
    Connection Successful
    +

    Hello,

    +

    This is a test email sent from TrustLab - PKI & Certificate Management to verify your SMTP configuration.

    + +
    +

    Mailer: {{ $mailer }}

    +

    Sent At: {{ now()->format('Y-m-d H:i:s') }}

    +

    Host: {{ $host }}

    +
    + +

    If you received this email, it means your SMTP settings for the {{ $mailer }} mailer are working correctly.

    + + Go to Dashboard +
    + +
    + + diff --git a/resources/views/emails/verify-email.blade.php b/resources/views/emails/verify-email.blade.php new file mode 100644 index 0000000..848f486 --- /dev/null +++ b/resources/views/emails/verify-email.blade.php @@ -0,0 +1,89 @@ + + + + + + + +
    +
    +

    TrustLab Verification

    +
    +
    +

    Hello {{ $name }},

    + +

    Thank you for joining TrustLab! Before you can start managing your certificates and API keys, we need you to verify your email address.

    + + Verify Email Address + +

    If you did not create an account, no further action is required.

    + + +
    + +
    + + diff --git a/resources/views/vendor/cloudflare-turnstile/.gitkeep b/resources/views/vendor/cloudflare-turnstile/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/vendor/cloudflare-turnstile/components/scripts.blade.php b/resources/views/vendor/cloudflare-turnstile/components/scripts.blade.php new file mode 100644 index 0000000..dbcc379 --- /dev/null +++ b/resources/views/vendor/cloudflare-turnstile/components/scripts.blade.php @@ -0,0 +1 @@ + diff --git a/resources/views/vendor/cloudflare-turnstile/components/turnstile.blade.php b/resources/views/vendor/cloudflare-turnstile/components/turnstile.blade.php new file mode 100644 index 0000000..7d80a6f --- /dev/null +++ b/resources/views/vendor/cloudflare-turnstile/components/turnstile.blade.php @@ -0,0 +1,45 @@ +@props([ + 'id' => 'captcha', +]) + +@php +if (! preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $id)) { + throw new InvalidArgumentException("The Turnstile ID [{$id}] must start with a letter or underscore, and can only contain alphanumeric or underscore characters."); +} + +$model = $attributes->has('wire:model') ? $attributes->get('wire:model') : null; +@endphp + +
    filter(fn($value, $key) => ! in_array($key, ['data-callback', 'data-expired-callback', 'data-timeout-callback', 'wire:model', 'id']))->class(['cf-turnstile']) }} + @else + {{ $attributes->class(['cf-turnstile']) }} + @endif +>
    + +@if ($model) + +@endif diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000..b7355d7 --- /dev/null +++ b/resources/views/welcome.blade.php @@ -0,0 +1,277 @@ + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) + @else + + @endif + + +
    + @if (Route::has('login')) + + @endif +
    +
    +
    +
    +

    Let's get started

    +

    Laravel has an incredibly rich ecosystem.
    We suggest starting with the following.

    + + +
    +
    + {{-- Laravel Logo --}} + + + + + + + + + + + {{-- Light Mode 12 SVG --}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{-- Dark Mode 12 SVG --}} + +
    +
    +
    +
    + + @if (Route::has('login')) + + @endif + + diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..74b13b8 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,126 @@ +group(function () { + Route::delete('/auth/social/{provider}', [AuthController::class, 'disconnectSocial']); + Route::post('/auth/set-password', [AuthController::class, 'setPassword']); + Route::get('/auth/link-token', [AuthController::class, 'getLinkToken']); + + Route::get('/user', function (Request $request) { + return $request->user()->load('socialAccounts'); + }); + Route::get('/services', [ServiceController::class, 'index']); + Route::get('/navigation', [NavigationController::class, 'index']); + + // Core Features (Require Email Verification) + Route::middleware(['verified'])->group(function () { + // Certificate Routes + Route::get('/certificates', [CertificateApiController::class, 'index']); + Route::post('/certificates', [CertificateApiController::class, 'store']); + Route::get('/certificates/{certificate}', [CertificateApiController::class, 'show']); + Route::delete('/certificates/{certificate}', [CertificateApiController::class, 'destroy']); + Route::get('/certificates/{certificate}/download/{type}', [CertificateApiController::class, 'downloadFile']); + + // CA Management (Admin) + Route::post('/ca/setup', [CertificateApiController::class, 'setupCa']); + + // Root CA Management (Admin Only) + Route::get('/admin/ca-certificates', [RootCaApiController::class, 'index']); + Route::post('/admin/ca-certificates/{certificate}/renew', [RootCaApiController::class, 'renew']); + + // API Keys Management + Route::get('/api-keys', [ApiKeyController::class, 'index']); + Route::post('/api-keys', [ApiKeyController::class, 'store']); + Route::delete('/api-keys/{id}', [ApiKeyController::class, 'destroy']); + Route::patch('/api-keys/{id}/toggle', [ApiKeyController::class, 'toggle']); + Route::post('/api-keys/{id}/regenerate', [ApiKeyController::class, 'regenerate']); + + // Profile Management (Sensitive parts) + Route::patch('/profile', [ProfileController::class, 'update']); + Route::put('/profile/password', [ProfileController::class, 'updatePassword']); + Route::post('/profile/avatar', [ProfileController::class, 'updateAvatar']); + Route::get('/profile/login-history', [ProfileController::class, 'getLoginHistory']); + Route::delete('/profile', [ProfileController::class, 'deleteAccount']); + Route::get('/profile/sessions', [ProfileController::class, 'getActiveSessions']); + Route::delete('/profile/sessions/{id}', [ProfileController::class, 'revokeSession']); + + // Notifications + Route::get('/notifications', [\App\Http\Controllers\Api\NotificationController::class, 'index']); + Route::patch('/notifications/{id}/read', [\App\Http\Controllers\Api\NotificationController::class, 'markAsRead']); + Route::post('/notifications/mark-all-read', [\App\Http\Controllers\Api\NotificationController::class, 'markAllAsRead']); + Route::delete('/notifications/{id}', [\App\Http\Controllers\Api\NotificationController::class, 'destroy']); + + // Dashboard + Route::get('/dashboard', [DashboardController::class, 'index']); + Route::get('/dashboard/ping', [DashboardController::class, 'ping']); + + // Support Tickets + Route::get('/support/tickets', [\App\Http\Controllers\Api\TicketController::class, 'index']); + Route::post('/support/tickets', [\App\Http\Controllers\Api\TicketController::class, 'store']); + Route::get('/support/tickets/{id}', [\App\Http\Controllers\Api\TicketController::class, 'show']); + Route::post('/support/tickets/{id}/reply', [\App\Http\Controllers\Api\TicketController::class, 'reply']); + Route::patch('/support/tickets/{id}/close', [\App\Http\Controllers\Api\TicketController::class, 'close']); + + // User Management (Admin Only) + Route::apiResource('/admin/users', UserApiController::class); + + // Inquiry Management (Admin Only) + Route::middleware(['admin'])->group(function () { + Route::get('/admin/inquiries', [\App\Http\Controllers\Api\InquiryController::class, 'index']); + Route::get('/admin/inquiries/{inquiry}', [\App\Http\Controllers\Api\InquiryController::class, 'show']); + Route::post('/admin/inquiries/{inquiry}/reply', [\App\Http\Controllers\Api\InquiryController::class, 'reply']); + Route::delete('/admin/inquiries/{inquiry}', [\App\Http\Controllers\Api\InquiryController::class, 'destroy']); + }); + + // SMTP Testing (Admin Only) + Route::middleware(['admin'])->group(function () { + Route::get('/admin/smtp/config', [MailController::class, 'getConfigurations']); + Route::post('/admin/smtp/test', [MailController::class, 'sendTestEmail']); + }); + + // Legal Pages Management (Admin Only) + Route::middleware(['admin'])->group(function () { + Route::get('/admin/legal-pages', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'index']); + Route::post('/admin/legal-pages', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'store']); + Route::get('/admin/legal-pages/{legalPage}', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'show']); + Route::get('/admin/legal-pages/{id}/history', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'getHistory']); + Route::put('/admin/legal-pages/{legalPage}', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'update']); + Route::delete('/admin/legal-pages/{legalPage}', [\App\Http\Controllers\Api\Admin\LegalPageController::class, 'destroy']); + }); + }); + + +}); + +// Authenticated API Routes (v1) - Using API Key +Route::middleware([\App\Http\Middleware\CheckApiKey::class])->prefix('v1')->group(function () { + Route::get('/certificates', [CertificateApiController::class, 'index']); +}); diff --git a/routes/channels.php b/routes/channels.php new file mode 100644 index 0000000..57bea24 --- /dev/null +++ b/routes/channels.php @@ -0,0 +1,7 @@ +id === (string) $id; +}); diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..a6876c3 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,11 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); + +Schedule::command('certificates:notify-expiring')->daily(); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..44d411e --- /dev/null +++ b/routes/web.php @@ -0,0 +1,42 @@ +json(['status' => 'ok', 'service' => 'trustlab-api']); +}); + +use App\Http\Controllers\AuthController; + +Route::post('/login', [AuthController::class, 'login']); +Route::post('/logout', [AuthController::class, 'logout']); +Route::post('/register', [AuthController::class, 'register']); + +use App\Http\Controllers\Api\PasswordResetController; +Route::post('/forgot-password', [PasswordResetController::class, 'sendResetLinkEmail']); +Route::post('/reset-password', [PasswordResetController::class, 'resetPassword']); + +// Social Auth Routes (Manually prefixed with /api to match frontend/callback config, but served via web middleware) +Route::get('/api/auth/{provider}/redirect', [AuthController::class, 'socialRedirect'])->name('social.redirect'); +Route::get('/api/auth/{provider}/callback', [AuthController::class, 'socialCallback'])->name('social.callback'); + +use App\Http\Controllers\TwoFactorController; + +// 2FA Routes (Protected by Sanctum - supports both full auth and temp 2fa auth) +Route::middleware('auth:sanctum')->group(function () { + Route::post('/api/auth/2fa/enable', [TwoFactorController::class, 'enable']); + Route::post('/api/auth/2fa/confirm', [TwoFactorController::class, 'confirm']); + Route::delete('/api/auth/2fa/disable', [TwoFactorController::class, 'disable']); + Route::post('/api/auth/2fa/verify', [TwoFactorController::class, 'verify']); + Route::get('/api/auth/2fa/recovery-codes', [TwoFactorController::class, 'recoveryCodes']); +}); + +use App\Http\Controllers\Api\VerificationController; + +Route::get('/email/verify/{id}/{hash}', [VerificationController::class, 'verify']) + ->middleware(['auth:sanctum', 'signed']) + ->name('verification.verify'); + +Route::post('/api/email/verification-notification', [VerificationController::class, 'resend']) + ->middleware(['auth:sanctum', 'throttle:6,1']) + ->name('verification.send'); diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000..fedb287 --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,4 @@ +* +!private/ +!public/ +!.gitignore diff --git a/storage/app/private/.gitignore b/storage/app/private/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/private/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/cache/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/Feature/InquiryNotificationTest.php b/tests/Feature/InquiryNotificationTest.php new file mode 100644 index 0000000..fa15ab2 --- /dev/null +++ b/tests/Feature/InquiryNotificationTest.php @@ -0,0 +1,53 @@ +first(); + if (!$admin) { + $admin = User::factory()->create([ + 'role' => 'admin', + 'email' => 'admin_test_'.time().'@example.com', + ]); + } + + // Clear previous notifications for clean test? + // $admin->notifications()->delete(); + + $initialCount = $admin->notifications()->count(); + + // 2. Send Request + $response = $this->postJson('/api/public/inquiries', [ + 'name' => 'John Doe', + 'email' => 'john@example.com', + 'subject' => 'Test Inquiry Subject', + 'message' => 'This is a test message for notification.', + 'category' => 'General', + ]); + + $response->assertStatus(201); + + // 3. Verify Notification in Database + // We re-fetch admin to see new notifications + $finalCount = $admin->notifications()->count(); + + $this->assertTrue($finalCount > $initialCount, "Notification count did not increase. Initial: $initialCount, Final: $finalCount"); + + $latest = $admin->notifications()->latest()->first(); + $this->assertEquals('App\Notifications\NewInquiryNotification', $latest->type); + $this->assertEquals('New inquiry from John Doe: Test Inquiry Subject', $latest->data['message']); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..f35b4e7 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + tailwindcss(), + ], + server: { + watch: { + ignored: ['**/storage/framework/views/**'], + }, + }, +});