From 8d6ddb443464cc121160a5de153dddf12b163bee Mon Sep 17 00:00:00 2001 From: Will Browning Date: Wed, 4 Oct 2023 11:32:39 +0100 Subject: [PATCH] Rebrand update --- .env.example | 12 +- .gitignore | 1 + README.md | 80 +- SECURITY.md | 4 +- .../Commands/CheckDomainsMxValidation.php | 3 +- .../CheckDomainsSendingVerification.php | 3 +- .../Commands/ClearOutboundMessages.php | 35 + app/Console/Commands/ClearPostfixQueueIds.php | 43 - .../EmailUsersWithTokenExpiringSoon.php | 63 + app/Console/Commands/ReceiveEmail.php | 286 +- app/Console/Commands/ResetBandwidth.php | 2 +- app/Console/Kernel.php | 4 +- app/CustomMailDriver/CustomMailer.php | 49 +- .../Mime/Part/EncryptedPart.php | 12 +- .../Mime/Part/InlineImagePart.php | 19 +- app/Enums/DisplayFromFormat.php | 14 + app/Helpers/Helper.php | 14 + .../Controllers/AliasExportController.php | 4 + .../Controllers/AliasImportController.php | 2 +- .../Controllers/Api/AliasBulkController.php | 252 ++ app/Http/Controllers/Api/AliasController.php | 14 +- .../Controllers/Api/ChartDataController.php | 62 + app/Http/Controllers/Api/DomainController.php | 10 +- .../Api/LoginableUsernameController.php | 34 + .../Controllers/Api/UsernameController.php | 10 +- .../Auth/ApiAuthenticationController.php | 2 +- .../Controllers/Auth/BackupCodeController.php | 11 +- .../Auth/ForgotPasswordController.php | 15 +- .../Auth/ForgotUsernameController.php | 9 + app/Http/Controllers/Auth/LoginController.php | 60 +- .../Auth/PersonalAccessTokenController.php | 6 + .../Controllers/Auth/RegisterController.php | 26 +- .../Auth/ResetPasswordController.php | 7 +- .../Auth/TwoFactorAuthController.php | 11 +- .../Auth/VerificationController.php | 46 +- .../Controllers/Auth/WebauthnController.php | 97 +- .../Auth/WebauthnEnabledKeyController.php | 6 +- .../Controllers/BannerLocationController.php | 2 +- .../Controllers/BrowserSessionController.php | 6 +- .../Controllers/DeactivateAliasController.php | 2 +- .../Controllers/DefaultAliasController.php | 16 - .../DefaultAliasDomainController.php | 2 +- .../DefaultAliasFormatController.php | 2 +- .../DefaultRecipientController.php | 29 +- .../Controllers/DefaultUsernameController.php | 22 + .../DisplayFromFormatController.php | 17 + .../Controllers/EmailSubjectController.php | 2 +- app/Http/Controllers/FromNameController.php | 6 +- app/Http/Controllers/PasswordController.php | 6 +- app/Http/Controllers/SettingController.php | 62 +- app/Http/Controllers/ShowAliasController.php | 206 +- .../Controllers/ShowDashboardController.php | 37 + app/Http/Controllers/ShowDomainController.php | 48 +- .../ShowFailedDeliveryController.php | 36 +- .../Controllers/ShowRecipientController.php | 96 +- app/Http/Controllers/ShowRuleController.php | 18 +- .../Controllers/ShowUsernameController.php | 48 +- .../StoreFailedDeliveryController.php | 2 +- app/Http/Controllers/UseReplyToController.php | 2 +- app/Http/Kernel.php | 1 + app/Http/Middleware/HandleInertiaRequests.php | 62 + app/Http/Requests/DestroyAccountRequest.php | 2 +- .../Requests/EditDefaultRecipientRequest.php | 2 +- .../Requests/EnableTwoFactorAuthRequest.php | 1 + app/Http/Requests/StoreAliasRequest.php | 11 +- .../StorePersonalAccessTokenRequest.php | 4 + ...t.php => UpdateAccountFromNameRequest.php} | 2 +- app/Http/Requests/UpdateAliasRequest.php | 1 + .../UpdateDefaultRecipientRequest.php | 2 +- .../Requests/UpdateDefaultUsernameRequest.php | 28 + .../UpdateDisplayFromFormatRequest.php | 34 + app/Http/Requests/UpdateDomainRequest.php | 1 + app/Http/Requests/UpdatePasswordRequest.php | 9 +- app/Http/Requests/UpdateUsernameRequest.php | 1 + app/Http/Resources/AliasResource.php | 1 + app/Http/Resources/DomainResource.php | 1 + app/Http/Resources/UserResource.php | 1 + app/Http/Resources/UsernameResource.php | 2 + .../Responses/RegisterSuccessResponse.php | 24 + app/Imports/AliasesImport.php | 4 +- app/Jobs/DeleteAccount.php | 2 +- app/Mail/ForwardEmail.php | 63 +- app/Mail/ReplyToEmail.php | 20 +- app/Mail/SendFromEmail.php | 20 +- app/Mail/TokenExpiringSoon.php | 8 +- app/Models/Alias.php | 49 +- app/Models/Domain.php | 4 +- app/Models/EmailData.php | 2 +- app/Models/FailedDelivery.php | 39 +- app/Models/OutboundMessage.php | 63 + app/Models/PostfixQueueId.php | 29 - app/Models/Recipient.php | 66 +- app/Models/Rule.php | 2 +- app/Models/User.php | 105 +- app/Models/Username.php | 22 +- .../AliasesImportedNotification.php | 2 +- app/Notifications/CustomResetPassword.php | 140 + app/Notifications/CustomVerifyEmail.php | 5 +- app/Notifications/DefaultRecipientUpdated.php | 4 +- .../DisallowedReplySendAttempt.php | 4 +- app/Notifications/DomainMxRecordsInvalid.php | 8 +- .../DomainUnverifiedForSending.php | 6 +- .../FailedDeliveryNotification.php | 6 +- app/Notifications/GpgKeyExpired.php | 7 +- .../IncorrectOtpNotification.php | 4 +- app/Notifications/NearBandwidthLimit.php | 4 +- app/Notifications/SpamReplySendAttempt.php | 4 +- app/Notifications/UsernameReminder.php | 6 +- app/Providers/AppServiceProvider.php | 33 + app/Services/CredentialRepository.php | 53 + composer.json | 14 +- composer.lock | 1427 +++++----- config/hashing.php | 2 +- config/sanctum.php | 15 + config/webauthn.php | 12 +- config/ziggy.php | 20 + database/factories/UsernameFactory.php | 2 +- ...13130_add_defer_columns_to_users_table.php | 34 + ...add_reject_until_column_to_users_table.php | 32 + ..._135311_create_outbound_messages_table.php | 39 + ...destination_to_failed_deliveries_table.php | 32 + ...31_110006_drop_postfix_queue_ids_table.php | 34 + ...ndex_for_aliasable_id_to_aliases_table.php | 32 + ...023_05_31_131832_add_from_name_columns.php | 44 + ...add_pending_column_to_recipients_table.php | 28 + ...55657_add_can_login_to_usernames_table.php | 28 + ...add_display_from_format_to_users_table.php | 29 + package-lock.json | 2325 +++++++++++------ package.json | 30 +- postcss.config.js | 6 + postfix/AccessPolicy.php | 352 +++ postfix/composer.json | 7 + postfix/composer.lock | 1659 ++++++++++++ public/android-chrome-192x192.png | Bin 9003 -> 6521 bytes public/android-chrome-256x256.png | Bin 12799 -> 0 bytes public/android-chrome-512x512.png | Bin 0 -> 19348 bytes public/api-logo.png | Bin 0 -> 5393 bytes public/apple-touch-icon.png | Bin 8358 -> 6514 bytes public/favicon-16x16.png | Bin 841 -> 882 bytes public/favicon-32x32.png | Bin 1257 -> 1492 bytes public/favicon.ico | Bin 7406 -> 15086 bytes public/logo.png | Bin 6831 -> 15227 bytes public/mstile-144x144.png | Bin 0 -> 6042 bytes public/mstile-150x150.png | Bin 6358 -> 6327 bytes public/mstile-310x150.png | Bin 0 -> 6876 bytes public/mstile-310x310.png | Bin 0 -> 13437 bytes public/mstile-70x70.png | Bin 0 -> 4521 bytes public/safari-pinned-tab.svg | 47 +- public/site.webmanifest | 12 +- public/svg/icon-logo.svg | 67 +- public/svg/logo-dark.svg | 124 +- public/svg/logo.svg | 124 +- resources/css/app.css | 26 + .../js/Components/BannerNotification.vue | 44 + .../js/{components => Components}/Icon.vue | 31 +- .../js/{components => Components}/Loader.vue | 0 .../js/{components => Components}/Modal.vue | 6 +- .../js/{components => Components}/Toggle.vue | 0 resources/js/Layouts/AppLayout.vue | 535 ++++ resources/js/Layouts/SettingsLayout.vue | 98 + resources/js/Pages/Aliases/Edit.vue | 233 ++ resources/js/Pages/Aliases/Index.vue | 2320 ++++++++++++++++ resources/js/Pages/Auth/Verify.vue | 59 + resources/js/Pages/Dashboard/Index.vue | 349 +++ .../Pages/Dashboard/OutboundMessagesGraph.vue | 144 + .../Pages/Dashboard/OutboundMessagesPie.vue | 41 + resources/js/Pages/Domains/Edit.vue | 273 ++ resources/js/Pages/Domains/Index.vue | 1037 ++++++++ resources/js/Pages/FailedDeliveries.vue | 342 +++ resources/js/Pages/Recipients/Edit.vue | 305 +++ resources/js/Pages/Recipients/Index.vue | 862 ++++++ resources/js/{pages => Pages}/Rules.vue | 1245 +++++---- resources/js/Pages/Settings/Account.vue | 136 + resources/js/Pages/Settings/Api.vue | 395 +++ resources/js/Pages/Settings/Data.vue | 140 + resources/js/Pages/Settings/General.vue | 910 +++++++ resources/js/Pages/Settings/Security.vue | 1025 ++++++++ resources/js/Pages/Usernames/Edit.vue | 270 ++ resources/js/Pages/Usernames/Index.vue | 791 ++++++ resources/js/app.js | 114 +- resources/js/bootstrap.js | 7 +- resources/js/components/DropdownNav.vue | 43 - resources/js/components/MoreOptions.vue | 35 - resources/js/components/WebauthnKeys.vue | 165 -- .../sanctum/PersonalAccessTokens.vue | 372 --- resources/js/pages/Aliases.vue | 1345 ---------- resources/js/pages/Domains.vue | 844 ------ resources/js/pages/FailedDeliveries.vue | 327 --- resources/js/pages/Recipients.vue | 850 ------ resources/js/pages/Usernames.vue | 655 ----- resources/js/webauthn/authenticate.js | 71 + resources/js/webauthn/register.js | 76 + resources/js/{ => webauthn}/webauthn.js | 24 +- resources/views/aliases/index.blade.php | 9 - resources/views/auth/backup_code.blade.php | 6 +- resources/views/auth/login.blade.php | 43 +- .../views/auth/passwords/email.blade.php | 37 +- .../views/auth/passwords/reset.blade.php | 14 +- resources/views/auth/register.blade.php | 22 +- resources/views/auth/two_factor.blade.php | 4 +- .../views/auth/usernames/email.blade.php | 26 +- resources/views/domains/index.blade.php | 9 - resources/views/emails/forward/html.blade.php | 5 +- .../forward/html_spam_warning.blade.php | 7 + .../views/failed_deliveries/index.blade.php | 9 - resources/views/layouts/app.blade.php | 29 +- resources/views/layouts/auth.blade.php | 2 +- .../mail/domain_mx_records_invalid.blade.php | 2 +- .../domain_unverified_for_sending.blade.php | 4 +- .../views/mail/failed_login_attempt.blade.php | 2 +- .../views/mail/token_expiring_soon.blade.php | 13 + resources/views/nav/nav.blade.php | 59 - resources/views/recipients/index.blade.php | 9 - resources/views/rules/index.blade.php | 9 - resources/views/settings/show.blade.php | 833 ------ resources/views/shared/status.blade.php | 5 - resources/views/usernames/index.blade.php | 9 - .../vendor/webauthn/authenticate.blade.php | 88 +- .../views/vendor/webauthn/register.blade.php | 86 +- routes/api.php | 23 + routes/web.php | 45 +- tailwind.config.js | 8 +- tests/Feature/Api/AliasesTest.php | 300 ++- tests/Feature/Api/ApiTokensTest.php | 24 +- tests/Feature/Api/DomainsTest.php | 15 + tests/Feature/Api/RecipientsTest.php | 6 +- tests/Feature/Api/RulesTest.php | 2 - tests/Feature/Api/UsernamesTest.php | 58 +- tests/Feature/ApiAuthenticationTest.php | 11 +- tests/Feature/LoginTest.php | 10 +- tests/Feature/ReceiveEmailTest.php | 8 +- tests/Feature/ReplyToEmailTest.php | 10 +- tests/Feature/SendFromEmailTest.php | 10 +- tests/Feature/SettingsTest.php | 316 ++- tests/Feature/ShowAliasesTest.php | 59 +- tests/Feature/ShowDomainsTest.php | 27 +- tests/Feature/ShowFailedDeliveriesTest.php | 30 +- tests/Feature/ShowRecipientsTest.php | 36 +- tests/Feature/ShowUsernamesTest.php | 27 +- tests/TestCase.php | 43 +- tests/Unit/AliasTest.php | 4 +- .../EmailUsersWithTokenExpiringSoonTest.php | 53 + tests/Unit/UserTest.php | 7 +- vite.config.js | 46 + 244 files changed, 19717 insertions(+), 8893 deletions(-) create mode 100644 app/Console/Commands/ClearOutboundMessages.php delete mode 100644 app/Console/Commands/ClearPostfixQueueIds.php create mode 100644 app/Console/Commands/EmailUsersWithTokenExpiringSoon.php create mode 100644 app/Enums/DisplayFromFormat.php create mode 100644 app/Http/Controllers/Api/AliasBulkController.php create mode 100644 app/Http/Controllers/Api/ChartDataController.php create mode 100644 app/Http/Controllers/Api/LoginableUsernameController.php delete mode 100644 app/Http/Controllers/DefaultAliasController.php create mode 100644 app/Http/Controllers/DefaultUsernameController.php create mode 100644 app/Http/Controllers/DisplayFromFormatController.php create mode 100644 app/Http/Controllers/ShowDashboardController.php create mode 100644 app/Http/Middleware/HandleInertiaRequests.php rename app/Http/Requests/{UpdateFromNameRequest.php => UpdateAccountFromNameRequest.php} (89%) create mode 100644 app/Http/Requests/UpdateDefaultUsernameRequest.php create mode 100644 app/Http/Requests/UpdateDisplayFromFormatRequest.php create mode 100644 app/Http/Responses/RegisterSuccessResponse.php create mode 100644 app/Models/OutboundMessage.php delete mode 100644 app/Models/PostfixQueueId.php create mode 100644 app/Notifications/CustomResetPassword.php create mode 100644 app/Services/CredentialRepository.php create mode 100644 config/ziggy.php create mode 100644 database/migrations/2022_11_11_113130_add_defer_columns_to_users_table.php create mode 100644 database/migrations/2022_11_21_164512_add_reject_until_column_to_users_table.php create mode 100644 database/migrations/2022_11_24_135311_create_outbound_messages_table.php create mode 100644 database/migrations/2023_01_20_111533_add_destination_to_failed_deliveries_table.php create mode 100644 database/migrations/2023_01_31_110006_drop_postfix_queue_ids_table.php create mode 100644 database/migrations/2023_03_24_151053_add_index_for_aliasable_id_to_aliases_table.php create mode 100644 database/migrations/2023_05_31_131832_add_from_name_columns.php create mode 100644 database/migrations/2023_06_05_111322_add_pending_column_to_recipients_table.php create mode 100644 database/migrations/2023_06_29_155657_add_can_login_to_usernames_table.php create mode 100644 database/migrations/2023_06_30_101305_add_display_from_format_to_users_table.php create mode 100644 postcss.config.js create mode 100644 postfix/AccessPolicy.php create mode 100644 postfix/composer.json create mode 100644 postfix/composer.lock delete mode 100644 public/android-chrome-256x256.png create mode 100644 public/android-chrome-512x512.png create mode 100644 public/api-logo.png create mode 100644 public/mstile-144x144.png create mode 100644 public/mstile-310x150.png create mode 100644 public/mstile-310x310.png create mode 100644 public/mstile-70x70.png create mode 100644 resources/js/Components/BannerNotification.vue rename resources/js/{components => Components}/Icon.vue (97%) rename resources/js/{components => Components}/Loader.vue (100%) rename resources/js/{components => Components}/Modal.vue (91%) rename resources/js/{components => Components}/Toggle.vue (100%) create mode 100644 resources/js/Layouts/AppLayout.vue create mode 100644 resources/js/Layouts/SettingsLayout.vue create mode 100644 resources/js/Pages/Aliases/Edit.vue create mode 100644 resources/js/Pages/Aliases/Index.vue create mode 100644 resources/js/Pages/Auth/Verify.vue create mode 100644 resources/js/Pages/Dashboard/Index.vue create mode 100644 resources/js/Pages/Dashboard/OutboundMessagesGraph.vue create mode 100644 resources/js/Pages/Dashboard/OutboundMessagesPie.vue create mode 100644 resources/js/Pages/Domains/Edit.vue create mode 100644 resources/js/Pages/Domains/Index.vue create mode 100644 resources/js/Pages/FailedDeliveries.vue create mode 100644 resources/js/Pages/Recipients/Edit.vue create mode 100644 resources/js/Pages/Recipients/Index.vue rename resources/js/{pages => Pages}/Rules.vue (50%) create mode 100644 resources/js/Pages/Settings/Account.vue create mode 100644 resources/js/Pages/Settings/Api.vue create mode 100644 resources/js/Pages/Settings/Data.vue create mode 100644 resources/js/Pages/Settings/General.vue create mode 100644 resources/js/Pages/Settings/Security.vue create mode 100644 resources/js/Pages/Usernames/Edit.vue create mode 100644 resources/js/Pages/Usernames/Index.vue delete mode 100644 resources/js/components/DropdownNav.vue delete mode 100644 resources/js/components/MoreOptions.vue delete mode 100644 resources/js/components/WebauthnKeys.vue delete mode 100644 resources/js/components/sanctum/PersonalAccessTokens.vue delete mode 100644 resources/js/pages/Aliases.vue delete mode 100644 resources/js/pages/Domains.vue delete mode 100644 resources/js/pages/FailedDeliveries.vue delete mode 100644 resources/js/pages/Recipients.vue delete mode 100644 resources/js/pages/Usernames.vue create mode 100644 resources/js/webauthn/authenticate.js create mode 100644 resources/js/webauthn/register.js rename resources/js/{ => webauthn}/webauthn.js (94%) delete mode 100644 resources/views/aliases/index.blade.php delete mode 100644 resources/views/domains/index.blade.php create mode 100644 resources/views/emails/forward/html_spam_warning.blade.php delete mode 100644 resources/views/failed_deliveries/index.blade.php create mode 100644 resources/views/mail/token_expiring_soon.blade.php delete mode 100644 resources/views/nav/nav.blade.php delete mode 100644 resources/views/recipients/index.blade.php delete mode 100644 resources/views/rules/index.blade.php delete mode 100644 resources/views/settings/show.blade.php delete mode 100644 resources/views/shared/status.blade.php delete mode 100644 resources/views/usernames/index.blade.php create mode 100644 tests/Unit/EmailUsersWithTokenExpiringSoonTest.php create mode 100644 vite.config.js diff --git a/.env.example b/.env.example index 554dc79..025821b 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -APP_NAME=AnonAddy +APP_NAME=addy.io APP_ENV=production APP_KEY= APP_DEBUG=false @@ -11,8 +11,8 @@ LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 -DB_DATABASE=anonaddy_database -DB_USERNAME=anonaddy +DB_DATABASE=addy_database +DB_USERNAME=addy DB_PASSWORD=secret BROADCAST_DRIVER=log @@ -28,9 +28,9 @@ REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 -# The from name to be used for outgoing email notifications from AnonAddy +# The from name to be used for outgoing email notifications from addy.io MAIL_FROM_NAME=Example -# The from address to be used for outgoing email notifications from AnonAddy +# The from address to be used for outgoing email notifications from addy.io MAIL_FROM_ADDRESS=mailer@example.com MAIL_DRIVER=smtp MAIL_HOST=mail.example.com @@ -48,7 +48,7 @@ ANONADDY_DOMAIN=example.com ANONADDY_HOSTNAME=mail.example.com ANONADDY_DNS_RESOLVER=127.0.0.1 ANONADDY_ALL_DOMAINS=example.com,example2.com -# Used for verifying custom domains, can be anything e.g. 64U64QcpgWHAZPyr4nN58kDGvwj9TkKMGyuXcjMFA7CdhTDy2f +# Used for verifying custom domains and variable envelope return paths, can be anything e.g. 64U64QcpgWHAZPyr4nN58kDGvwj9TkKMGyuXcjMFA7CdhTDy2f ANONADDY_SECRET=long-random-string # Number of emails that can be forwarded through the service per hour by any one user ANONADDY_LIMIT=200 diff --git a/.gitignore b/.gitignore index d9c2771..7bcc5be 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /storage/*.key /storage/debugbar /vendor +/postfix/vendor /.idea /.vscode /.vagrant diff --git a/README.md b/README.md index eb59645..9468b1a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Anonymous Email Forwarding -This is the source code for self-hosting AnonAddy. +This is the source code for self-hosting addy.io. ## FAQ -- [Why is it called AnonAddy?](#why-is-it-called-anonaddy) +- [Why is it called addy.io?](#why-is-it-called-addy-io) - [Why did you make this site?](#why-did-you-make-this-site) -- [Why should I use AnonAddy?](#why-should-i-use-anonaddy) +- [Why should I use addy.io?](#why-should-i-use-addy-io) - [Do you store emails?](#do-you-store-emails) - [What is a shared domain alias?](#what-is-a-shared-domain-alias) - [What is a standard alias?](#what-is-a-standard-alias) @@ -30,7 +30,7 @@ This is the source code for self-hosting AnonAddy. - [How do I reply to a forwarded email?](#how-do-i-reply-to-a-forwarded-email) - [I'm trying to reply/send from an alias but the email keeps coming back to me, what's wrong?](#im-trying-to-replysend-from-an-alias-but-the-email-keeps-coming-back-to-me-whats-wrong) - [I'm trying to reply/send from an alias but it is rejected, what's wrong?](#im-trying-to-replysend-from-an-alias-but-it-is-rejected-whats-wrong) -- [Does AnonAddy strip out the banner information when I reply to an email?](#does-anonaddy-strip-out-the-banner-information-when-i-reply-to-an-email) +- [Does addy.io strip out the banner information when I reply to an email?](#does-addy-io-strip-out-the-banner-information-when-i-reply-to-an-email) - [How do I send email from an alias?](#how-do-i-send-email-from-an-alias) - [Will people see my real email if I reply to a forwarded one?](#will-people-see-my-real-email-if-i-reply-to-a-forwarded-one) - [Can emails have attachments?](#can-emails-have-attachments) @@ -48,15 +48,15 @@ This is the source code for self-hosting AnonAddy. - [I'm not receiving any emails, what's wrong?](#im-not-receiving-any-emails-whats-wrong) - [I'm having trouble logging in, what's wrong?](#im-having-trouble-logging-in-whats-wrong) - [How do I know this site won't disappear next month?](#how-do-i-know-this-site-wont-disappear-next-month) -- [What happens to AnonAddy if you die?](#what-happens-to-anonaddy-if-you-die) +- [What happens to addy.io if you die?](#what-happens-to-addy-io-if-you-die) - [Is the application tested?](#is-the-appliction-tested) - [How do I host this myself?](#how-do-i-host-this-myself) -- [Who's behind AnonAddy?](#whos-behind-anonaddy) +- [Who's behind addy.io?](#whos-behind-addy-io) - [I couldn't find an answer to my question, how can I contact you?](#i-couldnt-find-an-answer-to-my-question-how-can-i-contact-you) -## Why is it called AnonAddy? +## Why is it called addy.io? -AnonAddy is short for "Anonymous Email Address". The word "Addy" is internet slang for email address, e.g. +Addy is short for "Address". The word "Addy" is internet slang for an email address, e.g. > "My addy is being spammed. I should've kept it private." @@ -75,7 +75,7 @@ I made the code open-source to show everyone what was going on behind the scenes I use this service myself for the vast majority of sites I'm signed up to. -## Why should I use AnonAddy? +## Why should I use addy.io? There are a number of reasons you should consider using this service: @@ -104,24 +104,24 @@ Yes you can use your own domain name so you can also have *@example.com as your ## Can I add a domain and also use it as a recipient? -No, you cannot use the same domain as a custom domain and also for a recipient on AnonAddy. +No, you cannot use the same domain as a custom domain and also for a recipient on addy.io. e.g if you add "example.com" as a custom domain, you cannot then add "xyz@example.com" as a recipient. This is because a domain cannot direct email to multiple locations simultaneously using MX records. So your email would arrive for "example.com" and then attempt to be forwarded to "xyz@example.com" which would create a loop. -You can instead use a subdomain for your custom domain, e.g. "mail.example.com" instead of "example.com", this would allow you to create *@mail.example.com for your aliases. More details can be found [here](https://anonaddy.com/help/adding-a-custom-domain/). +You can instead use a subdomain for your custom domain, e.g. "mail.example.com" instead of "example.com", this would allow you to create *@mail.example.com for your aliases. More details can be found [here](https://addy.io/help/adding-a-custom-domain/). ## Can I add a domain if I'm already using it for email somewhere else? -If you have a custom domain say **example.com** and you are already using it for email somewhere else e.g. ProtonMail or Namecheap then you cannot also use it simultaneously with AnonAddy. +If you have a custom domain say **example.com** and you are already using it for email somewhere else e.g. ProtonMail or Namecheap then you cannot also use it simultaneously with addy.io. This is because emails cannot be handled by multiple different mail servers at the same time, even if they have the same priority MX records. It can only be delivered to one mail server at a time which will typically be the MX record with the smallest number since this has the highest priority. You can either: -- Migrate your domain to AnonAddy by removing the current provider's MX records and adding AnonAddy's. -- Or, if you would like to keep using your domain with your current email provider then I would recommend instead adding a subdomain of it to AnonAddy such as **mail.example.com**. +- Migrate your domain to addy.io by removing the current provider's MX records and adding addy.io's. +- Or, if you would like to keep using your domain with your current email provider then I would recommend instead adding a subdomain of it to addy.io such as **mail.example.com**. -Using a subdomain will not interfere with your current email setup and you'll be able to create aliases ***@mail.example.com** through AnonAddy. +Using a subdomain will not interfere with your current email setup and you'll be able to create aliases ***@mail.example.com** through addy.io. ## Why should I use this instead of a similar service? @@ -134,14 +134,14 @@ Here are a few reasons I can think of: * Open-source application code * No limitation on the number of aliases that can be created * Generous monthly bandwidth -* Multiple domains to choose for aliases (currently anonaddy.com, anonaddy.me and another 3 for paid plan users) +* Multiple domains to choose for aliases (currently anonaddy.com, anonaddy.me and more for paid plan users) * Ability to generate random character and random word aliases at shared domains * Ability to add additional usernames to compartmentalise aliases * New features added regularly ## Is there a browser extension? -Yes there is an [open-source](https://github.com/anonaddy/browser-extension) browser extension available to download for [Firefox](https://addons.mozilla.org/en-GB/firefox/addon/anonaddy/) and [Chrome](https://chrome.google.com/webstore/detail/anonaddy/iadbdpnoknmbdeolbapdackdcogdmjpe) (also available on other chromium based browsers such as Brave and Vivaldi). You can use the extension to generate new aliases remotely. +Yes there is an [open-source](https://github.com/anonaddy/browser-extension) browser extension available to download for [Firefox](https://addons.mozilla.org/en-GB/firefox/addon/addy_io/) and [Chrome](https://chrome.google.com/webstore/detail/addyio-anonymous-email-fo/iadbdpnoknmbdeolbapdackdcogdmjpe) (also available on other chromium based browsers such as Brave and Vivaldi). You can use the extension to generate new aliases remotely. ## Is there an Android app? @@ -159,7 +159,7 @@ Yes, [http.james'](https://httpjames.space/) [open-source](https://github.com/ra ## How do I add my own GPG/OpenPGP key for encryption? -On the recipients page you simply need to click "Add public key" and paste in your **public** key data. Now all emails forwarded to you will be encrypted with your key. You can even hide and encrypt the subject as AnonAddy supports protected headers. +On the recipients page you simply need to click "Add public key" and paste in your **public** key data. Now all emails forwarded to you will be encrypted with your key. You can even hide and encrypt the subject as addy.io supports protected headers. ## Are attachments encrypted too? @@ -167,11 +167,11 @@ Yes attachments are part of the email body and are also encrypted if you have it ## Are forwarded emails signed when encryption is enabled? -Yes when you have encryption enabled all forwarded emails are signed using our mailer@anonaddy.me private key. +Yes when you have encryption enabled all forwarded emails are signed using our no-reply@addy.io private key. You can add this key to your own keyring so that you can verify emails have come from us. -The fingerprint of the mailer@anonaddy.me key is "26A987650243B28802524E2F809FD0D502E2F695" you can find the key on [https://keys.openpgp.org](https://keys.openpgp.org/search?q=26A987650243B28802524E2F809FD0D502E2F695). +The fingerprint of the no-reply@addy.io key is "26A987650243B28802524E2F809FD0D502E2F695" you can find the key on [https://keys.openpgp.org](https://keys.openpgp.org/search?q=26A987650243B28802524E2F809FD0D502E2F695). ## What if I don't want anyone to link ownership of my aliases together? @@ -220,11 +220,11 @@ All you need to do is click reply in your email client or web interface and it w To check if a reply has worked properly check in your dashboard if the reply count has been incremented for that alias. -For further details please see this help article - [Replying to email using an alias](https://anonaddy.com/help/replying-to-email-using-an-alias/). +For further details please see this help article - [Replying to email using an alias](https://addy.io/help/replying-to-email-using-an-alias/). ## I'm trying to reply/send from an alias but the email keeps coming back to me, what's wrong? -If you are trying to reply or send from an alias but the email keeps coming back to yourself then it is most likely because you are not sending the message from an email address that **is not listed as a verified recipient** on your AnonAddy account. +If you are trying to reply or send from an alias but the email keeps coming back to yourself then it is most likely because you are not sending the message from an email address that **is not listed as a verified recipient** on your addy.io account. If you try to reply or send from an alias using an unverified email address then the message will simply be forwarded to you as it would be if it was sent by any other sender. @@ -236,11 +236,11 @@ If you see the rejection message `550 5.1.1 Recipient address rejected: Address If you receive an email notification with the subject "Attempted reply/send from alias has failed" then it is usually because you have a verified recipient that is using your own domain which does not have a DMARC policy. -> Note: This is referring to **your verified recipient address** on your AnonAddy account **and not** any of your custom domains or the email address that you are replying / sending to +> Note: This is referring to **your verified recipient address** on your addy.io account **and not** any of your custom domains or the email address that you are replying / sending to -When replying or sending from an alias, **additional checks** are carried out to ensure it is not a spoofed email. Your AnonAddy recipient's email domain must pass DMARC checks in order to protect against spoofed emails and to make sure that the reply/send from attempt definitely came from your recipient. +When replying or sending from an alias, **additional checks** are carried out to ensure it is not a spoofed email. Your addy.io recipient's email domain must pass DMARC checks in order to protect against spoofed emails and to make sure that the reply/send from attempt definitely came from your recipient. -For example if the verified recipient on your AnonAddy account is `hello@example.com` and you get this email notification then it is because the domain "example.com" does not have a DMARC policy in place. +For example if the verified recipient on your addy.io account is `hello@example.com` and you get this email notification then it is because the domain "example.com" does not have a DMARC policy in place. To resolve this you simply need to add a DMARC record, for example: @@ -253,9 +253,9 @@ You should also have SPF and DKIM records in place. To learn more about DMARC please see this site - [https://dmarc.org/](https://dmarc.org/). -If your AnonAddy recipient is with a popular mail service provider for example: Gmail, Outlook, Tutanota, Mailbox.org, Protonmail etc. then they will already have a DMARC policy in place so you do not need to take any action. +If your addy.io recipient is with a popular mail service provider for example: Gmail, Outlook, Tutanota, Mailbox.org, Protonmail etc. then they will already have a DMARC policy in place so you do not need to take any action. -## Does AnonAddy strip out the banner information when I reply to an email? +## Does addy.io strip out the banner information when I reply to an email? Yes, the email banner "This email was sent to..." will be automatically removed when you reply to any messages. You can test this by replying to yourself from one of your aliases. @@ -287,7 +287,7 @@ If you need to send an email to an address with an extension e.g. **hello+whatev Just enter the extension too! -For further details please see this help article - [Sending email from an alias](https://anonaddy.com/help/sending-email-from-an-alias/). +For further details please see this help article - [Sending email from an alias](https://addy.io/help/sending-email-from-an-alias/). ## Will people see my real email if I reply to a forwarded one? @@ -309,10 +309,10 @@ A few days before your billing cycle ends you will receive an email letting you * Any custom domains will be **deactivated** * Any additional usernames will be **deactivated** -* If you have any more than **2 recipients** they will be **deleted** +* If you have any more than **1 recipient** they will be **deleted** * Paid account settings will be reverted to default values * Any aliases using paid plan only domains will be **deactivated** -* If you have any more than 20 aliases using a shared domain e.g. anonaddy.me they will be **deactivated** +* If you have any more than 10 aliases using a shared domain e.g. anonaddy.me they will be **deactivated** * If your account username has catch-all disabled then it will be enabled You will not be able to activate any of the above again until you resubscribe. @@ -366,7 +366,7 @@ Yes, you can login with any of your usernames. You can add 1 additional username ## I'm not receiving any emails, what's wrong? -Please make sure to add mailer@anonaddy.me, mailer@anonaddy.com and any other aliases you use to your address book and also to check your spam folder. Make sure to mark emails from AnonAddy as safe if they turn up in spam. +Please make sure to add no-reply@addy.io and any aliases you use to your address book and also to check your spam folder. Make sure to mark emails from addy.io as safe if they turn up in spam. If an alias has been deleted and you try to send email to it, the emails will be rejected with an error message - "550 5.1.1 Recipient address rejected: Address does not exist". @@ -382,7 +382,7 @@ For some reason Apple seems to think these emails are spam/phishing and returns If you are having issues with emails being rejected as "possibly spammy" by Google, iCloud or Microsoft then please try the following steps if you can: -1. **Replace the email subject** by going to your settings in AnonAddy +1. **Replace the email subject** by going to your settings in addy.io 2. Try adding a GPG key and **enabling encryption**. This will prevent the email's content being scanned and reduce the chance of it being rejected. 3. Enable the option to hide and encrypt the email subject 4. Try disabling the banner information on forwarded emails @@ -406,11 +406,11 @@ Please make sure you are using your account username (e.g. johndoe) and not your 2. Forgotten password -If you've forgotten your password you can reset it by entering your username here - https://app.anonaddy.com/password/reset +If you've forgotten your password you can reset it by entering your username here - https://app.addy.io/password/reset 3. Forgotten username -If you've forgotten your username you can request a reminder by entering your email address here - https://app.anonaddy.com/username/reminder +If you've forgotten your username you can request a reminder by entering your email address here - https://app.addy.io/username/reminder 4. Lost 2FA device @@ -424,11 +424,11 @@ If you have a YubiKey and are using Windows and have an issue with your personal I am very passionate about this project. I use it myself every day and will be keeping it running indefinitely. The service also provides me with an income. -## What happens to AnonAddy if you die? +## What happens to addy.io if you die? -I do have someone in place who can keep the service running in the event of me not being here. They are able to continue paying for the servers that host AnonAddy and the domains that it uses. All AnonAddy domains also always have over 5 years until they expire. +I do have someone in place who can keep the service running in the event of me not being here. They are able to continue paying for the servers that host addy.io and the domains that it uses. All addy.io domains also always have over 5 years until they expire. -They would make a Twitter announcement informing all users that they would be keeping the service running. You would then be able to decide whether you'd like to continue using AnonAddy or start to update your email addresses. +They would make a Twitter announcement informing all users that they would be keeping the service running. You would then be able to decide whether you'd like to continue using addy.io or start to update your email addresses. ## Is the application tested? @@ -440,13 +440,13 @@ You will need to set up your own server with Postfix so that you can pipe the re For those who prefer using Docker there is an image you can use here - [github.com/anonaddy/docker](https://github.com/anonaddy/docker). -## Who's behind AnonAddy? +## Who's behind addy.io? My name is Will Browning, I'm a web developer from the UK and an advocate for online privacy and open-source software. You can find me on [Twitter](https://twitter.com/willbrowningme) although I don't tweet that much! ## I couldn't find an answer to my question, how can I contact you? -For any other questions just send an email to - [contact@anonaddy.com](mailto:contact@anonaddy.com) ([GPG Key](https://anonaddy.com/anonaddy-contact-public-key.asc)) +For any other questions just send an email to - contact (at) help.addy.io ([GPG Key](https://addy.io/contact-public-key.asc)) ## Self Hosting @@ -470,7 +470,7 @@ For full details please see the [self-hosting instructions file](SELF-HOSTING.md Thanks to [Vlad Timofeev](https://github.com/vlad-timofeev), [Patrick Dobler](https://github.com/patrickdobler), [Luca Steeb](https://github.com/steebchen), [Laiteux](https://github.com/Laiteux), [narolinus](https://github.com/narolinus),[Limon Monte](https://github.com/limonte) and [Lukas](https://github.com/lunibo) for supporting me by sponsoring the project on GitHub! -Also an extra special thanks to [CrazyMax](https://github.com/crazy-max) for sponsoring me and also creating and maintaining the awesome [AnonAddy Docker image](https://github.com/anonaddy/docker)! +Also an extra special thanks to [CrazyMax](https://github.com/crazy-max) for sponsoring me and also creating and maintaining the awesome [addy.io Docker image](https://github.com/anonaddy/docker)! ## Thanks diff --git a/SECURITY.md b/SECURITY.md index a3cf97e..9912e00 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,11 +11,11 @@ notify me. I welcome working with you to resolve the issue promptly. Thanks in a degradation of the service. Only interact with accounts you own or with explicit permission of the account holder. - If you would like to encrypt your report, please use the PGP key with fingerprint - `5FCAFD8A67D2A783CFF4D0E31AC6D923E6FB4EF7` (available on the openpgp.org keyserver). + `E652C2DB43859328F35575DEBF7B93C6497510D0` (available on the openpgp.org keyserver). # Reporting a Vulnerability -To report a vulnerability please send an email to contact@anonaddy.com, you can use the PGP key above if you wish to encrypt it. +To report a vulnerability please send an email to contact (at) help.addy.io, you can use the PGP key above if you wish to encrypt it. # In-scope diff --git a/app/Console/Commands/CheckDomainsMxValidation.php b/app/Console/Commands/CheckDomainsMxValidation.php index 908b679..214de84 100644 --- a/app/Console/Commands/CheckDomainsMxValidation.php +++ b/app/Console/Commands/CheckDomainsMxValidation.php @@ -39,7 +39,8 @@ class CheckDomainsMxValidation extends Command */ public function handle() { - Domain::all() + Domain::with('user.defaultUsername') + ->get() ->each(function ($domain) { try { if (! $domain->checkMxRecords()) { diff --git a/app/Console/Commands/CheckDomainsSendingVerification.php b/app/Console/Commands/CheckDomainsSendingVerification.php index 1a9d125..eedc961 100644 --- a/app/Console/Commands/CheckDomainsSendingVerification.php +++ b/app/Console/Commands/CheckDomainsSendingVerification.php @@ -39,7 +39,8 @@ class CheckDomainsSendingVerification extends Command */ public function handle() { - Domain::whereNotNull('domain_sending_verified_at')->get() + Domain::with('user.defaultUsername') + ->whereNotNull('domain_sending_verified_at')->get() ->each(function ($domain) { try { $result = $domain->checkVerificationForSending(); diff --git a/app/Console/Commands/ClearOutboundMessages.php b/app/Console/Commands/ClearOutboundMessages.php new file mode 100644 index 0000000..603c01f --- /dev/null +++ b/app/Console/Commands/ClearOutboundMessages.php @@ -0,0 +1,35 @@ +subDays(7))->delete(); + + return Command::SUCCESS; + } +} diff --git a/app/Console/Commands/ClearPostfixQueueIds.php b/app/Console/Commands/ClearPostfixQueueIds.php deleted file mode 100644 index 27e8bfa..0000000 --- a/app/Console/Commands/ClearPostfixQueueIds.php +++ /dev/null @@ -1,43 +0,0 @@ -subDays(7))->delete(); - } -} diff --git a/app/Console/Commands/EmailUsersWithTokenExpiringSoon.php b/app/Console/Commands/EmailUsersWithTokenExpiringSoon.php new file mode 100644 index 0000000..8f58353 --- /dev/null +++ b/app/Console/Commands/EmailUsersWithTokenExpiringSoon.php @@ -0,0 +1,63 @@ +whereHas('tokens', function ($query) { + $query->whereDate('expires_at', now()->addWeek()); + }) + ->get() + ->each(function (User $user) { + $this->sendTokenExpiringSoonMail($user); + }); + } + + protected function sendTokenExpiringSoonMail(User $user) + { + try { + Mail::to($user->email)->send(new TokenExpiringSoon($user)); + } catch (Exception $exception) { + $this->error("exception when sending mail to user: {$user->username}", $exception); + report($exception); + } + } +} diff --git a/app/Console/Commands/ReceiveEmail.php b/app/Console/Commands/ReceiveEmail.php index f6e3bff..251022f 100644 --- a/app/Console/Commands/ReceiveEmail.php +++ b/app/Console/Commands/ReceiveEmail.php @@ -8,8 +8,7 @@ use App\Mail\SendFromEmail; use App\Models\Alias; use App\Models\Domain; use App\Models\EmailData; -use App\Models\PostfixQueueId; -use App\Models\Recipient; +use App\Models\OutboundMessage; use App\Models\Username; use App\Notifications\DisallowedReplySendAttempt; use App\Notifications\FailedDeliveryNotification; @@ -21,6 +20,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; +use ParagonIE\ConstantTime\Base32; use PhpMimeMailParser\Parser; use Ramsey\Uuid\Uuid; @@ -53,6 +53,8 @@ class ReceiveEmail extends Command protected $size; + protected $rawEmail; + /** * Create a new command instance. * @@ -86,9 +88,44 @@ class ReceiveEmail extends Command $this->size = $this->option('size') / ($recipientCount ? $recipientCount : 1); foreach ($recipients as $recipient) { - // Handle bounces - if ($this->option('sender') === 'MAILER-DAEMON') { - $this->handleBounce($recipient['email']); + // Check if VERP bounce + if (substr($recipient['email'], 0, 2) === 'b_') { + if ($outboundMessageId = $this->getIdFromVerp($recipient['email'])) { + // Is a valid bounce + $outboundMessage = OutboundMessage::with(['user', 'alias', 'recipient'])->find($outboundMessageId); + + if (is_null($outboundMessage)) { + // Must have been more than 7 days + Log::info('VERP outboundMessage not found'); + + exit(0); + } + + $bouncedAlias = $outboundMessage->alias; + + // If already bounced then forward to the user instead + if (! $outboundMessage->bounced) { + $this->handleBounce($outboundMessage); + } + + if (in_array(strtolower($this->parser->getHeader('Auto-Submitted')), ['auto-replied', 'auto-generated']) && ! in_array($outboundMessage->email_type, ['R', 'S'])) { + Log::info('VERP auto-response to forward/notification, username: '.$outboundMessage->user?->username.' outboundMessageID: '.$outboundMessageId); + + exit(0); + } + + // If it is a notification then there is no alias so exit and log, may be an auto-reply to a notification. + if (is_null($bouncedAlias)) { + Log::info('VERP previously bounced/auto-response to notification, username: '.$outboundMessage->user?->username.' outboundMessageID: '.$outboundMessageId); + + exit(0); + } + + // If it is not a bounce (could be auto-reply) then redirect to alias + $recipient['email'] = $bouncedAlias->email; + $recipient['local_part'] = $bouncedAlias->local_part; + $recipient['domain'] = $bouncedAlias->domain; + } } // First determine if the alias already exists in the database @@ -155,7 +192,7 @@ class ReceiveEmail extends Command if ($verifiedRecipient?->can_reply_send) { // Check if the Dmarc allow or spam headers are present from Rspamd - if (! $this->parser->getHeader('X-AnonAddy-Dmarc-Allow') || $this->parser->getHeader('X-AnonAddy-Spam')) { + if (! $this->parser->getHeader('X-AnonAddy-Dmarc-Allow')) { // Notify user and exit $verifiedRecipient->notify(new SpamReplySendAttempt($recipient, $this->senderFrom, $this->parser->getHeader('X-AnonAddy-Authentication-Results'))); @@ -173,7 +210,8 @@ class ReceiveEmail extends Command exit(0); } else { - $this->handleForward($user, $recipient, $alias ?? null, $aliasable ?? null); + // Check if the spam header is present from Rspamd + $this->handleForward($user, $recipient, $alias ?? null, $aliasable ?? null, $this->parser->getHeader('X-AnonAddy-Spam') === 'Yes'); } } } catch (\Exception $e) { @@ -229,7 +267,7 @@ class ReceiveEmail extends Command Mail::to($sendTo)->queue($message); } - protected function handleForward($user, $recipient, $alias, $aliasable) + protected function handleForward($user, $recipient, $alias, $aliasable, $isSpam) { if (is_null($alias)) { // This is a new alias @@ -277,14 +315,14 @@ class ReceiveEmail extends Command $emailData = new EmailData($this->parser, $this->option('sender'), $this->size); - $alias->verifiedRecipientsOrDefault()->each(function ($recipient) use ($alias, $emailData) { - $message = new ForwardEmail($alias, $emailData, $recipient); + $alias->verifiedRecipientsOrDefault()->each(function ($recipient) use ($alias, $emailData, $isSpam) { + $message = (new ForwardEmail($alias, $emailData, $recipient, $isSpam)); Mail::to($recipient->email)->queue($message); }); } - protected function handleBounce($returnPath) + protected function handleBounce($outboundMessage) { // Collect the attachments $attachments = collect($this->parser->getAttachments()); @@ -294,136 +332,103 @@ class ReceiveEmail extends Command return $attachment->getContentType() === 'message/delivery-status'; })->first(); - if ($deliveryReport) { - $dsn = $this->parseDeliveryStatus($deliveryReport->getMimePartStr()); + // Is not a bounce, may be an auto-reply so return + if (! $deliveryReport) { + return; + } - // Verify queue ID - if (isset($dsn['X-postfix-queue-id'])) { - // First check in DB - $postfixQueueId = PostfixQueueId::firstWhere('queue_id', strtoupper($dsn['X-postfix-queue-id'])); + // Mark the outboundMessage as bounced + $outboundMessage->markAsBounced(); - if (! $postfixQueueId) { - exit(0); - } + $dsn = $this->parseDeliveryStatus($deliveryReport->getMimePartStr()); - // If found then delete from DB - $postfixQueueId->delete(); - } else { - exit(0); - } + // Get the bounced email address + $bouncedEmailAddress = isset($dsn['Final-recipient']) ? trim(Str::after($dsn['Final-recipient'], ';')) : null; - // Get the bounced email address - $bouncedEmailAddress = isset($dsn['Final-recipient']) ? trim(Str::after($dsn['Final-recipient'], ';')) : ''; + $remoteMta = isset($dsn['Remote-mta']) ? trim(Str::after($dsn['Remote-mta'], ';')) : ''; - $remoteMta = isset($dsn['Remote-mta']) ? trim(Str::after($dsn['Remote-mta'], ';')) : ''; + if (isset($dsn['Diagnostic-code']) && isset($dsn['Status'])) { + // Try to determine the bounce type, HARD, SPAM, SOFT + $bounceType = $this->getBounceType($dsn['Diagnostic-code'], $dsn['Status']); - if (isset($dsn['Diagnostic-code']) && isset($dsn['Status'])) { - // Try to determine the bounce type, HARD, SPAM, SOFT - $bounceType = $this->getBounceType($dsn['Diagnostic-code'], $dsn['Status']); + $diagnosticCode = Str::limit($dsn['Diagnostic-code'], 497); + } else { + $bounceType = null; + $diagnosticCode = null; + } - $diagnosticCode = Str::limit($dsn['Diagnostic-code'], 497); - } else { - $bounceType = null; - $diagnosticCode = null; - } + // Get the undelivered message + $undeliveredMessage = $attachments->filter(function ($attachment) { + return in_array($attachment->getContentType(), ['text/rfc822-headers', 'message/rfc822']); + })->first(); - // The return path is the alias except when it is from an unverified custom domain - if ($returnPath !== config('anonaddy.return_path')) { - $alias = Alias::withTrashed()->firstWhere('email', $returnPath); + $undeliveredMessageHeaders = []; - if (isset($alias)) { - $user = $alias->user; - } - } + if ($undeliveredMessage) { + $undeliveredMessageHeaders = $this->parseDeliveryStatus($undeliveredMessage->getMimePartStr()); + } - // Try to find a user from the bounced email address - if ($recipient = Recipient::select(['id', 'user_id', 'email', 'email_verified_at'])->get()->firstWhere('email', $bouncedEmailAddress)) { - if (! isset($user)) { - $user = $recipient->user; - } - } + // Get bounce user information + $user = $outboundMessage->user; + $alias = $outboundMessage->alias; + $recipient = $outboundMessage->recipient; + $emailType = $outboundMessage->getRawOriginal('email_type'); - // Get the undelivered message - $undeliveredMessage = $attachments->filter(function ($attachment) { - return in_array($attachment->getContentType(), ['text/rfc822-headers', 'message/rfc822']); - })->first(); - - $undeliveredMessageHeaders = []; - $emailType = null; + if ($user) { + $failedDeliveryId = Uuid::uuid4(); if ($undeliveredMessage) { - $undeliveredMessageHeaders = $this->parseDeliveryStatus($undeliveredMessage->getMimePartStr()); - - if (isset($undeliveredMessageHeaders['Feedback-id'])) { - [$emailType, $aliasId] = explode(':', $undeliveredMessageHeaders['Feedback-id']); - - if (in_array($emailType, ['F', 'R', 'S']) && ! isset($alias)) { - $alias = Alias::find($aliasId); - - // Find the user from the alias if we don't have it from the recipient - if (! isset($user) && isset($alias)) { - $user = $alias->user; - } - } + // Store the undelivered message if enabled by user. Do not store email verification notifications. + if ($user->store_failed_deliveries && ! in_array($emailType, ['VR', 'VU'])) { + $isStored = Storage::disk('local')->put("{$failedDeliveryId}.eml", $this->trimUndeliveredMessage($undeliveredMessage->getMimePartStr())); } } - if (isset($user)) { - $failedDeliveryId = Uuid::uuid4(); + $failedDelivery = $user->failedDeliveries()->create([ + 'id' => $failedDeliveryId, + 'recipient_id' => $recipient->id ?? null, + 'alias_id' => $alias->id ?? null, + 'is_stored' => $isStored ?? false, + 'bounce_type' => $bounceType, + 'remote_mta' => $remoteMta ?? null, + 'sender' => $undeliveredMessageHeaders['X-anonaddy-original-sender'] ?? null, + 'destination' => $bouncedEmailAddress, + 'email_type' => $emailType, + 'status' => $dsn['Status'] ?? null, + 'code' => $diagnosticCode, + 'attempted_at' => $outboundMessage->created_at, + ]); - if ($undeliveredMessage) { - - // Store the undelivered message if enabled by user. - if ($user->store_failed_deliveries) { - $isStored = Storage::disk('local')->put("{$failedDeliveryId}.eml", $this->trimUndeliveredMessage($undeliveredMessage->getMimePartStr())); - } + // Check the aliases failed deliveries + if ($alias) { + // Decrement the alias forward count due to failed delivery + if ($failedDelivery->getRawOriginal('email_type') === 'F' && $alias->emails_forwarded > 0) { + $alias->decrement('emails_forwarded'); } - $failedDelivery = $user->failedDeliveries()->create([ - 'id' => $failedDeliveryId, - 'recipient_id' => $recipient->id ?? null, - 'alias_id' => $alias->id ?? null, - 'is_stored' => $isStored ?? false, - 'bounce_type' => $bounceType, - 'remote_mta' => $remoteMta ?? null, - 'sender' => $undeliveredMessageHeaders['X-anonaddy-original-sender'] ?? null, - 'email_type' => $emailType ?? null, - 'status' => $dsn['Status'] ?? null, - 'code' => $diagnosticCode, - 'attempted_at' => $postfixQueueId->created_at, - ]); - - if (isset($alias)) { - // Decrement the alias forward count due to failed delivery - if ($failedDelivery->email_type === 'F' && $alias->emails_forwarded > 0) { - $alias->decrement('emails_forwarded'); - } - - if ($failedDelivery->email_type === 'R' && $alias->emails_replied > 0) { - $alias->decrement('emails_replied'); - } - - if ($failedDelivery->email_type === 'S' && $alias->emails_sent > 0) { - $alias->decrement('emails_sent'); - } + if ($failedDelivery->getRawOriginal('email_type') === 'R' && $alias->emails_replied > 0) { + $alias->decrement('emails_replied'); + } + + if ($failedDelivery->getRawOriginal('email_type') === 'S' && $alias->emails_sent > 0) { + $alias->decrement('emails_sent'); } - } else { - Log::info([ - 'info' => 'user not found from bounce report', - 'deliveryReport' => $deliveryReport, - ]); } + } else { + Log::info('User not found from outbound message, may have been deleted.'); + } - // Check if the bounce is a Failed delivery notification and if so do not notify the user again - if (! in_array($emailType, ['FDN'])) { + // Check if the bounce is a Failed delivery notification and if so do not notify the user again + if (! in_array($emailType, ['FDN'])) { - $notifiable = $recipient?->email_verified_at ? $recipient : $user?->defaultRecipient; + $notifiable = $recipient?->email_verified_at ? $recipient : $user?->defaultRecipient; - // Notify user of failed delivery - if ($notifiable?->email_verified_at) { + // Notify user of failed delivery + if ($notifiable?->email_verified_at) { - $notifiable->notify(new FailedDeliveryNotification($alias->email ?? null, $undeliveredMessageHeaders['X-anonaddy-original-sender'] ?? null, $undeliveredMessageHeaders['Subject'] ?? null, $failedDelivery?->is_stored, $user?->store_failed_deliveries, $recipient?->email)); - } + $notifiable->notify(new FailedDeliveryNotification($alias->email ?? null, $undeliveredMessageHeaders['X-anonaddy-original-sender'] ?? null, $undeliveredMessageHeaders['Subject'] ?? null, $failedDelivery?->is_stored, $user?->store_failed_deliveries, $recipient?->email)); + + Log::info('FDN '.$emailType.': '.$notifiable->email); } } @@ -433,6 +438,8 @@ class ReceiveEmail extends Command protected function checkBandwidthLimit($user) { if ($user->hasReachedBandwidthLimit()) { + $user->update(['reject_until' => now()->endOfMonth()]); + $this->error('4.2.1 Bandwidth limit exceeded for user. Please try again later.'); exit(1); @@ -453,7 +460,9 @@ class ReceiveEmail extends Command ->then( function () { }, - function () { + function () use ($user) { + $user->update(['defer_until' => now()->addHour()]); + $this->error('4.2.1 Rate limit exceeded for user. Please try again later.'); exit(1); @@ -532,6 +541,11 @@ class ReceiveEmail extends Command return $result; } + protected function trimUndeliveredMessage($message) + { + return Str::after($message, 'Content-Type: message/rfc822'.PHP_EOL.PHP_EOL); + } + protected function getBounceType($code, $status) { if (preg_match("/(:?mailbox|address|user|account|recipient|@).*(:?rejected|unknown|disabled|unavailable|invalid|inactive|not exist|does(n't| not) exist)|(:?rejected|unknown|unavailable|no|illegal|invalid|no such).*(:?mailbox|address|user|account|recipient|alias)|(:?address|user|recipient) does(n't| not) have .*(:?mailbox|account)|returned to sender|(:?auth).*(:?required)/i", $code)) { @@ -553,12 +567,48 @@ class ReceiveEmail extends Command protected function getSenderFrom() { try { - return $this->parser->getAddresses('from')[0]['address']; + // Ensure contains '@', may be malformed header which causes sends/replies to fail + $address = $this->parser->getAddresses('from')[0]['address']; + + return Str::contains($address, '@') ? $address : $this->option('sender'); } catch (\Exception $e) { return $this->option('sender'); } } + protected function getIdFromVerp($verp) + { + $localPart = Str::beforeLast($verp, '@'); + + $parts = explode('_', $localPart); + + if (count($parts) !== 3) { + Log::channel('single')->info('VERP invalid email: '.$verp); + + return; + } + + try { + $id = Base32::decodeNoPadding($parts[1]); + + $signature = Base32::decodeNoPadding($parts[2]); + } catch (\Exception $e) { + Log::channel('single')->info('VERP base32 decode failure: '.$verp.' '.$e->getMessage()); + + return; + } + + $expectedSignature = substr(hash_hmac('sha3-224', $id, config('anonaddy.secret')), 0, 8); + + if ($signature !== $expectedSignature) { + Log::channel('single')->info('VERP invalid signature: '.$verp); + + return; + } + + return $id; + } + protected function exitIfFromSelf() { // To prevent recipient alias infinite nested looping. diff --git a/app/Console/Commands/ResetBandwidth.php b/app/Console/Commands/ResetBandwidth.php index ee85b11..33977f1 100644 --- a/app/Console/Commands/ResetBandwidth.php +++ b/app/Console/Commands/ResetBandwidth.php @@ -38,6 +38,6 @@ class ResetBandwidth extends Command */ public function handle() { - User::where('bandwidth', '>', 0)->update(['bandwidth' => 0]); + User::where('bandwidth', '>', 0)->update(['bandwidth' => 0, 'reject_until' => null, 'defer_until' => null]); } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 419a3d2..c516550 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -27,8 +27,10 @@ class Kernel extends ConsoleKernel $schedule->command('anonaddy:check-domains-sending-verification')->daily(); $schedule->command('anonaddy:check-domains-mx-validation')->daily(); $schedule->command('anonaddy:clear-failed-deliveries')->daily(); - $schedule->command('anonaddy:clear-postfix-queue-ids')->hourly(); + $schedule->command('anonaddy:clear-outbound-messages')->everySixHours(); + $schedule->command('anonaddy:email-users-with-token-expiring-soon')->daily(); $schedule->command('auth:clear-resets')->daily(); + $schedule->command('sanctum:prune-expired --hours=168')->daily(); $schedule->command('cache:prune-stale-tags')->hourly(); } diff --git a/app/CustomMailDriver/CustomMailer.php b/app/CustomMailDriver/CustomMailer.php index 3ceb0f2..2a40706 100644 --- a/app/CustomMailDriver/CustomMailer.php +++ b/app/CustomMailDriver/CustomMailer.php @@ -4,14 +4,14 @@ namespace App\CustomMailDriver; use App\CustomMailDriver\Mime\Crypto\AlreadyEncrypted; use App\CustomMailDriver\Mime\Crypto\OpenPGPEncrypter; -use App\Models\PostfixQueueId; +use App\Models\OutboundMessage; use App\Models\Recipient; use App\Notifications\GpgKeyExpired; +use Exception; use Illuminate\Contracts\Mail\Mailable as MailableContract; -use Illuminate\Database\QueryException; use Illuminate\Mail\Mailer; use Illuminate\Mail\SentMessage; -use Illuminate\Support\Str; +use ParagonIE\ConstantTime\Base32; use Symfony\Component\Mailer\Envelope; use Symfony\Component\Mailer\Exception\RuntimeException; use Symfony\Component\Mime\Crypto\DkimOptions; @@ -121,6 +121,17 @@ class CustomMailer extends Mailer } if ($this->shouldSendMessage($symfonyMessage, $data)) { + // Set VERP address + $id = randomString(12); + $verpLocalPart = $this->getVerpLocalPart($id); + + // If the message is a forward, reply or send then use the verp domain + if (isset($data['emailType']) && in_array($data['emailType'], ['F', 'R', 'S'])) { + $message->returnPath($verpLocalPart.'@'.$data['verpDomain']); + } else { + $message->returnPath($verpLocalPart.'@'.config('anonaddy.domain')); + } + $symfonySentMessage = $this->sendSymfonyMessage($symfonyMessage); if ($symfonySentMessage) { @@ -128,16 +139,20 @@ class CustomMailer extends Mailer $this->dispatchSentEvent($sentMessage, $data); - try { - // Get Postfix Queue ID and save in DB - $id = str_replace("\r\n", '', Str::after($sentMessage->getDebug(), 'Ok: queued as ')); + // Create a new Outbound Message for verifying any bounces + if (isset($data['userId']) && ! is_null($data['userId']) && isset($data['emailType']) && ! is_null($data['emailType'])) { - PostfixQueueId::create([ - 'queue_id' => $id, - ]); - } catch (QueryException $e) { - // duplicate entry - //Log::info('Failed to save Postfix Queue ID: ' . $id); + try { + OutboundMessage::create([ + 'id' => $id, + 'user_id' => $data['userId'], + 'alias_id' => $data['aliasId'] ?? null, + 'recipient_id' => $data['recipientId'] ?? null, + 'email_type' => $data['emailType'], + ]); + } catch (Exception $e) { + report($e); + } } return $sentMessage; @@ -171,4 +186,14 @@ class CustomMailer extends Mailer // } } + + protected function getVerpLocalPart($id) + { + $hmac = hash_hmac('sha3-224', $id, config('anonaddy.secret')); + $hmacPayload = substr($hmac, 0, 8); + $encodedPayload = Base32::encodeUnpadded($id); + $encodedSignature = Base32::encodeUnpadded($hmacPayload); + + return "b_{$encodedPayload}_{$encodedSignature}"; + } } diff --git a/app/CustomMailDriver/Mime/Part/EncryptedPart.php b/app/CustomMailDriver/Mime/Part/EncryptedPart.php index db3d772..8e322e2 100644 --- a/app/CustomMailDriver/Mime/Part/EncryptedPart.php +++ b/app/CustomMailDriver/Mime/Part/EncryptedPart.php @@ -41,7 +41,7 @@ class EncryptedPart extends AbstractPart $this->body = $body; $this->charset = $charset; $this->subtype = $subtype; - $this->seekable = \is_resource($body) ? stream_get_meta_data($body)['seekable'] && 0 === fseek($body, 0, \SEEK_CUR) : null; + $this->seekable = \is_resource($body) ? stream_get_meta_data($body)['seekable'] && fseek($body, 0, \SEEK_CUR) === 0 : null; } public function getMediaType(): string @@ -56,7 +56,7 @@ class EncryptedPart extends AbstractPart public function getBody(): string { - if (null === $this->seekable) { + if ($this->seekable === null) { return $this->body; } @@ -74,7 +74,7 @@ class EncryptedPart extends AbstractPart public function bodyToIterable(): iterable { - if (null !== $this->seekable) { + if ($this->seekable !== null) { if ($this->seekable) { rewind($this->body); } @@ -92,10 +92,10 @@ class EncryptedPart extends AbstractPart public function asDebugString(): string { $str = parent::asDebugString(); - if (null !== $this->charset) { + if ($this->charset !== null) { $str .= ' charset: '.$this->charset; } - if (null !== $this->disposition) { + if ($this->disposition !== null) { $str .= ' disposition: '.$this->disposition; } @@ -110,7 +110,7 @@ class EncryptedPart extends AbstractPart public function __sleep(): array { // convert resources to strings for serialization - if (null !== $this->seekable) { + if ($this->seekable !== null) { $this->body = $this->getBody(); } diff --git a/app/CustomMailDriver/Mime/Part/InlineImagePart.php b/app/CustomMailDriver/Mime/Part/InlineImagePart.php index 9aacbd6..b32db85 100644 --- a/app/CustomMailDriver/Mime/Part/InlineImagePart.php +++ b/app/CustomMailDriver/Mime/Part/InlineImagePart.php @@ -19,6 +19,21 @@ class InlineImagePart extends DataPart return $this; } + public function getContentId(): string + { + return $this->cid ?: $this->cid = $this->generateContentId(); + } + + public function hasContentId(): bool + { + return $this->cid !== null; + } + + private function generateContentId(): string + { + return bin2hex(random_bytes(16)).'@symfony'; + } + /** * Sets the name of the file. * @@ -35,11 +50,11 @@ class InlineImagePart extends DataPart { $headers = parent::getPreparedHeaders(); - if (null !== $this->cid) { + if ($this->cid !== null) { $headers->setHeaderBody('Id', 'Content-ID', $this->cid); } - if (null !== $this->filename) { + if ($this->filename !== null) { $headers->setHeaderParameter('Content-Disposition', 'filename', $this->filename); } diff --git a/app/Enums/DisplayFromFormat.php b/app/Enums/DisplayFromFormat.php new file mode 100644 index 0000000..f2aa832 --- /dev/null +++ b/app/Enums/DisplayFromFormat.php @@ -0,0 +1,14 @@ +allAliases()->count()) { + return back()->withErrors(['aliases_export' => 'You don\'t have any aliases to export.']); + } + return Excel::download(new AliasesExport(), 'aliases-'.now()->toDateString().'.csv'); } } diff --git a/app/Http/Controllers/AliasImportController.php b/app/Http/Controllers/AliasImportController.php index fc75a92..55935b0 100644 --- a/app/Http/Controllers/AliasImportController.php +++ b/app/Http/Controllers/AliasImportController.php @@ -36,6 +36,6 @@ class AliasImportController extends Controller report($e); } - return back()->with(['status' => 'File uploaded successfully, your aliases are being imported']); + return back()->with(['flash' => 'File uploaded successfully, your aliases are being imported']); } } diff --git a/app/Http/Controllers/Api/AliasBulkController.php b/app/Http/Controllers/Api/AliasBulkController.php new file mode 100644 index 0000000..3994711 --- /dev/null +++ b/app/Http/Controllers/Api/AliasBulkController.php @@ -0,0 +1,252 @@ +middleware('throttle:12,1'); + } + + public function get(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliases = user()->aliases()->withTrashed() + ->whereIn('id', $request->ids) + ->get(); + + // If there are no aliases found return 404 response + if (! $aliases->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + return AliasResource::collection($aliases); + } + + public function activate(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliasesWithTrashed = user()->aliases()->withTrashed() + ->select(['id', 'user_id', 'active', 'deleted_at']) + ->where('active', false) + ->whereIn('id', $request->ids) + ->get(); + + // If there are no aliases found return 404 response + if (! $aliasesWithTrashedCount = $aliasesWithTrashed->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + // Check if all aliases are deleted, if so return message + $aliases = $aliasesWithTrashed->filter(function ($alias) { + return ! $alias->trashed(); + }); + + if ($aliases->count() === 0) { + return response()->json([ + 'message' => $aliasesWithTrashedCount === 1 ? 'You need to restore this alias before you can activate it' : 'You need to restore these aliases before you can activate them', + 'ids' => $aliasesWithTrashed->pluck('id'), + ], 422); + } + + $aliasIds = $aliases->pluck('id')->all(); + $aliasIdsCount = count($aliasIds); + user()->aliases()->whereIn('id', $aliasIds)->update(['active' => true]); + + return response()->json([ + 'message' => $aliasIdsCount === 1 ? '1 alias activated successfully' : "{$aliasIdsCount} aliases activated successfully", + 'ids' => $aliasIds, + ], 200); + } + + public function deactivate(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliasIds = user()->aliases() + ->where('active', true) + ->whereIn('id', $request->ids) + ->pluck('id'); + + // If there are no aliases found return 404 response + if (! $aliasIdsCount = $aliasIds->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + user()->aliases()->whereIn('id', $aliasIds)->update(['active' => false]); + + return response()->json([ + 'message' => $aliasIdsCount === 1 ? '1 alias deactivated successfully' : "{$aliasIdsCount} aliases deactivated successfully", + 'ids' => $aliasIds, + ], 200); + } + + public function delete(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliasIds = user()->aliases() + ->whereIn('id', $request->ids) + ->pluck('id'); + + // If there are no aliases found return 404 response + if (! $aliasIdsCount = $aliasIds->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + // Detach any recipients + DB::table('alias_recipients')->whereIn('alias_id', $aliasIds)->delete(); + + // Use update since delete() does not trigger model event + user()->aliases()->whereIn('id', $aliasIds)->update(['active' => false, 'deleted_at' => now()]); + + // Don't return 204 as that is only for empty responses + return response()->json([ + 'message' => $aliasIdsCount === 1 ? '1 alias deleted successfully' : "{$aliasIdsCount} aliases deleted successfully", + 'ids' => $aliasIds, + ], 200); + } + + public function forget(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliasIds = user()->aliases()->withTrashed() + ->whereIn('id', $request->ids) + ->pluck('id'); + + // If there are no aliases found return 404 response + if (! $aliasIdsCount = $aliasIds->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + // Detach any recipients + DB::table('alias_recipients')->whereIn('alias_id', $aliasIds)->delete(); + + // Shared Domain aliases, remove all data and change user_id + $forgottenSharedDomainCount = user()->aliases()->withTrashed() + ->whereIn('id', $aliasIds) + ->whereIn('domain', config('anonaddy.all_domains')) + ->update([ + 'user_id' => '00000000-0000-0000-0000-000000000000', + 'extension' => null, + 'description' => null, + 'emails_forwarded' => 0, + 'emails_blocked' => 0, + 'emails_replied' => 0, + 'emails_sent' => 0, + 'active' => false, + 'deleted_at' => now(), + ]); + + if ($forgottenSharedDomainCount < $aliasIdsCount) { + // Standard aliases + user()->aliases()->withTrashed() + ->whereIn('id', $aliasIds) + ->whereNotIn('domain', config('anonaddy.all_domains')) + ->forceDelete(); + } + + // Don't return 204 as that is only for empty responses + return response()->json([ + 'message' => $aliasIdsCount === 1 ? '1 alias forgotten successfully' : "{$aliasIdsCount} aliases forgotten successfully", + 'ids' => $aliasIds, + ], 200); + } + + public function restore(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $aliasIds = user()->aliases()->onlyTrashed() + ->whereIn('id', $request->ids) + ->pluck('id'); + + // If there are no aliases found return 404 response + if (! $aliasIdsCount = $aliasIds->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + // Use update since delete() does not trigger model event + user()->aliases()->onlyTrashed()->whereIn('id', $aliasIds)->update(['active' => true, 'deleted_at' => null]); + + // Don't return 204 as that is only for empty responses + return response()->json([ + 'message' => $aliasIdsCount === 1 ? '1 alias restored successfully' : "{$aliasIdsCount} aliases restored successfully", + 'ids' => $aliasIds, + ], 200); + } + + public function recipients(Request $request) + { + $request->validate([ + 'ids' => 'required|array|max:25|min:1', + 'ids.*' => 'required|uuid|distinct', + 'recipient_ids' => [ + 'array', + 'max:10', + new VerifiedRecipientId(), + ], + 'recipient_ids.*' => 'required|uuid|distinct', + ]); + + $aliasIds = user()->aliases()->withTrashed() + ->whereIn('id', $request->ids) + ->pluck('id'); + + // If there are no aliases found return 404 response + if (! $aliasIdsCount = $aliasIds->count()) { + return response()->json(['message' => 'No aliases found'], 404); + } + + // First delete existing alias recipients + DB::table('alias_recipients')->whereIn('alias_id', $aliasIds)->delete(); + // Then create alias recipients + DB::table('alias_recipients')->insert((collect($aliasIds))->flatMap(function ($aliasId) use ($request) { + $val = []; + foreach ($request->recipient_ids as $recipientId) { + $val[] = [ + 'id' => Uuid::uuid4(), + 'alias_id' => $aliasId, + 'recipient_id' => $recipientId, + ]; + } + + return $val; + })->all()); + + // Don't return 204 as that is only for empty responses + return response()->json([ + 'message' => $aliasIdsCount === 1 ? 'recipients updated for 1 alias successfully' : "recipients updated for {$aliasIdsCount} aliases successfully", + 'ids' => $aliasIds, + ], 200); + } +} diff --git a/app/Http/Controllers/Api/AliasController.php b/app/Http/Controllers/Api/AliasController.php index 8b109fc..8795310 100644 --- a/app/Http/Controllers/Api/AliasController.php +++ b/app/Http/Controllers/Api/AliasController.php @@ -73,8 +73,8 @@ class AliasController extends Controller return response('You have reached your hourly limit for creating new aliases', 429); } - if (isset($request->validated()['local_part'])) { - $localPart = $request->validated()['local_part']; + if (isset($request->validated()['local_part_without_extension'])) { + $localPart = $request->local_part; // To get the local_part with any potential extension // Local part has extension if (Str::contains($localPart, '+')) { @@ -153,7 +153,15 @@ class AliasController extends Controller { $alias = user()->aliases()->withTrashed()->findOrFail($id); - $alias->update(['description' => $request->description]); + if ($request->has('description')) { + $alias->description = $request->description; + } + + if ($request->has('from_name')) { + $alias->from_name = $request->from_name; + } + + $alias->save(); return new AliasResource($alias->refresh()->load('recipients')); } diff --git a/app/Http/Controllers/Api/ChartDataController.php b/app/Http/Controllers/Api/ChartDataController.php new file mode 100644 index 0000000..7e44b7b --- /dev/null +++ b/app/Http/Controllers/Api/ChartDataController.php @@ -0,0 +1,62 @@ +outboundMessages() + ->select(['user_id', 'email_type', 'created_at']) + ->where('created_at', '>=', now()->subDays(6)->startOfDay()) + ->get() + ->groupBy(function ($outboundMessage) { + return $outboundMessage->created_at->format('l'); + }) + ->map(function ($group) { + return [ + 'forwards' => $group->where('email_type', 'F')->count(), + 'replies' => $group->where('email_type', 'R')->count(), + 'sends' => $group->where('email_type', 'S')->count(), + ]; + }); + + $days = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + ]; + + $today = date('w'); // 0 Sunday + + // Get the days until today including today + $previous = array_slice($days, 0, $today + 1); + + // Get remaining days in week + $coming = array_slice($days, $today + 1); + + $data = collect(array_merge($coming, $previous))->mapWithKeys(function ($day) use ($outboundMessages) { + return [$day => $outboundMessages->get($day, ['forwards' => 0, 'replies' => 0, 'sends' => 0])]; + }); + + $outboundMessageTotals = [ + $outboundMessages->sum('forwards'), + $outboundMessages->sum('replies'), + $outboundMessages->sum('sends'), + ]; + + return response()->json([ + 'forwardsData' => $data->pluck('forwards'), + 'repliesData' => $data->pluck('replies'), + 'sendsData' => $data->pluck('sends'), + 'labels' => $data->keys(), + 'outboundMessageTotals' => $outboundMessageTotals, + ]); + } +} diff --git a/app/Http/Controllers/Api/DomainController.php b/app/Http/Controllers/Api/DomainController.php index 591c60d..2d2c76b 100644 --- a/app/Http/Controllers/Api/DomainController.php +++ b/app/Http/Controllers/Api/DomainController.php @@ -47,7 +47,15 @@ class DomainController extends Controller { $domain = user()->domains()->findOrFail($id); - $domain->update(['description' => $request->description]); + if ($request->has('description')) { + $domain->description = $request->description; + } + + if ($request->has('from_name')) { + $domain->from_name = $request->from_name; + } + + $domain->save(); return new DomainResource($domain->refresh()->load(['aliases', 'defaultRecipient'])); } diff --git a/app/Http/Controllers/Api/LoginableUsernameController.php b/app/Http/Controllers/Api/LoginableUsernameController.php new file mode 100644 index 0000000..2fc19a7 --- /dev/null +++ b/app/Http/Controllers/Api/LoginableUsernameController.php @@ -0,0 +1,34 @@ +validate(['id' => 'required|string']); + + $username = user()->usernames()->findOrFail($request->id); + + $username->allowLogin(); + + return new UsernameResource($username->load(['aliases', 'defaultRecipient'])); + } + + public function destroy($id) + { + $username = user()->usernames()->findOrFail($id); + + if ($id === user()->default_username_id) { + return response('You cannot disallow login for your default username', 403); + } + + $username->disallowLogin(); + + return response('', 204); + } +} diff --git a/app/Http/Controllers/Api/UsernameController.php b/app/Http/Controllers/Api/UsernameController.php index a9ff6c3..6d93594 100644 --- a/app/Http/Controllers/Api/UsernameController.php +++ b/app/Http/Controllers/Api/UsernameController.php @@ -38,7 +38,15 @@ class UsernameController extends Controller { $username = user()->usernames()->findOrFail($id); - $username->update(['description' => $request->description]); + if ($request->has('description')) { + $username->description = $request->description; + } + + if ($request->has('from_name')) { + $username->from_name = $request->from_name; + } + + $username->save(); return new UsernameResource($username->refresh()->load(['aliases', 'defaultRecipient'])); } diff --git a/app/Http/Controllers/Auth/ApiAuthenticationController.php b/app/Http/Controllers/Auth/ApiAuthenticationController.php index 2551f13..3cf54d9 100644 --- a/app/Http/Controllers/Auth/ApiAuthenticationController.php +++ b/app/Http/Controllers/Auth/ApiAuthenticationController.php @@ -42,7 +42,7 @@ class ApiAuthenticationController extends Controller } elseif (Webauthn::enabled($user)) { // If WebAuthn is enabled then return currently unsupported message return response()->json([ - 'error' => 'WebAuthn authentication is not currently supported from the extension or mobile apps, please use an API key to login instead', + 'error' => 'Security key authentication is not currently supported from the extension or mobile apps, please use an API key to login instead', ], 403); } diff --git a/app/Http/Controllers/Auth/BackupCodeController.php b/app/Http/Controllers/Auth/BackupCodeController.php index 83bca2f..0563053 100644 --- a/app/Http/Controllers/Auth/BackupCodeController.php +++ b/app/Http/Controllers/Auth/BackupCodeController.php @@ -57,12 +57,19 @@ class BackupCodeController extends Controller return redirect()->intended($request->redirectPath); } - public function update() + public function update(Request $request) { + $request->validate([ + 'current' => 'required|string|current_password', + ]); + user()->update([ 'two_factor_backup_code' => bcrypt($code = Str::random(40)), ]); - return back()->with(['backupCode' => $code]); + return back()->with([ + 'flash' => 'New Backup Code Generated Successfully', + 'regeneratedBackupCode' => $code, + ]); } } diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index df79d90..7ecaae5 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -3,10 +3,10 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; -use App\Models\User; use App\Models\Username; use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Password; class ForgotPasswordController extends Controller @@ -66,7 +66,18 @@ class ForgotPasswordController extends Controller */ protected function validateUsername(Request $request) { - $request->validate(['username' => 'required|regex:/^[a-zA-Z0-9]*$/|max:20']); + // Validate captcha separately first to prevent username enumeration + if (! App::environment('testing')) { + $request->validate([ + 'captcha' => 'required|captcha', + ], [ + 'captcha.captcha' => 'The text entered was incorrect, please try again.', + ]); + } + + $request->validate(['username' => 'required|regex:/^[a-zA-Z0-9]*$/|max:20'], [ + 'username.regex' => 'Your username can only contain letters and numbers, do not use your email.', + ]); } /** diff --git a/app/Http/Controllers/Auth/ForgotUsernameController.php b/app/Http/Controllers/Auth/ForgotUsernameController.php index 4c62845..18711e3 100644 --- a/app/Http/Controllers/Auth/ForgotUsernameController.php +++ b/app/Http/Controllers/Auth/ForgotUsernameController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\Recipient; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; class ForgotUsernameController extends Controller { @@ -54,6 +55,14 @@ class ForgotUsernameController extends Controller */ protected function validateEmail(Request $request) { + if (! App::environment('testing')) { + $request->validate([ + 'captcha' => 'required|captcha', + ], [ + 'captcha.captcha' => 'The text entered was incorrect, please try again.', + ]); + } + $request->validate(['email' => 'required|email:rfc']); } } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index bfb959e..8382a4b 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -6,7 +6,10 @@ use App\Http\Controllers\Controller; use App\Models\Username; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; +use Inertia\Inertia; class LoginController extends Controller { @@ -42,11 +45,17 @@ class LoginController extends Controller public function username() { - $userId = Username::firstWhere('username', request()->input('username'))?->user_id; + return 'id'; + } + + public function addIdToRequest() + { + $userId = Username::select(['user_id', 'username', 'can_login']) + ->where('username', request()->input('username')) + ->where('can_login', true) + ->first()?->user_id; request()->merge(['id' => $userId]); - - return 'id'; } /** @@ -58,10 +67,25 @@ class LoginController extends Controller */ protected function validateLogin(Request $request) { - $request->validate([ - $this->username() => 'nullable|string', + $this->addIdToRequest(); + + Validator::make($request->all(), [ + 'username' => 'required|regex:/^[a-zA-Z0-9]*$/|min:1|max:20', 'password' => 'required|string', - ]); + $this->username() => 'nullable|string', + ], [ + 'username.regex' => 'Your username can only contain letters and numbers, do not use your email.', + ])->validate(); + } + + /** + * Get the needed authorization credentials from the request. + * + * @return array + */ + protected function credentials(Request $request) + { + return $request->only('id', 'password'); } /** @@ -77,4 +101,28 @@ class LoginController extends Controller 'username' => [trans('auth.failed')], ]); } + + /** + * The user has been authenticated. + * + * @param mixed $user + * @return mixed + */ + protected function authenticated(Request $request, $user) + { + // Check if the user's password needs rehashing + if (Hash::needsRehash($user->password)) { + $user->update(['password' => Hash::make($request->password)]); + } + } + + /** + * The user has logged out of the application. + * + * @return mixed + */ + protected function loggedOut(Request $request) + { + return Inertia::location(route('login')); + } } diff --git a/app/Http/Controllers/Auth/PersonalAccessTokenController.php b/app/Http/Controllers/Auth/PersonalAccessTokenController.php index 8f500eb..1eb037b 100644 --- a/app/Http/Controllers/Auth/PersonalAccessTokenController.php +++ b/app/Http/Controllers/Auth/PersonalAccessTokenController.php @@ -6,6 +6,8 @@ use App\Http\Controllers\Controller; use App\Http\Requests\StorePersonalAccessTokenRequest; use App\Http\Resources\PersonalAccessTokenResource; use chillerlan\QRCode\QRCode; +use Illuminate\Support\Facades\Hash; +use Illuminate\Validation\ValidationException; class PersonalAccessTokenController extends Controller { @@ -16,6 +18,10 @@ class PersonalAccessTokenController extends Controller public function store(StorePersonalAccessTokenRequest $request) { + if (! Hash::check($request->password, user()->password)) { + throw ValidationException::withMessages(['password' => 'Incorrect password entered']); + } + // day, week, month, year or null if ($request->expiration) { $method = 'add'.ucfirst($request->expiration); diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 2a45c62..e8e9b82 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -14,6 +14,7 @@ use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; +use Illuminate\Validation\Rules\Password; use Ramsey\Uuid\Uuid; class RegisterController extends Controller @@ -55,6 +56,22 @@ class RegisterController extends Controller */ protected function validator(array $data) { + // Validate captcha separately first to prevent username enumeration + if (! App::environment('testing')) { + $validator = Validator::make($data, [ + 'captcha' => [ + 'required', + 'captcha', + ], + ], [ + 'captcha.captcha' => 'The text entered was incorrect, please try again.', + ]); + + if ($validator->fails()) { + return $validator; + } + } + return Validator::make($data, [ 'username' => [ 'required', @@ -72,13 +89,10 @@ class RegisterController extends Controller new RegisterUniqueRecipient(), new NotLocalRecipient(), ], - 'password' => ['required', 'min:8'], + 'password' => ['required', Password::defaults()], ], [ - 'captcha.captcha' => 'The text entered was incorrect, please try again.', - ]) - ->sometimes('captcha', 'required|captcha', function () { - return ! App::environment('testing'); - }); + 'username.regex' => 'Your username can only contain letters and numbers.', + ]); } /** diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 524cde0..bc609f2 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\Username; use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Http\Request; +use Illuminate\Validation\Rules\Password; class ResetPasswordController extends Controller { @@ -65,7 +66,11 @@ class ResetPasswordController extends Controller return [ 'token' => 'required', 'username' => 'required|regex:/^[a-zA-Z0-9]*$/|max:20', - 'password' => 'required|confirmed|min:8', + 'password' => [ + 'required', + 'confirmed', + Password::defaults(), + ], ]; } diff --git a/app/Http/Controllers/Auth/TwoFactorAuthController.php b/app/Http/Controllers/Auth/TwoFactorAuthController.php index 76f4323..904c1ba 100644 --- a/app/Http/Controllers/Auth/TwoFactorAuthController.php +++ b/app/Http/Controllers/Auth/TwoFactorAuthController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Http\Requests\EnableTwoFactorAuthRequest; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use PragmaRX\Google2FALaravel\Support\Authenticator; @@ -52,14 +51,14 @@ class TwoFactorAuthController extends Controller user()->update(['two_factor_secret' => $this->twoFactor->generateSecretKey()]); - return back()->with(['status' => '2FA Secret Successfully Regenerated']); + return back()->with(['flash' => '2FA Secret Successfully Regenerated']); } public function destroy(Request $request) { - if (! Hash::check($request->current_password_2fa, user()->password)) { - return back()->withErrors(['current_password_2fa' => 'Current password incorrect']); - } + $request->validate([ + 'current' => 'required|string|current_password', + ]); user()->update([ 'two_factor_enabled' => false, @@ -68,7 +67,7 @@ class TwoFactorAuthController extends Controller $this->authenticator->logout(); - return back()->with(['status' => '2FA Disabled Successfully']); + return back()->with(['flash' => '2FA Disabled Successfully']); } public function authenticateTwoFactor(Request $request) diff --git a/app/Http/Controllers/Auth/VerificationController.php b/app/Http/Controllers/Auth/VerificationController.php index c58333e..2f0ed28 100644 --- a/app/Http/Controllers/Auth/VerificationController.php +++ b/app/Http/Controllers/Auth/VerificationController.php @@ -5,11 +5,14 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\Recipient; use App\Models\User; +use App\Notifications\DefaultRecipientUpdated; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Auth\Events\Verified; use Illuminate\Foundation\Auth\VerifiesEmails; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; +use Inertia\Inertia; class VerificationController extends Controller { @@ -46,6 +49,18 @@ class VerificationController extends Controller $this->middleware('throttle:6,1')->only('verify'); } + /** + * Show the email verification notice. + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View + */ + public function show(Request $request) + { + return $request->user()->hasVerifiedEmail() + ? redirect($this->redirectPath()) + : Inertia::render('Auth/Verify', ['flash' => $request->session()->get('resent', null) ? 'A fresh verification link has been sent to your email address.' : null]); + } + /** * Mark the authenticated user's email address as verified. * @@ -55,7 +70,7 @@ class VerificationController extends Controller */ public function verify(Request $request) { - $verifiable = User::find($request->route('id')) ?? Recipient::find($request->route('id')); + $verifiable = User::find($request->route('id')) ?? Recipient::withPending()->find($request->route('id')); if (is_null($verifiable)) { throw new AuthorizationException('Email address not found.'); @@ -83,8 +98,35 @@ class VerificationController extends Controller $redirect = 'login'; } + // Check if the verifiable is a pending new email Recipient + if ($verifiable instanceof Recipient && $verifiable->pending) { + + try { + DB::transaction(function () use ($verifiable) { + $user = $verifiable->user; + $defaultRecipient = $user->defaultRecipient; + // Notify the current default recipient of the change + $defaultRecipient->notify(new DefaultRecipientUpdated($verifiable->email)); + + // Set verifiable email as new default recipient + $defaultRecipient->update([ + 'email' => strtolower($verifiable->email), + 'email_verified_at' => now(), + ]); + + // Delete pending verifiable + $verifiable->delete(); + }); + } catch (\Exception $e) { + report($e); + + return redirect($redirect) + ->with(['flash' => 'An error has occurred, please try again later.']); + } + } + return redirect($redirect) ->with('verified', true) - ->with(['status' => 'Email Address Verified Successfully']); + ->with(['flash' => 'Email Address Verified Successfully']); } } diff --git a/app/Http/Controllers/Auth/WebauthnController.php b/app/Http/Controllers/Auth/WebauthnController.php index 7b6c546..303fe71 100644 --- a/app/Http/Controllers/Auth/WebauthnController.php +++ b/app/Http/Controllers/Auth/WebauthnController.php @@ -2,16 +2,13 @@ namespace App\Http\Controllers\Auth; -use App\Facades\Webauthn as WebauthnFacade; -use App\Models\WebauthnKey; -use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Redirect; -use Illuminate\Support\Facades\Response; -use Illuminate\Support\Str; use LaravelWebauthn\Actions\PrepareCreationData; use LaravelWebauthn\Actions\ValidateKeyCreation; +use LaravelWebauthn\Contracts\DestroyResponse; +use LaravelWebauthn\Contracts\RegisterSuccessResponse; use LaravelWebauthn\Contracts\RegisterViewResponse; +use LaravelWebauthn\Facades\Webauthn; use LaravelWebauthn\Http\Controllers\WebauthnKeyController as ControllersWebauthnController; use LaravelWebauthn\Http\Requests\WebauthnRegisterRequest; @@ -24,67 +21,39 @@ class WebauthnController extends ControllersWebauthnController /** * Return the register data to attempt a Webauthn registration. - * - * @return RegisterViewResponse */ - public function create(Request $request) + /* public function create(Request $request): RegisterViewResponse { $publicKey = app(PrepareCreationData::class)($request->user()); return app(RegisterViewResponse::class) ->setPublicKey($request, $publicKey); - } + } */ /** * Validate and create the Webauthn request. * * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse */ - public function store(WebauthnRegisterRequest $request) + public function store(WebauthnRegisterRequest $request): RegisterSuccessResponse { $request->validate([ 'name' => 'required|string|max:50', + 'password' => 'required|string|current_password', ]); - try { - app(ValidateKeyCreation::class)( - $request->user(), - $request->only(['id', 'rawId', 'response', 'type']), - $request->input('name') - ); - - user()->update([ - 'two_factor_enabled' => false, - ]); - - return $this->redirectAfterSuccessRegister(); - } catch (\Exception $e) { - return Response::json([ - 'error' => [ - 'message' => $e->getMessage(), - ], - ], 403); - } - } - - /** - * Return the redirect destination after a successfull register. - * - * @param WebauthnKey $webauthnKey - * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse - */ - protected function redirectAfterSuccessRegister() - { - // If the user already has at least one key do not generate a new backup code. - if (user()->webauthnKeys()->count() > 1) { - return Redirect::intended('/settings'); - } + $webauthnKey = app(ValidateKeyCreation::class)( + $request->user(), + $request->only(['id', 'rawId', 'response', 'type']), + $request->input('name') + ); user()->update([ - 'two_factor_backup_code' => bcrypt($code = Str::random(40)), + 'two_factor_enabled' => false, ]); - return Redirect::intended('/settings')->with(['backupCode' => $code]); + return app(RegisterSuccessResponse::class) + ->setWebauthnKey($request, $webauthnKey); } /** @@ -92,27 +61,27 @@ class WebauthnController extends ControllersWebauthnController * * @return \Illuminate\Http\JsonResponse */ - public function destroy(Request $request, $webauthnKeyId) + public function destroy(Request $request, $webauthnKeyId): DestroyResponse { - try { - user()->webauthnKeys() - ->findOrFail($webauthnKeyId) - ->delete(); + $request->validate([ + 'current' => 'required|string|current_password', + ]); - if (! WebauthnFacade::hasKey(user())) { - WebauthnFacade::logout(); - } + user()->webauthnKeys() + ->findOrFail($webauthnKeyId) + ->delete(); - return Response::json([ - 'deleted' => true, - 'id' => $webauthnKeyId, - ]); - } catch (ModelNotFoundException $e) { - return Response::json([ - 'error' => [ - 'message' => trans('webauthn::errors.object_not_found'), - ], - ], 404); + // Using vendor Facade to ensure disabled keys are included + if (! Webauthn::hasKey(user())) { + // Remove session value when last key is deleted + Webauthn::logout(); } + + return app(DestroyResponse::class); + } + + public function delete() + { + return abort(404); } } diff --git a/app/Http/Controllers/Auth/WebauthnEnabledKeyController.php b/app/Http/Controllers/Auth/WebauthnEnabledKeyController.php index 91099f4..00899d0 100644 --- a/app/Http/Controllers/Auth/WebauthnEnabledKeyController.php +++ b/app/Http/Controllers/Auth/WebauthnEnabledKeyController.php @@ -16,8 +16,12 @@ class WebauthnEnabledKeyController extends Controller return response('', 201); } - public function destroy($id) + public function destroy(Request $request, $id) { + $request->validate([ + 'current' => 'required|string|current_password', + ]); + $webauthnKey = user()->webauthnKeys()->findOrFail($id); $webauthnKey->disable(); diff --git a/app/Http/Controllers/BannerLocationController.php b/app/Http/Controllers/BannerLocationController.php index 6c7502b..c75ba88 100644 --- a/app/Http/Controllers/BannerLocationController.php +++ b/app/Http/Controllers/BannerLocationController.php @@ -10,6 +10,6 @@ class BannerLocationController extends Controller { user()->update(['banner_location' => $request->banner_location]); - return back()->with(['status' => 'Location Updated Successfully']); + return back()->with(['flash' => 'Location Updated Successfully']); } } diff --git a/app/Http/Controllers/BrowserSessionController.php b/app/Http/Controllers/BrowserSessionController.php index 243ab2e..d20a4ed 100644 --- a/app/Http/Controllers/BrowserSessionController.php +++ b/app/Http/Controllers/BrowserSessionController.php @@ -10,11 +10,11 @@ class BrowserSessionController extends Controller public function destroy(Request $request) { $request->validate([ - 'current_password_sesssions' => 'current_password', + 'current' => 'required|string|current_password', ]); - Auth::logoutOtherDevices($request->current_password_sesssions); + Auth::logoutOtherDevices($request->current); - return back()->with(['status' => 'Successfully logged out of other browser sessions!']); + return back()->with(['flash' => 'Successfully logged out of other browser sessions!']); } } diff --git a/app/Http/Controllers/DeactivateAliasController.php b/app/Http/Controllers/DeactivateAliasController.php index 9a2ee0d..5cf3eba 100644 --- a/app/Http/Controllers/DeactivateAliasController.php +++ b/app/Http/Controllers/DeactivateAliasController.php @@ -22,6 +22,6 @@ class DeactivateAliasController extends Controller $alias->deactivate(); return redirect()->route('aliases.index') - ->with(['status' => 'Alias '.$alias->email.' deactivated successfully!']); + ->with(['flash' => 'Alias '.$alias->email.' deactivated successfully!']); } } diff --git a/app/Http/Controllers/DefaultAliasController.php b/app/Http/Controllers/DefaultAliasController.php deleted file mode 100644 index 9e9764e..0000000 --- a/app/Http/Controllers/DefaultAliasController.php +++ /dev/null @@ -1,16 +0,0 @@ -default_alias_format = $request->format; - user()->save(); - - return back()->with(['status' => 'Default Alias Format Updated Successfully']); - } -} diff --git a/app/Http/Controllers/DefaultAliasDomainController.php b/app/Http/Controllers/DefaultAliasDomainController.php index 7b5b925..dad9370 100644 --- a/app/Http/Controllers/DefaultAliasDomainController.php +++ b/app/Http/Controllers/DefaultAliasDomainController.php @@ -11,6 +11,6 @@ class DefaultAliasDomainController extends Controller user()->default_alias_domain = $request->domain; user()->save(); - return back()->with(['status' => 'Default Alias Domain Updated Successfully']); + return back()->with(['flash' => 'Default Alias Domain Updated Successfully']); } } diff --git a/app/Http/Controllers/DefaultAliasFormatController.php b/app/Http/Controllers/DefaultAliasFormatController.php index 9e9764e..53aa081 100644 --- a/app/Http/Controllers/DefaultAliasFormatController.php +++ b/app/Http/Controllers/DefaultAliasFormatController.php @@ -11,6 +11,6 @@ class DefaultAliasFormatController extends Controller user()->default_alias_format = $request->format; user()->save(); - return back()->with(['status' => 'Default Alias Format Updated Successfully']); + return back()->with(['flash' => 'Default Alias Format Updated Successfully']); } } diff --git a/app/Http/Controllers/DefaultRecipientController.php b/app/Http/Controllers/DefaultRecipientController.php index 2066f2b..07849fa 100644 --- a/app/Http/Controllers/DefaultRecipientController.php +++ b/app/Http/Controllers/DefaultRecipientController.php @@ -21,29 +21,46 @@ class DefaultRecipientController extends Controller public function update(UpdateDefaultRecipientRequest $request) { - $recipient = user()->verifiedRecipients()->findOrFail($request->default_recipient); + $recipient = user()->verifiedRecipients()->findOrFail($request->id); $currentDefaultRecipient = user()->defaultRecipient; - user()->default_recipient = $recipient; - user()->save(); + user()->update(['default_recipient_id' => $recipient->id]); if ($currentDefaultRecipient->id !== $recipient->id) { $currentDefaultRecipient->notify(new DefaultRecipientUpdated($recipient->email)); } - return back()->with(['status' => 'Default Recipient Updated Successfully']); + return response()->json([ + 'success' => true, + ]); } public function edit(EditDefaultRecipientRequest $request) { $recipient = user()->defaultRecipient; - $recipient->email = $request->email; + // Updating already verified default recipient, create new pending entry and send verification email. + if ($recipient->hasVerifiedEmail()) { + // Clear all other pending entries + user()->pendingRecipients()->delete(); + + $pendingRecipient = user()->recipients()->create([ + 'email' => strtolower($request->email), + 'pending' => true, + ]); + + $pendingRecipient->sendEmailVerificationNotification(); + + return back()->with(['flash' => 'Email Pending Verification, Please Check Your Inbox For The Verification Email']); + } + + // Unverified default recipient so we can simply update and send the verification email. + $recipient->email = strtolower($request->email); $recipient->save(); user()->sendEmailVerificationNotification(); - return back()->with(['status' => 'Email Updated Successfully, Please Check Your Inbox For The Verification Email']); + return back()->with(['flash' => 'Email Updated Successfully, Please Check Your Inbox For The Verification Email']); } } diff --git a/app/Http/Controllers/DefaultUsernameController.php b/app/Http/Controllers/DefaultUsernameController.php new file mode 100644 index 0000000..5c36307 --- /dev/null +++ b/app/Http/Controllers/DefaultUsernameController.php @@ -0,0 +1,22 @@ +usernames()->findOrFail($request->id); + + // Ensure username can be used to login + $username->allowLogin(); + + user()->update(['default_username_id' => $username->id]); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/app/Http/Controllers/DisplayFromFormatController.php b/app/Http/Controllers/DisplayFromFormatController.php new file mode 100644 index 0000000..5ebc6fc --- /dev/null +++ b/app/Http/Controllers/DisplayFromFormatController.php @@ -0,0 +1,17 @@ +display_from_format = DisplayFromFormat::from($request->format); + user()->save(); + + return back()->with(['flash' => 'Default Alias Format Updated Successfully']); + } +} diff --git a/app/Http/Controllers/EmailSubjectController.php b/app/Http/Controllers/EmailSubjectController.php index 12477fd..871a4ad 100644 --- a/app/Http/Controllers/EmailSubjectController.php +++ b/app/Http/Controllers/EmailSubjectController.php @@ -10,6 +10,6 @@ class EmailSubjectController extends Controller { user()->update(['email_subject' => $request->email_subject]); - return back()->with(['status' => 'Email Subject Updated Successfully']); + return back()->with(['flash' => 'Email Subject Updated Successfully']); } } diff --git a/app/Http/Controllers/FromNameController.php b/app/Http/Controllers/FromNameController.php index d1a0e5a..703de7c 100644 --- a/app/Http/Controllers/FromNameController.php +++ b/app/Http/Controllers/FromNameController.php @@ -2,14 +2,14 @@ namespace App\Http\Controllers; -use App\Http\Requests\UpdateFromNameRequest; +use App\Http\Requests\UpdateAccountFromNameRequest; class FromNameController extends Controller { - public function update(UpdateFromNameRequest $request) + public function update(UpdateAccountFromNameRequest $request) { user()->update(['from_name' => $request->from_name]); - return back()->with(['status' => 'From Name Updated Successfully']); + return back()->with(['flash' => 'From Name Updated Successfully']); } } diff --git a/app/Http/Controllers/PasswordController.php b/app/Http/Controllers/PasswordController.php index 425fd9e..5bff31d 100644 --- a/app/Http/Controllers/PasswordController.php +++ b/app/Http/Controllers/PasswordController.php @@ -10,16 +10,12 @@ class PasswordController extends Controller { public function update(UpdatePasswordRequest $request) { - if (! Hash::check($request->current, user()->password)) { - return redirect(url()->previous().'#update-password')->withErrors(['current' => 'Current password incorrect']); - } - // Log out of other sessions Auth::logoutOtherDevices($request->current); user()->password = Hash::make($request->password); user()->save(); - return back()->with(['status' => 'Password Updated Successfully']); + return back()->with(['flash' => 'Password Updated Successfully']); } } diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php index 6412e6e..b1083cd 100644 --- a/app/Http/Controllers/SettingController.php +++ b/app/Http/Controllers/SettingController.php @@ -3,12 +3,30 @@ namespace App\Http\Controllers; use App\Http\Requests\DestroyAccountRequest; +use App\Http\Resources\PersonalAccessTokenResource; use App\Jobs\DeleteAccount; -use Illuminate\Support\Facades\Hash; +use Illuminate\Http\Request; +use Inertia\Inertia; +use LaravelWebauthn\Facades\Webauthn; class SettingController extends Controller { public function show() + { + return Inertia::render('Settings/General', [ + 'defaultAliasDomain' => user()->default_alias_domain, + 'defaultAliasFormat' => user()->default_alias_format, + 'displayFromFormat' => user()->display_from_format->value, + 'useReplyTo' => user()->use_reply_to, + 'storeFailedDeliveries' => user()->store_failed_deliveries, + 'fromName' => user()->from_name ?? '', + 'emailSubject' => user()->email_subject ?? '', + 'bannerLocation' => user()->banner_location, + 'domainOptions' => user()->domainOptions(), + ]); + } + + public function security(Request $request) { $twoFactor = app('pragmarx.google2fa'); @@ -18,26 +36,48 @@ class SettingController extends Controller user()->two_factor_secret ); - return view('settings.show', [ - 'user' => user(), - 'recipientOptions' => user()->verifiedRecipients, - 'authSecret' => user()->two_factor_secret, - 'qrCode' => $qrCode, + // User has either webauthn or TOTP 2FA enabled + $hasTwoFactor = Webauthn::enabled(user()) || user()->two_factor_enabled; + + return Inertia::render('Settings/Security', [ + 'authSecret' => $hasTwoFactor ? null : user()->two_factor_secret, + 'qrCode' => $hasTwoFactor ? null : $qrCode, + 'regeneratedBackupCode' => $request->session()->get('regeneratedBackupCode', null), + 'backupCode' => $request->session()->get('backupCode', null), + 'twoFactorEnabled' => user()->two_factor_enabled, + 'webauthnEnabled' => Webauthn::enabled(user()), + 'initialKeys' => user()->webauthnKeys()->latest()->select(['id', 'name', 'enabled', 'created_at'])->get()->values(), ]); } + public function api() + { + return Inertia::render('Settings/Api', [ + 'initialTokens' => PersonalAccessTokenResource::collection(user()->tokens()->select(['id', 'tokenable_id', 'name', 'created_at', 'last_used_at', 'expires_at', 'updated_at', 'created_at'])->get()), + ]); + } + + public function data() + { + return Inertia::render('Settings/Data', [ + 'totalAliasesCount' => user()->allAliases()->count(), + 'domainsCount' => user()->domains()->count(), + ]); + } + + public function account() + { + return Inertia::render('Settings/Account'); + } + public function destroy(DestroyAccountRequest $request) { - if (! Hash::check($request->current_password_delete, user()->password)) { - return back()->withErrors(['current_password_delete' => 'Incorrect password entered']); - } - DeleteAccount::dispatch(user()); auth()->logout(); $request->session()->invalidate(); return redirect()->route('login') - ->with(['status' => 'Account deleted successfully!']); + ->with(['flash' => 'Account deleted successfully!']); } } diff --git a/app/Http/Controllers/ShowAliasController.php b/app/Http/Controllers/ShowAliasController.php index bea81f2..26da305 100644 --- a/app/Http/Controllers/ShowAliasController.php +++ b/app/Http/Controllers/ShowAliasController.php @@ -2,34 +2,192 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Illuminate\Validation\Rule; +use Inertia\Inertia; + class ShowAliasController extends Controller { - public function index() + public function index(Request $request) { - $totals = user() - ->aliases() - ->withTrashed() - ->toBase() - ->selectRaw('ifnull(sum(emails_forwarded),0) as forwarded') - ->selectRaw('ifnull(sum(emails_blocked),0) as blocked') - ->selectRaw('ifnull(sum(emails_replied),0) as replies') - ->first(); + $validated = $request->validate([ + 'page' => [ + 'nullable', + 'integer', + ], + 'page_size' => [ + 'nullable', + 'integer', + 'in:25,50,100', + ], + 'search' => [ + 'nullable', + 'string', + 'max:50', + 'min:2', + ], + 'deleted' => [ + 'nullable', + 'in:with,without,only', + 'string', + ], + 'active' => [ + 'nullable', + 'in:true,false', + 'string', + ], + 'shared_domain' => [ + 'nullable', + 'in:true,false', + 'string', + ], + 'sort' => [ + 'nullable', + 'max:20', + 'min:3', + Rule::in([ + 'local_part', + 'domain', + 'email', + 'emails_forwarded', + 'emails_blocked', + 'emails_replied', + 'emails_sent', + 'active', + 'created_at', + 'updated_at', + 'deleted_at', + '-local_part', + '-domain', + '-email', + '-emails_forwarded', + '-emails_blocked', + '-emails_replied', + '-emails_sent', + '-active', + '-created_at', + '-updated_at', + '-deleted_at', + ]), + ], + 'recipient' => [ + 'nullable', + 'uuid', + ], + 'domain' => [ + 'nullable', + 'uuid', + ], + 'username' => [ + 'nullable', + 'uuid', + ], + ]); - return view('aliases.index', [ - 'user' => user(), - 'defaultRecipientEmail' => user()->email, - 'aliases' => user() - ->aliases() - ->with([ - 'recipients:id,email', - 'aliasable.defaultRecipient:id,email', - ]) - ->latest() - ->get(), - 'recipients' => user()->verifiedRecipients()->select(['id', 'email'])->get(), - 'totals' => $totals, - 'domain' => user()->username.'.'.config('anonaddy.domain'), - 'domainOptions' => user()->domainOptions(), + if ($request->has('sort')) { + $direction = strpos($request->input('sort'), '-') === 0 ? 'desc' : 'asc'; + $sort = ltrim($request->input('sort'), '-'); + } else { + $direction = 'desc'; + $sort = 'created_at'; + } + + $aliases = user()->aliases() + ->select(['id', 'user_id', 'aliasable_id', 'aliasable_type', 'local_part', 'extension', 'email', 'domain', 'description', 'active', 'emails_forwarded', 'emails_blocked', 'emails_replied', 'emails_sent', 'created_at', 'deleted_at']) + ->when($request->input('recipient'), function ($query, $id) { + return $query->usesRecipientWithId($id, $id === user()->default_recipient_id); + }) + ->when($request->input('domain'), function ($query, $id) { + return $query->belongsToAliasable('App\Models\Domain', $id); + }) + ->when($request->input('username'), function ($query, $id) { + return $query->belongsToAliasable('App\Models\Username', $id); + }) + ->when($request->input('sort'), function ($query) use ($sort, $direction) { + if ($sort === 'created_at') { + return $query->orderBy($sort, $direction); + } + + // Secondary order by latest first + return $query + ->orderBy($sort, $direction) + ->orderBy('created_at', 'desc'); + }, function ($query) { + return $query->latest(); + }) + ->when($request->input('active'), function ($query, $value) { + $active = $value === 'true' ? true : false; + + return $query->where('active', $active); + }) + ->when($request->input('shared_domain'), function ($query, $value) { + if ($value === 'true') { + return $query->whereIn('domain', config('anonaddy.all_domains')); + } + + return $query->whereNotIn('domain', config('anonaddy.all_domains')); + }) + ->with([ + 'recipients:id,email', + 'aliasable.defaultRecipient:id,email', + ]); + + // Check if with deleted + if ($request->deleted === 'with') { + $aliases->withTrashed(); + } + + if ($request->deleted === 'only') { + $aliases->onlyTrashed(); + } + + if (isset($validated['search'])) { + $searchTerm = strtolower($validated['search']); + + // Chunk aliases and build results array by passing &$results, this is for users with tens of thousands of aliases to prevent out of memory issues. + $searchResults = collect(); + $aliases->chunk(10000, function ($chunkedAliases) use (&$searchResults, $searchTerm) { + $searchResults = $searchResults->concat($chunkedAliases->filter(function ($alias) use ($searchTerm) { + return Str::contains(strtolower($alias->email), $searchTerm) || Str::contains(strtolower($alias->description), $searchTerm); + })->values()); + }); + + $aliases = $searchResults; + } + + $aliases = $aliases->paginate($validated['page_size'] ?? 25)->withQueryString()->onEachSide(1); + + if ($request->has('active')) { + $currentAliasStatus = $request->input('active') === 'true' ? 'active' : 'inactive'; + } elseif ($request->has('deleted')) { + $currentAliasStatus = $request->input('deleted') === 'with' ? 'all' : 'deleted'; + } else { + $currentAliasStatus = 'active_inactive'; + } + + return Inertia::render('Aliases/Index', [ + 'initialRows' => fn () => $aliases, + 'recipientOptions' => fn () => user()->verifiedRecipients()->select(['id', 'email'])->get(), + 'subdomain' => fn () => user()->username.'.'.config('anonaddy.domain'), + 'domainOptions' => fn () => user()->domainOptions(), + 'defaultAliasDomain' => fn () => user()->default_alias_domain, + 'defaultAliasFormat' => fn () => user()->default_alias_format, + 'search' => $validated['search'] ?? null, + 'initialPageSize' => isset($validated['page_size']) ? (int) $validated['page_size'] : 25, + 'sort' => $sort, + 'sortDirection' => $direction, + 'currentAliasStatus' => $currentAliasStatus, + 'sharedDomains' => user()->sharedDomainOptions(), + ]); + } + + public function edit($id) + { + $alias = user()->aliases()->withTrashed()->findOrFail($id); + + return Inertia::render('Aliases/Edit', [ + 'initialAlias' => $alias->only(['id', 'user_id', 'local_part', 'extension', 'domain', 'email', 'active', 'description', 'from_name', 'deleted_at', 'updated_at']), ]); } } diff --git a/app/Http/Controllers/ShowDashboardController.php b/app/Http/Controllers/ShowDashboardController.php new file mode 100644 index 0000000..5476bad --- /dev/null +++ b/app/Http/Controllers/ShowDashboardController.php @@ -0,0 +1,37 @@ +aliases() + ->withTrashed() + ->toBase() + ->selectRaw('ifnull(count(id),0) as total') + ->selectRaw('ifnull(sum(active=1),0) as active') + ->selectRaw('ifnull(sum(CASE WHEN active=0 AND deleted_at IS NULL THEN 1 END),0) as inactive') + ->selectRaw('ifnull(sum(CASE WHEN deleted_at IS NOT NULL THEN 1 END),0) as deleted') + ->selectRaw('ifnull(sum(emails_forwarded),0) as forwarded') + ->selectRaw('ifnull(sum(emails_blocked),0) as blocked') + ->selectRaw('ifnull(sum(emails_replied),0) as replies') + ->selectRaw('ifnull(sum(emails_sent),0) as sent') + ->first(); + + return Inertia::render('Dashboard/Index', [ + 'totals' => $totals, + 'bandwidthMb' => user()->bandwidthMb, + 'bandwidthLimit' => user()->getBandwidthLimitMb(), + 'month' => now()->format('F'), + 'aliases' => user()->activeSharedDomainAliases()->count(), + 'recipients' => user()->recipients()->count(), + 'usernames' => user()->usernames()->count(), + 'domains' => user()->domains()->count(), + 'rules' => user()->rules()->count(), + ]); + } +} diff --git a/app/Http/Controllers/ShowDomainController.php b/app/Http/Controllers/ShowDomainController.php index d110e5b..2d90b66 100644 --- a/app/Http/Controllers/ShowDomainController.php +++ b/app/Http/Controllers/ShowDomainController.php @@ -2,17 +2,49 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Inertia\Inertia; + class ShowDomainController extends Controller { - public function index() + public function index(Request $request) { - return view('domains.index', [ - 'domains' => user() - ->domains() - ->with('defaultRecipient:id,email') - ->withCount('aliases') - ->latest() - ->get(), + // Validate search query + $validated = $request->validate([ + 'search' => 'nullable|string|max:50|min:2', + ]); + + $domains = user() + ->domains() + ->select(['id', 'user_id', 'default_recipient_id', 'domain', 'description', 'active', 'catch_all', 'domain_mx_validated_at', 'domain_sending_verified_at', 'created_at']) + ->with('defaultRecipient:id,email') + ->withCount('aliases') + ->latest() + ->get(); + + if (isset($validated['search'])) { + $searchTerm = strtolower($validated['search']); + + $domains = $domains->filter(function ($domain) use ($searchTerm) { + return Str::contains(strtolower($domain->domain), $searchTerm) || Str::contains(strtolower($domain->description), $searchTerm); + })->values(); + } + + return Inertia::render('Domains/Index', [ + 'initialRows' => $domains, + 'recipientOptions' => user()->verifiedRecipients()->select(['id', 'email'])->get(), + 'initialAaVerify' => sha1(config('anonaddy.secret').user()->id.user()->domains->count()), + 'search' => $validated['search'] ?? null, + ]); + } + + public function edit($id) + { + $domain = user()->domains()->findOrFail($id); + + return Inertia::render('Domains/Edit', [ + 'initialDomain' => $domain->only(['id', 'user_id', 'domain', 'description', 'from_name', 'domain_sending_verified_at', 'domain_mx_validated_at', 'updated_at']), ]); } } diff --git a/app/Http/Controllers/ShowFailedDeliveryController.php b/app/Http/Controllers/ShowFailedDeliveryController.php index e48fa32..c7a5d4f 100644 --- a/app/Http/Controllers/ShowFailedDeliveryController.php +++ b/app/Http/Controllers/ShowFailedDeliveryController.php @@ -2,17 +2,37 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Inertia\Inertia; + class ShowFailedDeliveryController extends Controller { - public function index() + public function index(Request $request) { - return view('failed_deliveries.index', [ - 'failedDeliveries' => user() - ->failedDeliveries() - ->with(['recipient:id,email', 'alias:id,email']) - ->select(['alias_id', 'bounce_type', 'code', 'attempted_at', 'created_at', 'id', 'recipient_id', 'remote_mta', 'sender', 'is_stored']) - ->latest() - ->get(), + // Validate search query + $validated = $request->validate([ + 'search' => 'nullable|string|max:50|min:2', + ]); + + $failedDeliveries = user() + ->failedDeliveries() + ->with(['recipient:id,email', 'alias:id,email']) + ->select(['alias_id', 'email_type', 'code', 'attempted_at', 'created_at', 'id', 'user_id', 'recipient_id', 'remote_mta', 'sender', 'destination', 'is_stored']) + ->latest() + ->get(); + + if (isset($validated['search'])) { + $searchTerm = strtolower($validated['search']); + + $failedDeliveries = $failedDeliveries->filter(function ($failedDelivery) use ($searchTerm) { + return Str::contains(strtolower($failedDelivery->code), $searchTerm); + })->values(); + } + + return Inertia::render('FailedDeliveries', [ + 'initialRows' => $failedDeliveries, + 'search' => $validated['search'] ?? null, ]); } } diff --git a/app/Http/Controllers/ShowRecipientController.php b/app/Http/Controllers/ShowRecipientController.php index 29e9a0f..27098fa 100644 --- a/app/Http/Controllers/ShowRecipientController.php +++ b/app/Http/Controllers/ShowRecipientController.php @@ -2,31 +2,31 @@ namespace App\Http\Controllers; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Inertia\Inertia; + class ShowRecipientController extends Controller { - public function index() + public function index(Request $request) { - $recipients = user()->recipients()->with([ - 'aliases:id,aliasable_id,email', - 'domainsUsingAsDefault.aliases:id,aliasable_id,email', - 'usernamesUsingAsDefault.aliases:id,aliasable_id,email', - ])->latest()->get(); + // Validate search query + $validated = $request->validate([ + 'search' => 'nullable|string|max:50|min:2', + ]); - $recipients->each(function ($recipient) { - if ($recipient->domainsUsingAsDefault) { - $domainAliases = $recipient->domainsUsingAsDefault->flatMap(function ($domain) { - return $domain->aliases; - }); - $recipient->setRelation('aliases', $recipient->aliases->concat($domainAliases)->unique('email')); - } - - if ($recipient->usernamesUsingAsDefault) { - $usernameAliases = $recipient->usernamesUsingAsDefault->flatMap(function ($domain) { - return $domain->aliases; - }); - $recipient->setRelation('aliases', $recipient->aliases->concat($usernameAliases)->unique('email')); - } - }); + $recipients = user()->recipients() + ->select([ + 'id', + 'user_id', + 'email', + 'should_encrypt', + 'fingerprint', + 'email_verified_at', + 'created_at', + ]) + ->latest()->get(); $count = $recipients->count(); @@ -34,11 +34,55 @@ class ShowRecipientController extends Controller $item['key'] = $count - $key; }); - return view('recipients.index', [ - 'recipients' => $recipients, - 'aliasesUsingDefault' => user()->aliasesUsingDefault()->take(5)->get(), - 'aliasesUsingDefaultCount' => user()->aliasesUsingDefault()->count(), - 'user' => user()->load('defaultUsername'), + if (isset($validated['search'])) { + $searchTerm = strtolower($validated['search']); + + $recipients = $recipients->filter(function ($recipient) use ($searchTerm) { + return Str::contains(strtolower($recipient->email), $searchTerm); + })->values(); + } + + return Inertia::render('Recipients/Index', [ + 'initialRows' => $recipients, + //'aliasesUsingDefaultCount' => user()->aliasesUsingDefaultCount(), + 'search' => $validated['search'] ?? null, + ]); + } + + public function aliasCount(Request $request) + { + // Validate search query + $validated = $request->validate([ + 'ids' => 'required|array|max:30|min:1', + 'ids.*' => 'required|uuid|distinct', + ]); + + $count = user()->recipients() + ->whereIn('id', $validated['ids']) + ->select([ + 'id', + 'user_id', + ])->withCount([ + 'aliases', + 'domainAliasesUsingAsDefault' => function (Builder $query) { + $query->doesntHave('recipients'); + }, + 'usernameAliasesUsingAsDefault' => function (Builder $query) { + $query->doesntHave('recipients'); + }, + ])->latest()->get(); // Must order by the same to ensure keys match + + return response()->json([ + 'count' => $count, + ], 200); + } + + public function edit($id) + { + $recipient = user()->recipients()->findOrFail($id); + + return Inertia::render('Recipients/Edit', [ + 'initialRecipient' => $recipient->only(['id', 'user_id', 'email', 'can_reply_send', 'fingerprint', 'protected_headers', 'inline_encryption', 'email_verified_at', 'updated_at']), ]); } } diff --git a/app/Http/Controllers/ShowRuleController.php b/app/Http/Controllers/ShowRuleController.php index e3a0758..1079be0 100644 --- a/app/Http/Controllers/ShowRuleController.php +++ b/app/Http/Controllers/ShowRuleController.php @@ -2,15 +2,27 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Inertia\Inertia; + class ShowRuleController extends Controller { - public function index() + public function index(Request $request) { - return view('rules.index', [ - 'rules' => user() + // Validate search query + $validated = $request->validate([ + 'search' => 'nullable|string|max:50|min:2', + ]); + + return Inertia::render('Rules', [ + 'initialRows' => user() ->rules() + ->when($request->input('search'), function ($query, $search) { + return $query->where('name', 'like', '%'.$search.'%'); + }) ->orderBy('order') ->get(), + 'search' => $validated['search'] ?? null, ]); } } diff --git a/app/Http/Controllers/ShowUsernameController.php b/app/Http/Controllers/ShowUsernameController.php index bb17c5c..b639b39 100644 --- a/app/Http/Controllers/ShowUsernameController.php +++ b/app/Http/Controllers/ShowUsernameController.php @@ -2,17 +2,49 @@ namespace App\Http\Controllers; +use Illuminate\Http\Request; +use Illuminate\Support\Str; +use Inertia\Inertia; + class ShowUsernameController extends Controller { - public function index() + public function index(Request $request) { - return view('usernames.index', [ - 'usernames' => user() - ->usernames() - ->with('defaultRecipient:id,email') - ->withCount('aliases') - ->latest() - ->get(), + // Validate search query + $validated = $request->validate([ + 'search' => 'nullable|string|max:50|min:2', + ]); + + $usernames = user() + ->usernames() + ->select(['id', 'user_id', 'default_recipient_id', 'username', 'description', 'active', 'catch_all', 'created_at']) + ->with('defaultRecipient:id,email') + ->withCount('aliases') + ->latest() + ->get(); + + if (isset($validated['search'])) { + $searchTerm = strtolower($validated['search']); + + $usernames = $usernames->filter(function ($username) use ($searchTerm) { + return Str::contains(strtolower($username->username), $searchTerm) || Str::contains(strtolower($username->description), $searchTerm); + })->values(); + } + + return Inertia::render('Usernames/Index', [ + 'initialRows' => $usernames, + 'recipientOptions' => user()->verifiedRecipients()->select(['id', 'email'])->get(), + 'search' => $validated['search'] ?? null, + 'usernameCount' => (int) config('anonaddy.additional_username_limit'), + ]); + } + + public function edit($id) + { + $username = user()->usernames()->findOrFail($id); + + return Inertia::render('Usernames/Edit', [ + 'initialUsername' => $username->only(['id', 'user_id', 'username', 'description', 'from_name', 'can_login', 'updated_at']), ]); } } diff --git a/app/Http/Controllers/StoreFailedDeliveryController.php b/app/Http/Controllers/StoreFailedDeliveryController.php index aeec6b6..e6506f9 100644 --- a/app/Http/Controllers/StoreFailedDeliveryController.php +++ b/app/Http/Controllers/StoreFailedDeliveryController.php @@ -14,6 +14,6 @@ class StoreFailedDeliveryController extends Controller user()->update(['store_failed_deliveries' => false]); } - return back()->with(['status' => $request->store_failed_deliveries ? 'Store Failed Deliveries Enabled Successfully' : 'Store Failed Deliveries Disabled Successfully']); + return back()->with(['flash' => $request->store_failed_deliveries ? 'Store Failed Deliveries Enabled Successfully' : 'Store Failed Deliveries Disabled Successfully']); } } diff --git a/app/Http/Controllers/UseReplyToController.php b/app/Http/Controllers/UseReplyToController.php index 5d108e9..01d2945 100644 --- a/app/Http/Controllers/UseReplyToController.php +++ b/app/Http/Controllers/UseReplyToController.php @@ -14,6 +14,6 @@ class UseReplyToController extends Controller user()->update(['use_reply_to' => false]); } - return back()->with(['status' => $request->use_reply_to ? 'Use Reply To Enabled Successfully' : 'Use Reply To Disabled Successfully']); + return back()->with(['flash' => $request->use_reply_to ? 'Use Reply To Enabled Successfully' : 'Use Reply To Disabled Successfully']); } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 2f04908..1d7582b 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -37,6 +37,7 @@ class Kernel extends HttpKernel \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\HandleInertiaRequests::class, // Must be the last item! ], 'api' => [ diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php new file mode 100644 index 0000000..d9a6e7e --- /dev/null +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -0,0 +1,62 @@ + $request->session()->get('flash', null), + 'user' => function () use ($request) { + if (! $request->user()) { + return; + } + + $user = $request->user(); + + return [ + 'username' => $user->username, + 'email' => $user->email, + 'default_recipient_id' => $user->default_recipient_id, + 'default_username_id' => $user->default_username_id, + ]; + }, + 'errorBags' => function () { + return collect(optional(Session::get('errors'))->getBags() ?: [])->mapWithKeys(function ($bag, $key) { + return [$key => $bag->messages()]; + })->all(); + }, + 'version' => GitVersionHelper::version(), + ]); + } +} diff --git a/app/Http/Requests/DestroyAccountRequest.php b/app/Http/Requests/DestroyAccountRequest.php index 01d992b..86cd2c9 100644 --- a/app/Http/Requests/DestroyAccountRequest.php +++ b/app/Http/Requests/DestroyAccountRequest.php @@ -24,7 +24,7 @@ class DestroyAccountRequest extends FormRequest public function rules() { return [ - 'current_password_delete' => 'required|string', + 'password' => 'required|string|current_password', ]; } } diff --git a/app/Http/Requests/EditDefaultRecipientRequest.php b/app/Http/Requests/EditDefaultRecipientRequest.php index 1216d64..383d9e1 100644 --- a/app/Http/Requests/EditDefaultRecipientRequest.php +++ b/app/Http/Requests/EditDefaultRecipientRequest.php @@ -29,10 +29,10 @@ class EditDefaultRecipientRequest extends FormRequest 'required', 'email:rfc,dns', 'max:254', - 'confirmed', new RegisterUniqueRecipient(), 'not_in:'.$this->user()->email, ], + 'current' => 'required|string|current_password', ]; } diff --git a/app/Http/Requests/EnableTwoFactorAuthRequest.php b/app/Http/Requests/EnableTwoFactorAuthRequest.php index ddacafc..3de225c 100644 --- a/app/Http/Requests/EnableTwoFactorAuthRequest.php +++ b/app/Http/Requests/EnableTwoFactorAuthRequest.php @@ -25,6 +25,7 @@ class EnableTwoFactorAuthRequest extends FormRequest { return [ 'two_factor_token' => 'required|min:6', + 'current' => 'required|string|current_password', ]; } } diff --git a/app/Http/Requests/StoreAliasRequest.php b/app/Http/Requests/StoreAliasRequest.php index 231558c..beae0c9 100644 --- a/app/Http/Requests/StoreAliasRequest.php +++ b/app/Http/Requests/StoreAliasRequest.php @@ -5,6 +5,7 @@ namespace App\Http\Requests; use App\Rules\ValidAliasLocalPart; use App\Rules\VerifiedRecipientId; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Str; use Illuminate\Validation\Rule; class StoreAliasRequest extends FormRequest @@ -26,6 +27,7 @@ class StoreAliasRequest extends FormRequest { $this->merge([ 'domain' => strtolower($this->domain), + 'local_part_without_extension' => Str::before($this->local_part, '+'), // Remove extension so that we can check alias uniqueness properly ]); } @@ -55,12 +57,11 @@ class StoreAliasRequest extends FormRequest public function withValidator($validator) { - $validator->sometimes('local_part', [ + $validator->sometimes('local_part_without_extension', [ 'required', 'max:50', - Rule::unique('aliases')->where(function ($query) { - return $query->where('local_part', $this->validationData()['local_part']) - ->where('domain', $this->validationData()['domain']); + Rule::unique('aliases', 'local_part')->where(function ($query) { + return $query->where('domain', $this->validationData()['domain']); }), new ValidAliasLocalPart(), ], function () { @@ -73,7 +74,7 @@ class StoreAliasRequest extends FormRequest public function messages() { return [ - 'local_part.unique' => 'That alias already exists.', + 'local_part_without_extension.unique' => 'That alias already exists.', ]; } } diff --git a/app/Http/Requests/StorePersonalAccessTokenRequest.php b/app/Http/Requests/StorePersonalAccessTokenRequest.php index 7df7c10..b742242 100644 --- a/app/Http/Requests/StorePersonalAccessTokenRequest.php +++ b/app/Http/Requests/StorePersonalAccessTokenRequest.php @@ -35,6 +35,10 @@ class StorePersonalAccessTokenRequest extends FormRequest 'max:5', 'in:day,week,month,year', ], + 'password' => [ + 'required', + 'string', + ], ]; } } diff --git a/app/Http/Requests/UpdateFromNameRequest.php b/app/Http/Requests/UpdateAccountFromNameRequest.php similarity index 89% rename from app/Http/Requests/UpdateFromNameRequest.php rename to app/Http/Requests/UpdateAccountFromNameRequest.php index 320fcfb..d9c3782 100644 --- a/app/Http/Requests/UpdateFromNameRequest.php +++ b/app/Http/Requests/UpdateAccountFromNameRequest.php @@ -4,7 +4,7 @@ namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; -class UpdateFromNameRequest extends FormRequest +class UpdateAccountFromNameRequest extends FormRequest { /** * Determine if the user is authorized to make this request. diff --git a/app/Http/Requests/UpdateAliasRequest.php b/app/Http/Requests/UpdateAliasRequest.php index 91d2b4a..928a15b 100644 --- a/app/Http/Requests/UpdateAliasRequest.php +++ b/app/Http/Requests/UpdateAliasRequest.php @@ -25,6 +25,7 @@ class UpdateAliasRequest extends FormRequest { return [ 'description' => 'nullable|max:200', + 'from_name' => 'nullable|string|max:50', ]; } } diff --git a/app/Http/Requests/UpdateDefaultRecipientRequest.php b/app/Http/Requests/UpdateDefaultRecipientRequest.php index 96aec11..5057d44 100644 --- a/app/Http/Requests/UpdateDefaultRecipientRequest.php +++ b/app/Http/Requests/UpdateDefaultRecipientRequest.php @@ -24,7 +24,7 @@ class UpdateDefaultRecipientRequest extends FormRequest public function rules() { return [ - 'default_recipient' => 'required|string', + 'id' => 'required|uuid', ]; } } diff --git a/app/Http/Requests/UpdateDefaultUsernameRequest.php b/app/Http/Requests/UpdateDefaultUsernameRequest.php new file mode 100644 index 0000000..da5b075 --- /dev/null +++ b/app/Http/Requests/UpdateDefaultUsernameRequest.php @@ -0,0 +1,28 @@ + + */ + public function rules(): array + { + return [ + 'id' => 'required|uuid', + ]; + } +} diff --git a/app/Http/Requests/UpdateDisplayFromFormatRequest.php b/app/Http/Requests/UpdateDisplayFromFormatRequest.php new file mode 100644 index 0000000..d5beebf --- /dev/null +++ b/app/Http/Requests/UpdateDisplayFromFormatRequest.php @@ -0,0 +1,34 @@ + + */ + public function rules(): array + { + return [ + 'format' => [ + 'required', + 'integer', + Rule::in(array_column(DisplayFromFormat::cases(), 'value')), + ], + ]; + } +} diff --git a/app/Http/Requests/UpdateDomainRequest.php b/app/Http/Requests/UpdateDomainRequest.php index fbb1035..491e1f8 100644 --- a/app/Http/Requests/UpdateDomainRequest.php +++ b/app/Http/Requests/UpdateDomainRequest.php @@ -25,6 +25,7 @@ class UpdateDomainRequest extends FormRequest { return [ 'description' => 'nullable|max:200', + 'from_name' => 'nullable|string|max:50', ]; } } diff --git a/app/Http/Requests/UpdatePasswordRequest.php b/app/Http/Requests/UpdatePasswordRequest.php index c041de0..1bd3b8c 100644 --- a/app/Http/Requests/UpdatePasswordRequest.php +++ b/app/Http/Requests/UpdatePasswordRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rules\Password; class UpdatePasswordRequest extends FormRequest { @@ -24,8 +25,12 @@ class UpdatePasswordRequest extends FormRequest public function rules() { return [ - 'current' => 'required|string', - 'password' => 'required|confirmed|min:8', + 'current' => 'required|string|current_password', + 'password' => [ + 'required', + 'confirmed', + Password::defaults(), + ], ]; } } diff --git a/app/Http/Requests/UpdateUsernameRequest.php b/app/Http/Requests/UpdateUsernameRequest.php index 42ccc4c..70fbb89 100644 --- a/app/Http/Requests/UpdateUsernameRequest.php +++ b/app/Http/Requests/UpdateUsernameRequest.php @@ -25,6 +25,7 @@ class UpdateUsernameRequest extends FormRequest { return [ 'description' => 'nullable|max:200', + 'from_name' => 'nullable|string|max:50', ]; } } diff --git a/app/Http/Resources/AliasResource.php b/app/Http/Resources/AliasResource.php index 715f1a0..86e47c6 100644 --- a/app/Http/Resources/AliasResource.php +++ b/app/Http/Resources/AliasResource.php @@ -19,6 +19,7 @@ class AliasResource extends JsonResource 'email' => $this->email, 'active' => $this->active, 'description' => $this->description, + 'from_name' => $this->from_name, 'emails_forwarded' => $this->emails_forwarded, 'emails_blocked' => $this->emails_blocked, 'emails_replied' => $this->emails_replied, diff --git a/app/Http/Resources/DomainResource.php b/app/Http/Resources/DomainResource.php index 59be781..30b464c 100644 --- a/app/Http/Resources/DomainResource.php +++ b/app/Http/Resources/DomainResource.php @@ -13,6 +13,7 @@ class DomainResource extends JsonResource 'user_id' => $this->user_id, 'domain' => $this->domain, 'description' => $this->description, + 'from_name' => $this->from_name, 'aliases' => AliasResource::collection($this->whenLoaded('aliases')), 'default_recipient' => new RecipientResource($this->whenLoaded('defaultRecipient')), 'active' => $this->active, diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 69c1bad..6819b7a 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -33,6 +33,7 @@ class UserResource extends JsonResource 'recipient_count' => $this->recipients()->count(), 'active_domain_count' => $this->domains()->where('active', true)->count(), 'active_shared_domain_alias_count' => $this->activeSharedDomainAliases()->count(), + 'active_rule_count' => $this->activeRules()->count(), 'total_emails_forwarded' => (int) $totals->forwarded, 'total_emails_blocked' => (int) $totals->blocked, 'total_emails_replied' => (int) $totals->replied, diff --git a/app/Http/Resources/UsernameResource.php b/app/Http/Resources/UsernameResource.php index b698f40..fa61443 100644 --- a/app/Http/Resources/UsernameResource.php +++ b/app/Http/Resources/UsernameResource.php @@ -13,10 +13,12 @@ class UsernameResource extends JsonResource 'user_id' => $this->user_id, 'username' => $this->username, 'description' => $this->description, + 'from_name' => $this->from_name, 'aliases' => AliasResource::collection($this->whenLoaded('aliases')), 'default_recipient' => new RecipientResource($this->whenLoaded('defaultRecipient')), 'active' => $this->active, 'catch_all' => $this->catch_all, + 'can_login' => $this->can_login, 'created_at' => $this->created_at->toDateTimeString(), 'updated_at' => $this->updated_at->toDateTimeString(), ]; diff --git a/app/Http/Responses/RegisterSuccessResponse.php b/app/Http/Responses/RegisterSuccessResponse.php new file mode 100644 index 0000000..7b36db0 --- /dev/null +++ b/app/Http/Responses/RegisterSuccessResponse.php @@ -0,0 +1,24 @@ +webauthnKeys()->count() > 1) { + return Redirect::intended('/settings/security'); + } + + user()->update([ + 'two_factor_backup_code' => bcrypt($code = Str::random(40)), + ]); + + return Redirect::intended('/settings/security')->with(['backupCode' => $code]); + } +} diff --git a/app/Imports/AliasesImport.php b/app/Imports/AliasesImport.php index 75035aa..8cd7c62 100644 --- a/app/Imports/AliasesImport.php +++ b/app/Imports/AliasesImport.php @@ -30,9 +30,9 @@ use Maatwebsite\Excel\Events\AfterSheet; use Maatwebsite\Excel\Events\ImportFailed; use Ramsey\Uuid\Uuid; -class AliasesImport implements ToModel, WithHeadingRow, WithValidation, WithChunkReading, ShouldQueue, SkipsOnFailure, SkipsEmptyRows, SkipsOnError, WithLimit, WithColumnLimit, WithEvents +class AliasesImport implements ShouldQueue, SkipsEmptyRows, SkipsOnError, SkipsOnFailure, ToModel, WithChunkReading, WithColumnLimit, WithEvents, WithHeadingRow, WithLimit, WithValidation { - use Queueable, Importable, SkipsFailures, SkipsErrors, RemembersRowNumber; + use Importable, Queueable, RemembersRowNumber, SkipsErrors, SkipsFailures; protected $user; diff --git a/app/Jobs/DeleteAccount.php b/app/Jobs/DeleteAccount.php index a26651a..eafb1b8 100644 --- a/app/Jobs/DeleteAccount.php +++ b/app/Jobs/DeleteAccount.php @@ -10,7 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -class DeleteAccount implements ShouldQueue, ShouldBeEncrypted +class DeleteAccount implements ShouldBeEncrypted, ShouldQueue { use Dispatchable; use InteractsWithQueue; diff --git a/app/Mail/ForwardEmail.php b/app/Mail/ForwardEmail.php index e0bdf73..7f8f2eb 100644 --- a/app/Mail/ForwardEmail.php +++ b/app/Mail/ForwardEmail.php @@ -3,6 +3,7 @@ namespace App\Mail; use App\CustomMailDriver\Mime\Part\InlineImagePart; +use App\Enums\DisplayFromFormat; use App\Models\Alias; use App\Models\EmailData; use App\Models\Recipient; @@ -17,11 +18,11 @@ use Illuminate\Support\Facades\URL; use Illuminate\Support\Str; use Symfony\Component\Mime\Email; -class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted +class ForwardEmail extends Mailable implements ShouldBeEncrypted, ShouldQueue { + use CheckUserRules; use Queueable; use SerializesModels; - use CheckUserRules; protected $email; @@ -41,6 +42,8 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted protected $emailSubject; + protected $replacedSubject; + protected $emailText; protected $emailHtml; @@ -55,6 +58,8 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted protected $bannerLocationHtml; + protected $isSpam; + protected $fingerprint; protected $encryptedParts; @@ -85,12 +90,14 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted protected $recipientId; + protected $verpDomain; + /** * Create a new message instance. * * @return void */ - public function __construct(Alias $alias, EmailData $emailData, Recipient $recipient) + public function __construct(Alias $alias, EmailData $emailData, Recipient $recipient, $isSpam = false) { $this->user = $alias->user; $this->alias = $alias; @@ -122,6 +129,7 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->fingerprint = $recipient->should_encrypt && ! $this->isAlreadyEncrypted() ? $recipient->fingerprint : null; $this->bannerLocationText = $this->bannerLocationHtml = $this->isAlreadyEncrypted() ? 'off' : $this->alias->user->banner_location; + $this->isSpam = $isSpam; } /** @@ -140,8 +148,6 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->fromEmail = $this->alias->local_part.'+'.Str::replaceLast('@', '=', $this->replyToAddress).'@'.$this->alias->domain; } - $returnPath = $this->alias->email; - if ($this->alias->isCustomDomain()) { if (! $this->alias->aliasable->isVerifiedForSending()) { if (! isset($replyToEmail)) { @@ -149,15 +155,22 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted } $this->fromEmail = config('mail.from.address'); - $returnPath = config('anonaddy.return_path'); + $this->verpDomain = config('anonaddy.domain'); } } + $displayFrom = base64_decode($this->displayFrom); + + if ($displayFrom === $this->sender) { + $displayFrom = Str::replaceLast('@', ' at ', $this->sender); + } else { + $displayFrom = $this->getUserDisplayFrom($displayFrom); + } + $this->email = $this - ->from($this->fromEmail, base64_decode($this->displayFrom)." '".$this->sender."'") + ->from($this->fromEmail, $displayFrom) ->subject($this->user->email_subject ?? base64_decode($this->emailSubject)) - ->withSymfonyMessage(function (Email $message) use ($returnPath) { - $message->returnPath($returnPath); + ->withSymfonyMessage(function (Email $message) { $message->getHeaders() ->addTextHeader('Feedback-ID', 'F:'.$this->alias->id.':anonaddy'); @@ -238,7 +251,7 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $part->setContentId(base64_decode($attachment['contentId'])); $part->setFileName(base64_decode($attachment['file_name'])); - $message->attachPart($part); + $message->addPart($part); } } @@ -268,6 +281,16 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted ]); } + // No HTML content but isSpam, then force html version + if (! $this->emailHtml && $this->isSpam) { + // Turn off the banner for the plain text version + $this->bannerLocationText = 'off'; + + $this->email->view('emails.forward.html')->with([ + 'html' => base64_decode($this->emailText), + ]); + } + // To prevent invalid view error where no text or html is present... if (! $this->emailHtml && ! $this->emailText) { $this->email->text('emails.forward.text')->with([ @@ -290,17 +313,22 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->email->with([ 'locationText' => $this->bannerLocationText, 'locationHtml' => $this->bannerLocationHtml, + 'isSpam' => $this->isSpam, 'deactivateUrl' => $this->deactivateUrl, 'aliasEmail' => $this->alias->email, 'aliasDomain' => $this->alias->domain, 'aliasDescription' => $this->alias->description, + 'userId' => $this->user->id, + 'aliasId' => $this->alias->id, 'recipientId' => $this->recipientId, + 'emailType' => 'F', 'fingerprint' => $this->fingerprint, 'encryptedParts' => $this->encryptedParts, 'fromEmail' => $this->sender, 'replacedSubject' => $this->replacedSubject, 'shouldBlock' => $this->size === 0, 'needsDkimSignature' => $this->needsDkimSignature(), + 'verpDomain' => $this->verpDomain ?? $this->alias->domain, ]); if (isset($replyToEmail)) { @@ -354,6 +382,21 @@ class ForwardEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted ]); } + private function getUserDisplayFrom($displayFrom) + { + // Check user display_from_format settings and then return correct format + return match ($this->user->display_from_format) { + DisplayFromFormat::DEFAULT => str_replace('@', ' at ', $displayFrom." '".$this->sender."'"), + DisplayFromFormat::BRACKETS => str_replace('@', '(at)', $displayFrom.' - '.$this->sender), + DisplayFromFormat::DOMAIN => str_replace('@', ' at ', $displayFrom.' - '.Str::afterLast($this->sender, '@')), + DisplayFromFormat::NAME => str_replace('@', ' at ', $displayFrom), + DisplayFromFormat::ADDRESS => str_replace('@', ' at ', $this->sender), + DisplayFromFormat::DOMAINONLY => Str::afterLast($this->sender, '@'), + DisplayFromFormat::NONE => null, + default => str_replace('@', ' at ', $displayFrom." '".$this->sender."'"), + }; + } + private function isAlreadyEncrypted() { return $this->encryptedParts || preg_match('/^-----BEGIN PGP MESSAGE-----([A-Za-z0-9+=\/\n]+)-----END PGP MESSAGE-----$/', base64_decode($this->emailText)); diff --git a/app/Mail/ReplyToEmail.php b/app/Mail/ReplyToEmail.php index e2644f4..8a24c8a 100644 --- a/app/Mail/ReplyToEmail.php +++ b/app/Mail/ReplyToEmail.php @@ -16,11 +16,11 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Str; use Symfony\Component\Mime\Email; -class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted +class ReplyToEmail extends Mailable implements ShouldBeEncrypted, ShouldQueue { + use CheckUserRules; use Queueable; use SerializesModels; - use CheckUserRules; protected $email; @@ -52,6 +52,8 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted protected $references; + protected $verpDomain; + /** * Create a new message instance. * @@ -68,7 +70,7 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->emailAttachments = $emailData->attachments; $this->emailInlineAttachments = $emailData->inlineAttachments; $this->encryptedParts = $emailData->encryptedParts ?? null; - $this->displayFrom = $user->from_name ?? null; + $this->displayFrom = $alias->getFromName(); $this->size = $emailData->size; $this->inReplyTo = $emailData->inReplyTo; $this->references = $emailData->references; @@ -81,21 +83,19 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted */ public function build() { - $returnPath = $this->alias->email; $this->fromEmail = $this->alias->email; if ($this->alias->isCustomDomain()) { if (! $this->alias->aliasable->isVerifiedForSending()) { $this->fromEmail = config('mail.from.address'); - $returnPath = config('anonaddy.return_path'); + $this->verpDomain = config('anonaddy.domain'); } } $this->email = $this ->from($this->fromEmail, $this->displayFrom) ->subject(base64_decode($this->emailSubject)) - ->withSymfonyMessage(function (Email $message) use ($returnPath) { - $message->returnPath($returnPath); + ->withSymfonyMessage(function (Email $message) { $message->getHeaders() ->addTextHeader('Feedback-ID', 'R:'.$this->alias->id.':anonaddy'); @@ -124,7 +124,7 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $part->setContentId(base64_decode($attachment['contentId'])); $part->setFileName(base64_decode($attachment['file_name'])); - $message->attachPart($part); + $message->addPart($part); } } }); @@ -159,10 +159,14 @@ class ReplyToEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->checkRules('Replies'); $this->email->with([ + 'userId' => $this->user->id, + 'aliasId' => $this->alias->id, + 'emailType' => 'R', 'shouldBlock' => $this->size === 0, 'encryptedParts' => $this->encryptedParts, 'needsDkimSignature' => $this->needsDkimSignature(), 'aliasDomain' => $this->alias->domain, + 'verpDomain' => $this->verpDomain ?? $this->alias->domain, ]); if ($this->alias->isCustomDomain() && ! $this->needsDkimSignature()) { diff --git a/app/Mail/SendFromEmail.php b/app/Mail/SendFromEmail.php index 35b8cd9..178bbcc 100644 --- a/app/Mail/SendFromEmail.php +++ b/app/Mail/SendFromEmail.php @@ -16,11 +16,11 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Str; use Symfony\Component\Mime\Email; -class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted +class SendFromEmail extends Mailable implements ShouldBeEncrypted, ShouldQueue { + use CheckUserRules; use Queueable; use SerializesModels; - use CheckUserRules; protected $email; @@ -48,6 +48,8 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted protected $size; + protected $verpDomain; + /** * Create a new message instance. * @@ -64,7 +66,7 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->emailAttachments = $emailData->attachments; $this->emailInlineAttachments = $emailData->inlineAttachments; $this->encryptedParts = $emailData->encryptedParts ?? null; - $this->displayFrom = $user->from_name ?? null; + $this->displayFrom = $alias->getFromName(); $this->size = $emailData->size; } @@ -75,21 +77,19 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted */ public function build() { - $returnPath = $this->alias->email; $this->fromEmail = $this->alias->email; if ($this->alias->isCustomDomain()) { if (! $this->alias->aliasable->isVerifiedForSending()) { $this->fromEmail = config('mail.from.address'); - $returnPath = config('anonaddy.return_path'); + $this->verpDomain = config('anonaddy.domain'); } } $this->email = $this ->from($this->fromEmail, $this->displayFrom) ->subject(base64_decode($this->emailSubject)) - ->withSymfonyMessage(function (Email $message) use ($returnPath) { - $message->returnPath($returnPath); + ->withSymfonyMessage(function (Email $message) { $message->getHeaders() ->addTextHeader('Feedback-ID', 'S:'.$this->alias->id.':anonaddy'); @@ -108,7 +108,7 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $part->setContentId(base64_decode($attachment['contentId'])); $part->setFileName(base64_decode($attachment['file_name'])); - $message->attachPart($part); + $message->addPart($part); } } }); @@ -143,10 +143,14 @@ class SendFromEmail extends Mailable implements ShouldQueue, ShouldBeEncrypted $this->checkRules('Sends'); $this->email->with([ + 'userId' => $this->user->id, + 'aliasId' => $this->alias->id, + 'emailType' => 'S', 'shouldBlock' => $this->size === 0, 'encryptedParts' => $this->encryptedParts, 'needsDkimSignature' => $this->needsDkimSignature(), 'aliasDomain' => $this->alias->domain, + 'verpDomain' => $this->verpDomain ?? $this->alias->domain, ]); if ($this->alias->isCustomDomain() && ! $this->needsDkimSignature()) { diff --git a/app/Mail/TokenExpiringSoon.php b/app/Mail/TokenExpiringSoon.php index 0166663..bc47978 100644 --- a/app/Mail/TokenExpiringSoon.php +++ b/app/Mail/TokenExpiringSoon.php @@ -10,7 +10,7 @@ use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; use Symfony\Component\Mime\Email; -class TokenExpiringSoon extends Mailable implements ShouldQueue, ShouldBeEncrypted +class TokenExpiringSoon extends Mailable implements ShouldBeEncrypted, ShouldQueue { use Queueable; use SerializesModels; @@ -38,10 +38,12 @@ class TokenExpiringSoon extends Mailable implements ShouldQueue, ShouldBeEncrypt public function build() { return $this - ->subject('Your AnonAddy API token expires soon') + ->subject('Your addy.io API key expires soon') ->markdown('mail.token_expiring_soon', [ 'user' => $this->user, - 'recipientId' => $this->recipient->id, + 'userId' => $this->user->id, + 'recipientId' => $this->user->default_recipient_id, + 'emailType' => 'TES', 'fingerprint' => $this->recipient->should_encrypt ? $this->recipient->fingerprint : null, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Models/Alias.php b/app/Models/Alias.php index 5d22561..5f4d3eb 100644 --- a/app/Models/Alias.php +++ b/app/Models/Alias.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Traits\HasEncryptedAttributes; use App\Traits\HasUuid; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -12,10 +13,10 @@ use Illuminate\Support\Str; class Alias extends Model { - use SoftDeletes; - use HasUuid; use HasEncryptedAttributes; use HasFactory; + use HasUuid; + use SoftDeletes; public $incrementing = false; @@ -23,6 +24,7 @@ class Alias extends Model protected $encrypted = [ 'description', + 'from_name', ]; protected $fillable = [ @@ -30,6 +32,7 @@ class Alias extends Model 'user_id', 'active', 'description', + 'from_name', 'email', 'local_part', 'extension', @@ -123,6 +126,14 @@ class Alias extends Model return $this->hasMany(FailedDelivery::class); } + /** + * Get all of the aliases' outbound messages. + */ + public function outboundMessages() + { + return $this->hasMany(OutboundMessage::class); + } + /** * Get all of the verified recipients for the email alias. */ @@ -154,6 +165,24 @@ class Alias extends Model return $verifiedRecipients; } + public function scopeUsesRecipientWithId($query, $id, $isDefault = false) + { + return $query->where(function (Builder $q) use ($id) { + return $q->whereHas('recipients', function (Builder $query) use ($id) { + $query->where('recipients.id', $id); + })->orWhere(function (Builder $q) use ($id) { + return $q->whereHasMorph('aliasable', ['App\Models\Domain', 'App\Models\Username'], function (Builder $query) use ($id) { + $query->where('default_recipient_id', $id); + })->doesntHave('recipients'); + }); + }); + } + + public function scopeBelongsToAliasable($query, $type, $id) + { + return $query->where('aliasable_type', $type)->where('aliasable_id', $id); + } + /** * Deactivate the alias. */ @@ -180,6 +209,22 @@ class Alias extends Model return in_array($this->domain, config('anonaddy.all_domains')); } + public function getFromName() + { + // Check alias from name + if ($aliasFromName = $this->from_name) { + return $aliasFromName; + } + + // Check username / custom domain from name + if ($aliasableFromName = $this->aliasable?->from_name) { + return $aliasableFromName; + } + + // Check user settings global from name + return $this->user->from_name ?? null; + } + public function isCustomDomain() { return $this->aliasable_type === 'App\Models\Domain'; diff --git a/app/Models/Domain.php b/app/Models/Domain.php index 39f203e..037fb95 100644 --- a/app/Models/Domain.php +++ b/app/Models/Domain.php @@ -14,9 +14,9 @@ use Illuminate\Support\Facades\Log; class Domain extends Model { - use HasUuid; use HasEncryptedAttributes; use HasFactory; + use HasUuid; public $incrementing = false; @@ -24,11 +24,13 @@ class Domain extends Model protected $encrypted = [ 'description', + 'from_name', ]; protected $fillable = [ 'domain', 'description', + 'from_name', 'active', 'catch_all', ]; diff --git a/app/Models/EmailData.php b/app/Models/EmailData.php index 8768844..7e44ad2 100644 --- a/app/Models/EmailData.php +++ b/app/Models/EmailData.php @@ -64,7 +64,7 @@ class EmailData $this->text = base64_encode(stream_get_contents($attachment->getStream())); } else { if (! str_contains($contentType, '/')) { - if (null === self::$mimeTypes) { + if (self::$mimeTypes === null) { self::$mimeTypes = new MimeTypes(); } $contentType = self::$mimeTypes->getMimeTypes($contentType)[0] ?? 'application/octet-stream'; diff --git a/app/Models/FailedDelivery.php b/app/Models/FailedDelivery.php index 14127f9..53782bd 100644 --- a/app/Models/FailedDelivery.php +++ b/app/Models/FailedDelivery.php @@ -5,15 +5,16 @@ namespace App\Models; use App\Traits\HasEncryptedAttributes; use App\Traits\HasUuid; use DateTimeInterface; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Storage; class FailedDelivery extends Model { - use HasUuid; use HasEncryptedAttributes; use HasFactory; + use HasUuid; public $incrementing = false; @@ -21,6 +22,7 @@ class FailedDelivery extends Model protected $encrypted = [ 'sender', + 'destination', ]; protected $fillable = [ @@ -32,6 +34,7 @@ class FailedDelivery extends Model 'bounce_type', 'remote_mta', 'sender', + 'destination', 'email_type', 'status', 'code', @@ -70,6 +73,40 @@ class FailedDelivery extends Model return $date->format('Y-m-d H:i:s'); } + /** + * Get the human readable email type. + * + * @param string $value + * @return string + */ + protected function emailType(): Attribute + { + return Attribute::make( + get: fn (?string $value) => match ($value) { + 'F' => 'Forward', + 'R' => 'Reply', + 'S' => 'Send', + 'RP' => 'Reset Password', + 'FDN' => 'Failed Delivery', + 'DMI' => 'Domain MX Invalid', + 'DRU' => 'Default Recipient Updated', + 'FLA' => 'Failed Login Attempt', + 'TES' => 'Token Expiring Soon', + 'UR' => 'Username Reminder', + 'VR' => 'Verify Recipient', + 'VU' => 'Verify User', + 'DRSA' => 'Disallowed Reply/Send Attempt', + 'DUS' => 'Domain Unverified For Sending', + 'GKE' => 'PGP Key Expired', + 'NBL' => 'Near Bandwidth Limit', + 'RSL' => 'Reached Reply/Send Limit', + 'SRSA' => 'Spam Reply/Send Attempt', + 'AIF' => 'Aliases Import Finished', + default => 'Forward', + }, + ); + } + /** * Get the user for the failed delivery. */ diff --git a/app/Models/OutboundMessage.php b/app/Models/OutboundMessage.php new file mode 100644 index 0000000..2b7ed63 --- /dev/null +++ b/app/Models/OutboundMessage.php @@ -0,0 +1,63 @@ + 'string', + 'user_id' => 'string', + 'alias_id' => 'string', + 'recipient_id' => 'string', + 'bounced' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Get the user for the outbound message. + */ + public function user() + { + return $this->belongsTo(User::class); + } + + /** + * Get the recipient for the outbound message. + */ + public function recipient() + { + return $this->belongsTo(Recipient::class); + } + + /** + * Get the alias for the outbound message. + */ + public function alias() + { + return $this->belongsTo(Alias::class); + } + + public function markAsBounced() + { + $this->update(['bounced' => true]); + } +} diff --git a/app/Models/PostfixQueueId.php b/app/Models/PostfixQueueId.php deleted file mode 100644 index 5c1ea33..0000000 --- a/app/Models/PostfixQueueId.php +++ /dev/null @@ -1,29 +0,0 @@ - 'string', - 'queue_id' => 'string', - ]; -} diff --git a/app/Models/Recipient.php b/app/Models/Recipient.php index 29e73bf..bfb7c3d 100644 --- a/app/Models/Recipient.php +++ b/app/Models/Recipient.php @@ -6,16 +6,17 @@ use App\Notifications\CustomVerifyEmail; use App\Notifications\UsernameReminder; use App\Traits\HasEncryptedAttributes; use App\Traits\HasUuid; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; class Recipient extends Model { - use Notifiable; - use HasUuid; use HasEncryptedAttributes; use HasFactory; + use HasUuid; + use Notifiable; public $incrementing = false; @@ -34,6 +35,7 @@ class Recipient extends Model 'inline_encryption', 'protected_headers', 'fingerprint', + 'pending', 'email_verified_at', ]; @@ -44,6 +46,7 @@ class Recipient extends Model 'should_encrypt' => 'boolean', 'inline_encryption' => 'boolean', 'protected_headers' => 'boolean', + 'pending' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'email_verified_at' => 'datetime', @@ -62,6 +65,33 @@ class Recipient extends Model }); } + /** + * The "booted" method of the model. + */ + protected static function booted(): void + { + // Global scope on Recipient model to not return any pending new email entries by default + static::addGlobalScope('notPending', function (Builder $builder) { + $builder->where('pending', false); + }); + } + + /** + * Scope a query to include pending new email recipients. + */ + public function scopeWithPending(Builder $query): void + { + $query->withoutGlobalScope('notPending'); + } + + /** + * Scope a query to get only pending email recipients. + */ + public function scopePending(Builder $query): void + { + $query->withoutGlobalScope('notPending')->where('pending', true); + } + /** * Query scope to return verified or unverified recipients. */ @@ -98,6 +128,14 @@ class Recipient extends Model return $this->hasMany(FailedDelivery::class); } + /** + * Get all of the recipient's outbound messages. + */ + public function outboundMessages() + { + return $this->hasMany(OutboundMessage::class); + } + /** * Get all of the user's custom domains. */ @@ -114,6 +152,30 @@ class Recipient extends Model return $this->hasMany(Username::class, 'default_recipient_id', 'id'); } + public function domainAliasesUsingAsDefault() + { + return $this->hasManyThrough( + Alias::class, + Domain::class, + 'default_recipient_id', // Foreign key on the domain table... + 'aliasable_id', // Foreign key on the alias table... + 'id', // Local key on the recipient table... + 'id' // Local key on the domain table... + ); + } + + public function usernameAliasesUsingAsDefault() + { + return $this->hasManyThrough( + Alias::class, + Username::class, + 'default_recipient_id', // Foreign key on the username table... + 'aliasable_id', // Foreign key on the alias table... + 'id', // Local key on the recipient table... + 'id' // Local key on the username table... + ); + } + /** * Determine if the recipient has a verified email address. * diff --git a/app/Models/Rule.php b/app/Models/Rule.php index f0d3b0a..3ed2d3c 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -8,8 +8,8 @@ use Illuminate\Database\Eloquent\Model; class Rule extends Model { - use HasUuid; use HasFactory; + use HasUuid; public $incrementing = false; diff --git a/app/Models/User.php b/app/Models/User.php index 341ecf9..955fb32 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Enums\DisplayFromFormat; +use App\Notifications\CustomResetPassword; use App\Notifications\CustomVerifyEmail; use App\Traits\HasEncryptedAttributes; use App\Traits\HasUuid; @@ -17,11 +19,11 @@ use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable implements MustVerifyEmail { - use Notifiable; - use HasUuid; - use HasEncryptedAttributes; use HasApiTokens; + use HasEncryptedAttributes; use HasFactory; + use HasUuid; + use Notifiable; public $incrementing = false; @@ -37,8 +39,12 @@ class User extends Authenticatable implements MustVerifyEmail 'from_name', 'email_subject', 'banner_location', + 'display_from_format', 'catch_all', 'bandwidth', + 'reject_until', + 'defer_until', + 'defer_new_aliases_until', 'default_alias_domain', 'default_alias_format', 'use_reply_to', @@ -85,6 +91,10 @@ class User extends Authenticatable implements MustVerifyEmail 'created_at' => 'datetime', 'updated_at' => 'datetime', 'email_verified_at' => 'datetime', + 'reject_until' => 'datetime', + 'defer_until' => 'datetime', + 'defer_new_aliases_until' => 'datetime', + 'display_from_format' => DisplayFromFormat::class, ]; /** @@ -153,6 +163,26 @@ class User extends Authenticatable implements MustVerifyEmail ); } + /** + * Get the user's default alias domain. + */ + protected function defaultAliasDomain(): Attribute + { + return Attribute::make( + get: fn (?string $value) => $value ?? 'anonaddy.me', + ); + } + + /** + * Get the user's default alias format. + */ + protected function defaultAliasFormat(): Attribute + { + return Attribute::make( + get: fn (?string $value) => $value ?? 'random_characters', + ); + } + /** * Get the user's default username. */ @@ -177,6 +207,14 @@ class User extends Authenticatable implements MustVerifyEmail return $this->hasMany(Alias::class); } + /** + * Get all of the user's email aliases including trashed. + */ + public function allAliases() + { + return $this->hasMany(Alias::class)->withTrashed(); + } + /** * Get all of the user's recipients. */ @@ -185,6 +223,14 @@ class User extends Authenticatable implements MustVerifyEmail return $this->hasMany(Recipient::class); } + /** + * Get all of the user's pending recipients. + */ + public function pendingRecipients() + { + return $this->recipients()->pending(); + } + /** * Get all of the user's custom domains. */ @@ -209,6 +255,14 @@ class User extends Authenticatable implements MustVerifyEmail return $this->hasMany(FailedDelivery::class); } + /** + * Get all of the user's outbound messages. + */ + public function outboundMessages() + { + return $this->hasMany(OutboundMessage::class); + } + /** * Get all of the user's active rules. */ @@ -321,6 +375,30 @@ class User extends Authenticatable implements MustVerifyEmail }); } + /** + * Get the count for the user's aliases that are using the default recipient + */ + public function aliasesUsingDefaultCount() + { + return $this->aliases()->select('id')->where(function (Builder $q) { + return $q->whereHas('recipients', function (Builder $query) { + $query->where('recipients.id', $this->default_recipient_id); + }) + ->orWhereDoesntHave('recipients')->where(function (Builder $q) { + return $q->whereDoesntHaveMorph( + 'aliasable', + ['App\Models\Domain', 'App\Models\Username'], + function (Builder $query) { + $query->whereNotNull('default_recipient_id'); + } + )->orWhereNull('aliasable_id') + ->orWhereHasMorph('aliasable', ['App\Models\Domain', 'App\Models\Username'], function (Builder $query) { + $query->where('default_recipient_id', $this->default_recipient_id); + }); + }); + })->count(); + } + /** * Send the email verification notification. * @@ -331,6 +409,17 @@ class User extends Authenticatable implements MustVerifyEmail $this->notify(new CustomVerifyEmail()); } + /** + * Send the password reset notification. + * + * @param string $token + * @return void + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new CustomResetPassword($token)); + } + public function hasVerifiedDefaultRecipient() { return ! is_null($this->defaultRecipient->email_verified_at); @@ -390,6 +479,11 @@ class User extends Authenticatable implements MustVerifyEmail return false; }, function () { + $now = now(); + if ($this->defer_new_aliases_until < $now) { + $this->update(['defer_new_aliases_until' => $now->addHour()->toDateTimeString()]); + } + return true; } ); @@ -510,4 +604,9 @@ class User extends Authenticatable implements MustVerifyEmail ->reverse() ->values(); } + + public function sharedDomainOptions() + { + return config('anonaddy.all_domains'); + } } diff --git a/app/Models/Username.php b/app/Models/Username.php index fd4d425..b7772ba 100644 --- a/app/Models/Username.php +++ b/app/Models/Username.php @@ -9,9 +9,9 @@ use Illuminate\Database\Eloquent\Model; class Username extends Model { - use HasUuid; use HasEncryptedAttributes; use HasFactory; + use HasUuid; public $incrementing = false; @@ -19,14 +19,17 @@ class Username extends Model protected $encrypted = [ 'description', + 'from_name', ]; protected $fillable = [ 'user_id', 'username', 'description', + 'from_name', 'active', 'catch_all', + 'can_login', ]; protected $casts = [ @@ -34,6 +37,7 @@ class Username extends Model 'user_id' => 'string', 'active' => 'boolean', 'catch_all' => 'boolean', + 'can_login' => 'boolean', 'default_recipient_id' => 'string', 'created_at' => 'datetime', 'updated_at' => 'datetime', @@ -121,4 +125,20 @@ class Username extends Model { $this->update(['catch_all' => true]); } + + /** + * Disallow login for the username. + */ + public function disallowLogin() + { + $this->update(['can_login' => false]); + } + + /** + * Allow login for the username. + */ + public function allowLogin() + { + $this->update(['can_login' => true]); + } } diff --git a/app/Notifications/AliasesImportedNotification.php b/app/Notifications/AliasesImportedNotification.php index 1ebdeb7..9a83663 100644 --- a/app/Notifications/AliasesImportedNotification.php +++ b/app/Notifications/AliasesImportedNotification.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class AliasesImportedNotification extends Notification implements ShouldQueue, ShouldBeEncrypted +class AliasesImportedNotification extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; diff --git a/app/Notifications/CustomResetPassword.php b/app/Notifications/CustomResetPassword.php new file mode 100644 index 0000000..ecc8ea1 --- /dev/null +++ b/app/Notifications/CustomResetPassword.php @@ -0,0 +1,140 @@ +token = $token; + } + + /** + * Get the notification's channels. + * + * @param mixed $notifiable + * @return array|string + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Build the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + if (static::$toMailCallback) { + return call_user_func(static::$toMailCallback, $notifiable, $this->token); + } + + return $this->buildMailMessage($this->resetUrl($notifiable), $notifiable); + } + + /** + * Get the reset password notification mail message for the given URL. + * + * @param string $url + * @return \Illuminate\Notifications\Messages\MailMessage + */ + protected function buildMailMessage($url, $notifiable) + { + $recipient = $notifiable->defaultRecipient; + $fingerprint = $recipient->should_encrypt ? $recipient->fingerprint : null; + + return (new MailMessage) + ->subject(Lang::get('Reset Password Notification')) + ->markdown('mail.reset_password', [ + 'resetUrl' => $url, + 'userId' => $notifiable->id, + 'recipientId' => $notifiable->default_recipient_id, + 'emailType' => 'RP', + 'fingerprint' => $fingerprint, + ]) + ->withSymfonyMessage(function (Email $message) { + $message->getHeaders() + ->addTextHeader('Feedback-ID', 'RP:anonaddy'); + }); + } + + /** + * Get the reset URL for the given notifiable. + * + * @param mixed $notifiable + * @return string + */ + protected function resetUrl($notifiable) + { + if (static::$createUrlCallback) { + return call_user_func(static::$createUrlCallback, $notifiable, $this->token); + } + + return url(route('password.reset', [ + 'token' => $this->token, + 'email' => $notifiable->getEmailForPasswordReset(), + ], false)); + } + + /** + * Set a callback that should be used when creating the reset password button URL. + * + * @param \Closure(mixed, string): string $callback + * @return void + */ + public static function createUrlUsing($callback) + { + static::$createUrlCallback = $callback; + } + + /** + * Set a callback that should be used when building the notification mail message. + * + * @param \Closure(mixed, string): \Illuminate\Notifications\Messages\MailMessage $callback + * @return void + */ + public static function toMailUsing($callback) + { + static::$toMailCallback = $callback; + } +} diff --git a/app/Notifications/CustomVerifyEmail.php b/app/Notifications/CustomVerifyEmail.php index 656d016..19fe742 100644 --- a/app/Notifications/CustomVerifyEmail.php +++ b/app/Notifications/CustomVerifyEmail.php @@ -15,7 +15,7 @@ use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\URL; use Symfony\Component\Mime\Email; -class CustomVerifyEmail extends VerifyEmail implements ShouldQueue, ShouldBeEncrypted +class CustomVerifyEmail extends VerifyEmail implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -35,12 +35,15 @@ class CustomVerifyEmail extends VerifyEmail implements ShouldQueue, ShouldBeEncr $feedbackId = $notifiable instanceof User ? 'VU:anonaddy' : 'VR:anonaddy'; $recipientId = $notifiable instanceof User ? $notifiable->default_recipient_id : $notifiable->id; + $userId = $notifiable instanceof User ? $notifiable->id : $notifiable->user_id; return (new MailMessage()) ->subject(Lang::get('Verify Email Address')) ->markdown('mail.verify_email', [ 'verificationUrl' => $verificationUrl, + 'userId' => $userId, 'recipientId' => $recipientId, + 'emailType' => $feedbackId, ]) ->withSymfonyMessage(function (Email $message) use ($feedbackId) { $message->getHeaders() diff --git a/app/Notifications/DefaultRecipientUpdated.php b/app/Notifications/DefaultRecipientUpdated.php index 80a0308..c707f5e 100644 --- a/app/Notifications/DefaultRecipientUpdated.php +++ b/app/Notifications/DefaultRecipientUpdated.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class DefaultRecipientUpdated extends Notification implements ShouldQueue, ShouldBeEncrypted +class DefaultRecipientUpdated extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -49,7 +49,9 @@ class DefaultRecipientUpdated extends Notification implements ShouldQueue, Shoul ->markdown('mail.default_recipient_updated', [ 'defaultRecipient' => $notifiable->email, 'newDefaultRecipient' => $this->newDefaultRecipient, + 'userId' => $notifiable->user_id, 'recipientId' => $notifiable->id, + 'emailType' => 'DRU', 'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/DisallowedReplySendAttempt.php b/app/Notifications/DisallowedReplySendAttempt.php index dc34358..b4b253e 100644 --- a/app/Notifications/DisallowedReplySendAttempt.php +++ b/app/Notifications/DisallowedReplySendAttempt.php @@ -10,7 +10,7 @@ use Illuminate\Notifications\Notification; use Illuminate\Support\Str; use Symfony\Component\Mime\Email; -class DisallowedReplySendAttempt extends Notification implements ShouldQueue, ShouldBeEncrypted +class DisallowedReplySendAttempt extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -63,7 +63,9 @@ class DisallowedReplySendAttempt extends Notification implements ShouldQueue, Sh 'recipient' => $this->recipient, 'destination' => $this->destination, 'authenticationResults' => $this->authenticationResults, + 'userId' => $notifiable->user_id, 'recipientId' => $notifiable->id, + 'emailType' => 'DRSA', 'fingerprint' => $fingerprint, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/DomainMxRecordsInvalid.php b/app/Notifications/DomainMxRecordsInvalid.php index f6dee17..ab55656 100644 --- a/app/Notifications/DomainMxRecordsInvalid.php +++ b/app/Notifications/DomainMxRecordsInvalid.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class DomainMxRecordsInvalid extends Notification implements ShouldQueue, ShouldBeEncrypted +class DomainMxRecordsInvalid extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -48,10 +48,12 @@ class DomainMxRecordsInvalid extends Notification implements ShouldQueue, Should $fingerprint = $recipient->should_encrypt ? $recipient->fingerprint : null; return (new MailMessage()) - ->subject("Your domain's MX records no longer point to AnonAddy") + ->subject("Your domain's MX records no longer point to addy.io") ->markdown('mail.domain_mx_records_invalid', [ 'domain' => $this->domain, - 'recipientId' => $recipient->_id, + 'userId' => $notifiable->id, + 'recipientId' => $recipient->id, + 'emailType' => 'DMI', 'fingerprint' => $fingerprint, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/DomainUnverifiedForSending.php b/app/Notifications/DomainUnverifiedForSending.php index 1f5b9bc..f3c29dc 100644 --- a/app/Notifications/DomainUnverifiedForSending.php +++ b/app/Notifications/DomainUnverifiedForSending.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class DomainUnverifiedForSending extends Notification implements ShouldQueue, ShouldBeEncrypted +class DomainUnverifiedForSending extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -51,11 +51,13 @@ class DomainUnverifiedForSending extends Notification implements ShouldQueue, Sh $fingerprint = $recipient->should_encrypt ? $recipient->fingerprint : null; return (new MailMessage()) - ->subject('Your domain has been unverified for sending on AnonAddy') + ->subject('Your domain has been unverified for sending on addy.io') ->markdown('mail.domain_unverified_for_sending', [ 'domain' => $this->domain, 'reason' => $this->reason, + 'userId' => $notifiable->id, 'recipientId' => $recipient->id, + 'emailType' => 'DUS', 'fingerprint' => $fingerprint, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/FailedDeliveryNotification.php b/app/Notifications/FailedDeliveryNotification.php index c3a284d..bbcc69b 100644 --- a/app/Notifications/FailedDeliveryNotification.php +++ b/app/Notifications/FailedDeliveryNotification.php @@ -8,7 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; -class FailedDeliveryNotification extends Notification implements ShouldQueue, ShouldBeEncrypted +class FailedDeliveryNotification extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -59,7 +59,7 @@ class FailedDeliveryNotification extends Notification implements ShouldQueue, Sh public function toMail($notifiable) { return (new MailMessage()) - ->subject('New failed delivery on AnonAddy') + ->subject('New failed delivery on addy.io') ->markdown('mail.failed_delivery_notification', [ 'aliasEmail' => $this->aliasEmail, 'recipientEmail' => $this->recipientEmail ?? $notifiable->email, @@ -67,7 +67,9 @@ class FailedDeliveryNotification extends Notification implements ShouldQueue, Sh 'originalSubject' => $this->originalSubject, 'isStored' => $this->isStored, 'storeFailedDeliveries' => $this->storeFailedDeliveries, + 'userId' => $notifiable->user_id, 'recipientId' => $notifiable->id, + 'emailType' => 'FDN', 'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null, ]) ->withSymfonyMessage(function ($message) { diff --git a/app/Notifications/GpgKeyExpired.php b/app/Notifications/GpgKeyExpired.php index 6c803db..3bf0964 100644 --- a/app/Notifications/GpgKeyExpired.php +++ b/app/Notifications/GpgKeyExpired.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class GpgKeyExpired extends Notification implements ShouldQueue, ShouldBeEncrypted +class GpgKeyExpired extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -33,9 +33,12 @@ class GpgKeyExpired extends Notification implements ShouldQueue, ShouldBeEncrypt public function toMail($notifiable) { return (new MailMessage()) - ->subject('Your GPG key has expired on AnonAddy') + ->subject('Your GPG key has expired on addy.io') ->markdown('mail.gpg_key_expired', [ 'recipient' => $notifiable, + 'userId' => $notifiable->user_id, + 'recipientId' => $notifiable->id, + 'emailType' => 'GKE', ]) ->withSymfonyMessage(function (Email $message) { $message->getHeaders() diff --git a/app/Notifications/IncorrectOtpNotification.php b/app/Notifications/IncorrectOtpNotification.php index 3e6925e..755c3c6 100644 --- a/app/Notifications/IncorrectOtpNotification.php +++ b/app/Notifications/IncorrectOtpNotification.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class IncorrectOtpNotification extends Notification implements ShouldQueue, ShouldBeEncrypted +class IncorrectOtpNotification extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -38,7 +38,9 @@ class IncorrectOtpNotification extends Notification implements ShouldQueue, Shou return (new MailMessage()) ->subject('Failed Two Factor Authentication Login Attempt') ->markdown('mail.failed_login_attempt', [ + 'userId' => $notifiable->id, 'recipientId' => $recipient->id, + 'emailType' => 'FLA', 'hasVerifiedEmail' => $recipient->hasVerifiedEmail(), 'fingerprint' => $fingerprint, 'username' => $notifiable->username, diff --git a/app/Notifications/NearBandwidthLimit.php b/app/Notifications/NearBandwidthLimit.php index c1b64fd..ed4d52a 100644 --- a/app/Notifications/NearBandwidthLimit.php +++ b/app/Notifications/NearBandwidthLimit.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class NearBandwidthLimit extends Notification implements ShouldQueue, ShouldBeEncrypted +class NearBandwidthLimit extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -57,7 +57,9 @@ class NearBandwidthLimit extends Notification implements ShouldQueue, ShouldBeEn 'bandwidthLimit' => $notifiable->getBandwidthLimitMb(), 'month' => $this->month, 'reset' => $this->reset, + 'userId' => $notifiable->id, 'recipientId' => $recipient->id, + 'emailType' => 'NBL', 'fingerprint' => $fingerprint, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/SpamReplySendAttempt.php b/app/Notifications/SpamReplySendAttempt.php index 143beed..8e76a5e 100644 --- a/app/Notifications/SpamReplySendAttempt.php +++ b/app/Notifications/SpamReplySendAttempt.php @@ -10,7 +10,7 @@ use Illuminate\Notifications\Notification; use Illuminate\Support\Str; use Symfony\Component\Mime\Email; -class SpamReplySendAttempt extends Notification implements ShouldQueue, ShouldBeEncrypted +class SpamReplySendAttempt extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -61,7 +61,9 @@ class SpamReplySendAttempt extends Notification implements ShouldQueue, ShouldBe 'recipient' => $this->recipient, 'destination' => $this->destination, 'authenticationResults' => $this->authenticationResults, + 'userId' => $notifiable->user_id, 'recipientId' => $notifiable->id, + 'emailType' => 'SRSA', 'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Notifications/UsernameReminder.php b/app/Notifications/UsernameReminder.php index a60b0f8..943b5c4 100644 --- a/app/Notifications/UsernameReminder.php +++ b/app/Notifications/UsernameReminder.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Symfony\Component\Mime\Email; -class UsernameReminder extends Notification implements ShouldQueue, ShouldBeEncrypted +class UsernameReminder extends Notification implements ShouldBeEncrypted, ShouldQueue { use Queueable; @@ -33,10 +33,12 @@ class UsernameReminder extends Notification implements ShouldQueue, ShouldBeEncr public function toMail($notifiable) { return (new MailMessage()) - ->subject('AnonAddy Username Reminder') + ->subject('addy.io Username Reminder') ->markdown('mail.username_reminder', [ 'username' => $notifiable->user->username, + 'userId' => $notifiable->user_id, 'recipientId' => $notifiable->id, + 'emailType' => 'UR', 'fingerprint' => $notifiable->should_encrypt ? $notifiable->fingerprint : null, ]) ->withSymfonyMessage(function (Email $message) { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 182895d..7aa57be 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Http\Responses\RegisterSuccessResponse; use App\Models\PersonalAccessToken; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -9,7 +10,9 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; +use Illuminate\Validation\Rules\Password; use Laravel\Sanctum\Sanctum; +use LaravelWebauthn\Services\Webauthn; class AppServiceProvider extends ServiceProvider { @@ -19,6 +22,7 @@ class AppServiceProvider extends ServiceProvider public function register(): void { Sanctum::ignoreMigrations(); + Webauthn::registerSuccessResponseUsing(RegisterSuccessResponse::class); } /** @@ -32,6 +36,14 @@ class AppServiceProvider extends ServiceProvider Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + Password::defaults(function () { + $rule = Password::min(8); + + return $this->app->isProduction() + ? $rule->letters()->uncompromised() + : $rule; + }); + Builder::macro('jsonPaginate', function (int $maxResults = null, int $defaultSize = null) { $maxResults = $maxResults ?? 100; $defaultSize = $defaultSize ?? 100; @@ -49,6 +61,27 @@ class AppServiceProvider extends ServiceProvider return $paginator; }); + Collection::macro('paginate', function (int $defaultSize = null, int $maxResults = null, $page = null) { + $maxResults = $maxResults ?? 100; + $defaultSize = $defaultSize ?? 25; + $size = (int) is_null(request()->input('pageSize')) ? $defaultSize : request()->input('pageSize'); + + $size = $size > $maxResults ? $maxResults : $size; + $page = (int) is_null(request()->input('page')) ? 1 : request()->input('page'); + + return new LengthAwarePaginator( + $this->forPage($page, $size)->values(), + $this->count(), + $size, + $page, + [ + 'path' => LengthAwarePaginator::resolveCurrentPath(), + 'query' => Arr::except(LengthAwarePaginator::resolveQueryString(), 'page'), + 'pageName' => 'page', + ] + ); + }); + Collection::macro('jsonPaginate', function (int $maxResults = null, int $defaultSize = null, $page = null) { $maxResults = $maxResults ?? 100; $defaultSize = $defaultSize ?? 100; diff --git a/app/Services/CredentialRepository.php b/app/Services/CredentialRepository.php new file mode 100644 index 0000000..0b15e37 --- /dev/null +++ b/app/Services/CredentialRepository.php @@ -0,0 +1,53 @@ + + */ + protected static function getAllRegisteredKeys(int|string $userId, bool $onlyEnabled = false): Collection + { + // Added override with enabled true + return (Webauthn::model())::where('user_id', $userId) + ->when($onlyEnabled, function ($query) { + $query->where('enabled', true); + }) + ->get() + ->map + ->publicKeyCredentialSource; + } + + /** + * List of registered PublicKeyCredentialDescriptor associated to the user. + * + * @return array + */ + public static function getRegisteredKeys(User $user): array + { + [$childClass, $calledBy] = debug_backtrace(false, 2); + + // If we are registering a new key then we want to get all the user's keys including disabled ones + if ($calledBy['function'] === 'getExcludedCredentials') { + return static::getAllRegisteredKeys($user->getAuthIdentifier()) + ->map + ->getPublicKeyCredentialDescriptor() + ->toArray(); + } + + // Else just get the enabled keys for getAllowedCredentials when authenticating + return static::getAllRegisteredKeys($user->getAuthIdentifier(), true) + ->map + ->getPublicKeyCredentialDescriptor() + ->toArray(); + } +} diff --git a/composer.json b/composer.json index 1b76c5e..df3002a 100644 --- a/composer.json +++ b/composer.json @@ -7,12 +7,13 @@ ], "license": "AGPL-3.0-or-later", "require": { - "php": "^8.1", - "asbiin/laravel-webauthn": "^3.0.0", + "php": "^8.2", + "asbiin/laravel-webauthn": "^4.0.0", "bacon/bacon-qr-code": "^2.0", "chillerlan/php-qrcode": "^4.3", "doctrine/dbal": "^3.0", "guzzlehttp/guzzle": "^7.2", + "inertiajs/inertia-laravel": "^0.6.9", "laravel/framework": "^10.0", "laravel/sanctum": "^3.2", "laravel/tinker": "^2.7", @@ -21,7 +22,8 @@ "mews/captcha": "^3.0.0", "php-mime-mail-parser/php-mime-mail-parser": "^8.0", "pragmarx/google2fa-laravel": "^2.0.0", - "ramsey/uuid": "^4.0" + "ramsey/uuid": "^4.0", + "tightenco/ziggy": "^1.6" }, "require-dev": { "fakerphp/faker": "^1.9.1", @@ -42,11 +44,15 @@ "dont-discover": [] } }, - "exclude-from-classmap": ["vendor/egulias/email-validator/src/Validation/MessageIDValidation.php"], "autoload": { + "exclude-from-classmap": [ + "vendor/egulias/email-validator/src/Validation/MessageIDValidation.php", + "vendor/asbiin/laravel-webauthn/src/Services/Webauthn/CredentialRepository.php" + ], "psr-4": { "App\\": "app/", "Egulias\\EmailValidator\\Validation\\": "app/CustomMailDriver/Validation/", + "LaravelWebauthn\\Services\\Webauthn\\": "app/Services/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } diff --git a/composer.lock b/composer.lock index 35acc39..91323a7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,43 +4,46 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9157f77755c349a041b1255e99adc1aa", + "content-hash": "4baf108431cf17f4762c521fd752c926", "packages": [ { "name": "asbiin/laravel-webauthn", - "version": "3.3.1", + "version": "4.1.2", "source": { "type": "git", "url": "https://github.com/asbiin/laravel-webauthn.git", - "reference": "fa246351081aac52c7cb48fbabf42510c3475ab9" + "reference": "1d1f417e69579dac06c5b0359b782c64b650ce0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/asbiin/laravel-webauthn/zipball/fa246351081aac52c7cb48fbabf42510c3475ab9", - "reference": "fa246351081aac52c7cb48fbabf42510c3475ab9", + "url": "https://api.github.com/repos/asbiin/laravel-webauthn/zipball/1d1f417e69579dac06c5b0359b782c64b650ce0d", + "reference": "1d1f417e69579dac06c5b0359b782c64b650ce0d", "shasum": "" }, "require": { "illuminate/support": "^9.0 || ^10.0", "php": ">=8.1", "psr/http-factory-implementation": "1.0", - "thecodingmachine/safe": "^2.0", "web-auth/cose-lib": "^4.0", - "web-auth/webauthn-lib": "^4.0", + "web-auth/webauthn-lib": "^4.7.1", "web-token/jwt-signature": "^3.0" }, + "conflict": { + "web-auth/webauthn-lib": "4.7.0" + }, "require-dev": { "ext-sqlite3": "*", "guzzlehttp/psr7": "^2.1", "laravel/legacy-factories": "^1.0", + "laravel/pint": "^1.13", "nunomaduro/larastan": "^2.0", "ocramius/package-versions": "^2.0", "orchestra/testbench": "^7.0 || ^8.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "thecodingmachine/phpstan-safe-rule": "^1.0" + "phpunit/phpunit": "^9.5 || ^10.0", + "xheaven/composer-git-hooks": "^3.1" }, "suggest": { "guzzlehttp/psr7": "To provide a psr/http-factory-implementation implementation", @@ -58,6 +61,16 @@ "providers": [ "LaravelWebauthn\\WebauthnServiceProvider" ] + }, + "hooks": { + "config": { + "stop-on-failure": [ + "pre-commit" + ] + }, + "pre-commit": [ + "files=$(git diff --staged --name-only);\"$(dirname \"$0\")/../../vendor/bin/pint\" $files; git add $files" + ] } }, "autoload": { @@ -92,7 +105,7 @@ "type": "github" } ], - "time": "2023-02-19T17:57:47+00:00" + "time": "2023-10-01T12:00:29+00:00" }, { "name": "bacon/bacon-qr-code", @@ -347,16 +360,16 @@ }, { "name": "composer/semver", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { @@ -406,9 +419,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { @@ -424,20 +437,20 @@ "type": "tidelift" } ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { "name": "dasprid/enum", - "version": "1.0.4", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f" + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f", - "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", + "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", "shasum": "" }, "require": { @@ -472,9 +485,9 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.4" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" }, - "time": "2023-03-01T18:44:03+00:00" + "time": "2023-08-25T16:18:39+00:00" }, { "name": "dflydev/dot-access-data", @@ -646,16 +659,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.4", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f" + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/00d03067f07482f025d41ab55e4ba0db5eca2cdf", + "reference": "00d03067f07482f025d41ab55e4ba0db5eca2cdf", "shasum": "" }, "require": { @@ -670,11 +683,12 @@ "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2022.3", - "phpstan/phpstan": "1.10.14", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.35", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.7", + "phpunit/phpunit": "9.6.13", "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^5.4|^6.0", "symfony/console": "^4.4|^5.4|^6.0", @@ -738,7 +752,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.4" + "source": "https://github.com/doctrine/dbal/tree/3.7.0" }, "funding": [ { @@ -754,20 +768,20 @@ "type": "tidelift" } ], - "time": "2023-06-15T07:40:12+00:00" + "time": "2023-09-26T20:56:55+00:00" }, { "name": "doctrine/deprecations", - "version": "v1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", - "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", "shasum": "" }, "require": { @@ -799,9 +813,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" }, - "time": "2023-06-03T09:27:29+00:00" + "time": "2023-09-27T20:04:15+00:00" }, { "name": "doctrine/event-manager", @@ -1064,16 +1078,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -1113,7 +1127,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -1121,7 +1135,7 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "egulias/email-validator", @@ -1386,22 +1400,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.7.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5" + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5", - "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1492,7 +1506,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.7.0" + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" }, "funding": [ { @@ -1508,20 +1522,20 @@ "type": "tidelift" } ], - "time": "2023-05-21T14:04:53+00:00" + "time": "2023-08-27T10:20:53+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6" + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6", - "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", "shasum": "" }, "require": { @@ -1575,7 +1589,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.0" + "source": "https://github.com/guzzle/promises/tree/2.0.1" }, "funding": [ { @@ -1591,20 +1605,20 @@ "type": "tidelift" } ], - "time": "2023-05-21T13:50:22+00:00" + "time": "2023-08-03T15:11:55+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.5.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6" + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6", - "reference": "b635f279edd83fc275f822a1188157ffea568ff6", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", "shasum": "" }, "require": { @@ -1691,7 +1705,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.5.0" + "source": "https://github.com/guzzle/psr7/tree/2.6.1" }, "funding": [ { @@ -1707,20 +1721,20 @@ "type": "tidelift" } ], - "time": "2023-04-17T16:11:26+00:00" + "time": "2023-08-27T10:13:57+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.1", + "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2" + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d", + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d", "shasum": "" }, "require": { @@ -1728,15 +1742,11 @@ "symfony/polyfill-php80": "^1.17" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", "phpunit/phpunit": "^8.5.19 || ^9.5.8", "uri-template/tests": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { "GuzzleHttp\\UriTemplate\\": "src" @@ -1775,7 +1785,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.1" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.2" }, "funding": [ { @@ -1791,7 +1801,79 @@ "type": "tidelift" } ], - "time": "2021-10-07T12:57:01+00:00" + "time": "2023-08-27T10:19:19+00:00" + }, + { + "name": "inertiajs/inertia-laravel", + "version": "v0.6.10", + "source": { + "type": "git", + "url": "https://github.com/inertiajs/inertia-laravel.git", + "reference": "609f960c9392e61f8f10418e333599cf1b12efbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/609f960c9392e61f8f10418e333599cf1b12efbe", + "reference": "609f960c9392e61f8f10418e333599cf1b12efbe", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel/framework": "^6.0|^7.0|^8.74|^9.0|^10.0", + "php": "^7.2|~8.0.0|~8.1.0|~8.2.0|~8.3.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^4.0|^5.0|^6.4|^7.0|^8.0", + "phpunit/phpunit": "^8.0|^9.5.8", + "roave/security-advisories": "dev-master" + }, + "suggest": { + "ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Inertia\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./helpers.php" + ], + "psr-4": { + "Inertia\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "homepage": "https://reinink.ca" + } + ], + "description": "The Laravel adapter for Inertia.js.", + "keywords": [ + "inertia", + "laravel" + ], + "support": { + "issues": "https://github.com/inertiajs/inertia-laravel/issues", + "source": "https://github.com/inertiajs/inertia-laravel/tree/v0.6.10" + }, + "funding": [ + { + "url": "https://github.com/reinink", + "type": "github" + } + ], + "time": "2023-09-13T02:24:55+00:00" }, { "name": "intervention/image", @@ -1879,16 +1961,16 @@ }, { "name": "laravel/framework", - "version": "v10.13.5", + "version": "v10.26.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "03106ae9ba2ec4b36dc973b7bdca6fad81e032b4" + "reference": "6e5440f7c518f26b4495e5d7e4796ec239e26df9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/03106ae9ba2ec4b36dc973b7bdca6fad81e032b4", - "reference": "03106ae9ba2ec4b36dc973b7bdca6fad81e032b4", + "url": "https://api.github.com/repos/laravel/framework/zipball/6e5440f7c518f26b4495e5d7e4796ec239e26df9", + "reference": "6e5440f7c518f26b4495e5d7e4796ec239e26df9", "shasum": "" }, "require": { @@ -1906,11 +1988,12 @@ "ext-tokenizer": "*", "fruitcake/php-cors": "^1.2", "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1.9", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.62.1", + "nesbot/carbon": "^2.67", "nunomaduro/termwind": "^1.13", "php": "^8.1", "psr/container": "^1.1.1|^2.0.1", @@ -1987,9 +2070,8 @@ "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.4", + "orchestra/testbench-core": "^8.12", "pda/pheanstalk": "^4.0", - "phpstan/phpdoc-parser": "^1.15", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", "predis/predis": "^2.0.2", @@ -2075,20 +2157,77 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-06-08T20:25:36+00:00" + "time": "2023-10-03T14:24:20+00:00" }, { - "name": "laravel/sanctum", - "version": "v3.2.5", + "name": "laravel/prompts", + "version": "v0.1.11", "source": { "type": "git", - "url": "https://github.com/laravel/sanctum.git", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876" + "url": "https://github.com/laravel/prompts.git", + "reference": "cce65a90e64712909ea1adc033e1d88de8455ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/8ebda85d59d3c414863a7f4d816ef8302faad876", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876", + "url": "https://api.github.com/repos/laravel/prompts/zipball/cce65a90e64712909ea1adc033e1d88de8455ffd", + "reference": "cce65a90e64712909ea1adc033e1d88de8455ffd", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.11" + }, + "time": "2023-10-03T01:07:35+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v3.3.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "338f633e6487e76b255470d3373fbc29228aa971" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/338f633e6487e76b255470d3373fbc29228aa971", + "reference": "338f633e6487e76b255470d3373fbc29228aa971", "shasum": "" }, "require": { @@ -2101,9 +2240,9 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.28.2|^8.8.3", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "type": "library", "extra": { @@ -2141,20 +2280,20 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2023-05-01T19:39:51+00:00" + "time": "2023-09-07T15:46:33+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/e5a3057a5591e1cfe8183034b0203921abe2c902", + "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902", "shasum": "" }, "require": { @@ -2201,20 +2340,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-01-30T18:31:20+00:00" + "time": "2023-07-14T13:56:28+00:00" }, { "name": "laravel/tinker", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10" + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", - "reference": "04a2d3bd0d650c0764f70bf49d1ee39393e4eb10", + "url": "https://api.github.com/repos/laravel/tinker/zipball/b936d415b252b499e8c3b1f795cd4fc20f57e1f3", + "reference": "b936d415b252b499e8c3b1f795cd4fc20f57e1f3", "shasum": "" }, "require": { @@ -2227,6 +2366,7 @@ }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.5.8|^9.3.3" }, "suggest": { @@ -2267,9 +2407,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.8.1" + "source": "https://github.com/laravel/tinker/tree/v2.8.2" }, - "time": "2023-02-15T16:40:09+00:00" + "time": "2023-08-15T14:27:00+00:00" }, { "name": "laravel/ui", @@ -2335,20 +2475,20 @@ }, { "name": "lcobucci/clock", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc" + "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/039ef98c6b57b101d10bd11d8fdfda12cbd996dc", - "reference": "039ef98c6b57b101d10bd11d8fdfda12cbd996dc", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/30a854ceb22bd87d83a7a4563b3f6312453945fc", + "reference": "30a854ceb22bd87d83a7a4563b3f6312453945fc", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0", + "php": "~8.2.0", "psr/clock": "^1.0" }, "provide": { @@ -2356,13 +2496,13 @@ }, "require-dev": { "infection/infection": "^0.26", - "lcobucci/coding-standard": "^9.0", + "lcobucci/coding-standard": "^10.0.0", "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-deprecation-rules": "^1.1.1", - "phpstan/phpstan-phpunit": "^1.3.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^9.5.27" + "phpstan/phpstan": "^1.10.7", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.0.17" }, "type": "library", "autoload": { @@ -2383,7 +2523,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.0.0" + "source": "https://github.com/lcobucci/clock/tree/3.1.0" }, "funding": [ { @@ -2395,20 +2535,20 @@ "type": "patreon" } ], - "time": "2022-12-19T15:00:24+00:00" + "time": "2023-03-20T19:12:25+00:00" }, { "name": "league/commonmark", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048" + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", "shasum": "" }, "require": { @@ -2501,7 +2641,7 @@ "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "time": "2023-08-30T16:55:00+00:00" }, { "name": "league/config", @@ -2587,16 +2727,16 @@ }, { "name": "league/flysystem", - "version": "3.15.1", + "version": "3.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed" + "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a141d430414fcb8bf797a18716b09f759a385bed", - "reference": "a141d430414fcb8bf797a18716b09f759a385bed", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729", + "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729", "shasum": "" }, "require": { @@ -2605,6 +2745,8 @@ "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", @@ -2624,7 +2766,7 @@ "microsoft/azure-storage-blob": "^1.1", "phpseclib/phpseclib": "^3.0.14", "phpstan/phpstan": "^0.12.26", - "phpunit/phpunit": "^9.5.11", + "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" }, "type": "library", @@ -2659,7 +2801,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.15.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.16.0" }, "funding": [ { @@ -2671,20 +2813,20 @@ "type": "github" } ], - "time": "2023-05-04T09:04:26+00:00" + "time": "2023-09-07T19:22:17+00:00" }, { "name": "league/flysystem-local", - "version": "3.15.0", + "version": "3.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3" + "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/543f64c397fefdf9cfeac443ffb6beff602796b3", - "reference": "543f64c397fefdf9cfeac443ffb6beff602796b3", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781", + "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781", "shasum": "" }, "require": { @@ -2719,7 +2861,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.15.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0" }, "funding": [ { @@ -2731,30 +2873,30 @@ "type": "github" } ], - "time": "2023-05-02T20:02:14+00:00" + "time": "2023-08-30T10:23:59+00:00" }, { "name": "league/mime-type-detection", - "version": "1.11.0", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96", + "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" + "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" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" }, "type": "library", "autoload": { @@ -2775,7 +2917,7 @@ "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.11.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0" }, "funding": [ { @@ -2787,7 +2929,7 @@ "type": "tidelift" } ], - "time": "2022-04-17T13:12:02+00:00" + "time": "2023-08-05T12:09:49+00:00" }, { "name": "maatwebsite/excel", @@ -3059,31 +3201,31 @@ }, { "name": "mews/captcha", - "version": "3.3.0", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/mewebstudio/captcha.git", - "reference": "8c439263d0ba42e6c05e4d1f3bee0a2b97e675d5" + "reference": "7aee0e80bcf7eb17fc0d574244e384e56ae2af77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mewebstudio/captcha/zipball/8c439263d0ba42e6c05e4d1f3bee0a2b97e675d5", - "reference": "8c439263d0ba42e6c05e4d1f3bee0a2b97e675d5", + "url": "https://api.github.com/repos/mewebstudio/captcha/zipball/7aee0e80bcf7eb17fc0d574244e384e56ae2af77", + "reference": "7aee0e80bcf7eb17fc0d574244e384e56ae2af77", "shasum": "" }, "require": { "ext-gd": "*", - "illuminate/config": "~5|^6|^7|^8|^9|^10.0", - "illuminate/filesystem": "~5|^6|^7|^8|^9|^10.0", - "illuminate/hashing": "~5|^6|^7|^8|^9|^10.0", - "illuminate/session": "~5|^6|^7|^8|^9|^10.0", - "illuminate/support": "~5|^6|^7|^8|^9|^10.0", + "illuminate/config": "~5|^6|^7|^8|^9|^10", + "illuminate/filesystem": "~5|^6|^7|^8|^9|^10", + "illuminate/hashing": "~5|^6|^7|^8|^9|^10", + "illuminate/session": "~5|^6|^7|^8|^9|^10", + "illuminate/support": "~5|^6|^7|^8|^9|^10", "intervention/image": "~2.5", - "php": "^7.2|^8.0" + "php": "^7.2|^8.1" }, "require-dev": { "mockery/mockery": "^1.0", - "phpunit/phpunit": "^8.5|^9.0" + "phpunit/phpunit": "^8.5|^9.5.10" }, "type": "package", "extra": { @@ -3116,7 +3258,7 @@ "role": "Developer" } ], - "description": "Laravel 5 & 6 Captcha Package", + "description": "Laravel 5/6/7/8/9/10 Captcha Package", "homepage": "https://github.com/mewebstudio/captcha", "keywords": [ "captcha", @@ -3126,9 +3268,9 @@ ], "support": { "issues": "https://github.com/mewebstudio/captcha/issues", - "source": "https://github.com/mewebstudio/captcha/tree/3.3.0" + "source": "https://github.com/mewebstudio/captcha/tree/3.3.2" }, - "time": "2023-03-14T08:30:02+00:00" + "time": "2023-07-15T15:07:48+00:00" }, { "name": "monolog/monolog", @@ -3233,25 +3375,29 @@ }, { "name": "nesbot/carbon", - "version": "2.67.0", + "version": "2.71.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8" + "reference": "98276233188583f2ff845a0f992a235472d9466a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/c1001b3bc75039b07f38a79db5237c4c529e04c8", - "reference": "c1001b3bc75039b07f38a79db5237c4c529e04c8", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/98276233188583f2ff845a0f992a235472d9466a", + "reference": "98276233188583f2ff845a0f992a235472d9466a", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", @@ -3331,25 +3477,25 @@ "type": "tidelift" } ], - "time": "2023-05-25T22:09:47+00:00" + "time": "2023-09-25T11:31:05+00:00" }, { "name": "nette/schema", - "version": "v1.2.3", + "version": "v1.2.4", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab", + "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab", "shasum": "" }, "require": { "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "php": "7.1 - 8.3" }, "require-dev": { "nette/tester": "^2.3 || ^2.4", @@ -3391,26 +3537,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "source": "https://github.com/nette/schema/tree/v1.2.4" }, - "time": "2022-10-13T01:24:26+00:00" + "time": "2023-08-05T18:56:25+00:00" }, { "name": "nette/utils", - "version": "v4.0.0", + "version": "v4.0.2", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + "reference": "cead6637226456b35e1175cc53797dd585d85545" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "url": "https://api.github.com/repos/nette/utils/zipball/cead6637226456b35e1175cc53797dd585d85545", + "reference": "cead6637226456b35e1175cc53797dd585d85545", "shasum": "" }, "require": { - "php": ">=8.0 <8.3" + "php": ">=8.0 <8.4" }, "conflict": { "nette/finder": "<3", @@ -3418,7 +3564,7 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "^2.4", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", "tracy/tracy": "^2.9" }, @@ -3428,8 +3574,7 @@ "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()", - "ext-xml": "to use Strings::length() etc. when mbstring is not available" + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" }, "type": "library", "extra": { @@ -3478,22 +3623,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.0" + "source": "https://github.com/nette/utils/tree/v4.0.2" }, - "time": "2023-02-02T10:41:53+00:00" + "time": "2023-09-19T11:58:07+00:00" }, { "name": "nikic/php-parser", - "version": "v4.16.0", + "version": "v4.17.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17" + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "shasum": "" }, "require": { @@ -3534,9 +3679,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" }, - "time": "2023-06-25T14:52:30+00:00" + "time": "2023-08-13T19:53:39+00:00" }, { "name": "nunomaduro/termwind", @@ -3693,16 +3838,16 @@ }, { "name": "php-mime-mail-parser/php-mime-mail-parser", - "version": "8.0.1", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git", - "reference": "c1abb017f22bda5f406dc1d5b52f1cb40b17c856" + "reference": "f922907123625d940adbba7be403a2e620d0a8e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c1abb017f22bda5f406dc1d5b52f1cb40b17c856", - "reference": "c1abb017f22bda5f406dc1d5b52f1cb40b17c856", + "url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/f922907123625d940adbba7be403a2e620d0a8e5", + "reference": "f922907123625d940adbba7be403a2e620d0a8e5", "shasum": "" }, "require": { @@ -3772,9 +3917,9 @@ ], "support": { "issues": "https://github.com/php-mime-mail-parser/php-mime-mail-parser/issues", - "source": "https://github.com/php-mime-mail-parser/php-mime-mail-parser/tree/8.0.1" + "source": "https://github.com/php-mime-mail-parser/php-mime-mail-parser/tree/8.0.2" }, - "time": "2023-05-30T16:25:03+00:00" + "time": "2023-08-23T13:29:02+00:00" }, { "name": "phpoffice/phpspreadsheet", @@ -4353,16 +4498,16 @@ }, { "name": "psr/http-client", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { @@ -4399,9 +4544,9 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "source": "https://github.com/php-fig/http-client" }, - "time": "2023-04-10T20:12:12+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", @@ -4614,16 +4759,16 @@ }, { "name": "psy/psysh", - "version": "v0.11.18", + "version": "v0.11.21", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec" + "reference": "bcb22101107f3bf770523b65630c9d547f60c540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", - "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/bcb22101107f3bf770523b65630c9d547f60c540", + "reference": "bcb22101107f3bf770523b65630c9d547f60c540", "shasum": "" }, "require": { @@ -4653,6 +4798,10 @@ "extra": { "branch-alias": { "dev-main": "0.11.x-dev" + }, + "bamarni-bin": { + "bin-links": false, + "forward-command": false } }, "autoload": { @@ -4684,9 +4833,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.11.18" + "source": "https://github.com/bobthecow/psysh/tree/v0.11.21" }, - "time": "2023-05-23T02:31:11+00:00" + "time": "2023-09-17T21:15:54+00:00" }, { "name": "ralouphie/getallheaders", @@ -5108,16 +5257,16 @@ }, { "name": "symfony/console", - "version": "v6.3.0", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", "shasum": "" }, "require": { @@ -5178,7 +5327,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.0" + "source": "https://github.com/symfony/console/tree/v6.3.4" }, "funding": [ { @@ -5194,20 +5343,20 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-08-16T10:10:12+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf" + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", "shasum": "" }, "require": { @@ -5243,7 +5392,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.0" + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" }, "funding": [ { @@ -5259,7 +5408,7 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:43:42+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5330,16 +5479,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99d2d814a6351461af350ead4d963bd67451236f" + "reference": "1f69476b64fb47105c06beef757766c376b548c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99d2d814a6351461af350ead4d963bd67451236f", - "reference": "99d2d814a6351461af350ead4d963bd67451236f", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1f69476b64fb47105c06beef757766c376b548c4", + "reference": "1f69476b64fb47105c06beef757766c376b548c4", "shasum": "" }, "require": { @@ -5384,7 +5533,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.3.0" + "source": "https://github.com/symfony/error-handler/tree/v6.3.5" }, "funding": [ { @@ -5400,20 +5549,20 @@ "type": "tidelift" } ], - "time": "2023-05-10T12:03:13+00:00" + "time": "2023-09-12T06:57:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa" + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", "shasum": "" }, "require": { @@ -5464,7 +5613,7 @@ "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/v6.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" }, "funding": [ { @@ -5480,7 +5629,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T14:41:17+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5560,16 +5709,16 @@ }, { "name": "symfony/finder", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", "shasum": "" }, "require": { @@ -5604,7 +5753,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.0" + "source": "https://github.com/symfony/finder/tree/v6.3.5" }, "funding": [ { @@ -5620,20 +5769,20 @@ "type": "tidelift" } ], - "time": "2023-04-02T01:25:41+00:00" + "time": "2023-09-26T12:56:25+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.3.1", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66" + "reference": "b50f5e281d722cb0f4c296f908bacc3e2b721957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b50f5e281d722cb0f4c296f908bacc3e2b721957", + "reference": "b50f5e281d722cb0f4c296f908bacc3e2b721957", "shasum": "" }, "require": { @@ -5681,7 +5830,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.1" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.5" }, "funding": [ { @@ -5697,20 +5846,20 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-09-04T21:33:54+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.1", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374" + "reference": "9f991a964368bee8d883e8d57ced4fe9fff04dfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/161e16fd2e35fb4881a43bc8b383dfd5be4ac374", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f991a964368bee8d883e8d57ced4fe9fff04dfc", + "reference": "9f991a964368bee8d883e8d57ced4fe9fff04dfc", "shasum": "" }, "require": { @@ -5719,7 +5868,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.3", "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.2.7", + "symfony/http-foundation": "^6.3.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5727,7 +5876,7 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.3", + "symfony/dependency-injection": "<6.3.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", @@ -5751,7 +5900,7 @@ "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3", + "symfony/dependency-injection": "^6.3.4", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", @@ -5794,7 +5943,7 @@ "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/v6.3.1" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.5" }, "funding": [ { @@ -5810,20 +5959,20 @@ "type": "tidelift" } ], - "time": "2023-06-26T06:07:32+00:00" + "time": "2023-09-30T06:37:04+00:00" }, { "name": "symfony/mailer", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435" + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/7b03d9be1dea29bfec0a6c7b603f5072a4c97435", - "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d89611a7830d51b5e118bca38e390dea92f9ea06", + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06", "shasum": "" }, "require": { @@ -5874,7 +6023,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.3.0" + "source": "https://github.com/symfony/mailer/tree/v6.3.5" }, "funding": [ { @@ -5890,24 +6039,25 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-09-06T09:47:15+00:00" }, { "name": "symfony/mime", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad" + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", - "reference": "7b5d2121858cd6efbed778abce9cfdd7ab1f62ad", + "url": "https://api.github.com/repos/symfony/mime/zipball/d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -5916,7 +6066,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2" + "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", @@ -5925,7 +6075,7 @@ "symfony/dependency-injection": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "^6.2" + "symfony/serializer": "~6.2.13|^6.3.2" }, "type": "library", "autoload": { @@ -5957,7 +6107,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.0" + "source": "https://github.com/symfony/mime/tree/v6.3.5" }, "funding": [ { @@ -5973,20 +6123,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T15:57:00+00:00" + "time": "2023-09-29T06:59:36+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -6001,7 +6151,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6039,7 +6189,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -6055,20 +6205,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -6080,7 +6230,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6120,7 +6270,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -6136,20 +6286,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", "shasum": "" }, "require": { @@ -6163,7 +6313,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6207,7 +6357,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" }, "funding": [ { @@ -6223,20 +6373,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:30:37+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -6248,7 +6398,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6291,7 +6441,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -6307,20 +6457,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -6335,7 +6485,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6374,7 +6524,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -6390,20 +6540,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { @@ -6412,7 +6562,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6450,7 +6600,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" }, "funding": [ { @@ -6466,20 +6616,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -6488,7 +6638,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6533,7 +6683,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" }, "funding": [ { @@ -6549,20 +6699,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57" + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/508c652ba3ccf69f8c97f251534f229791b52a57", - "reference": "508c652ba3ccf69f8c97f251534f229791b52a57", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", "shasum": "" }, "require": { @@ -6572,7 +6722,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6585,7 +6735,10 @@ ], "psr-4": { "Symfony\\Polyfill\\Php83\\": "" - } + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6610,7 +6763,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" }, "funding": [ { @@ -6626,20 +6779,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-08-16T06:22:46+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166" + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e", + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e", "shasum": "" }, "require": { @@ -6654,7 +6807,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6692,7 +6845,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0" }, "funding": [ { @@ -6708,20 +6861,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/process", - "version": "v6.3.0", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", "shasum": "" }, "require": { @@ -6753,7 +6906,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.0" + "source": "https://github.com/symfony/process/tree/v6.3.4" }, "funding": [ { @@ -6769,24 +6922,25 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-08-07T10:39:22+00:00" }, { "name": "symfony/routing", - "version": "v6.3.1", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5" + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", + "url": "https://api.github.com/repos/symfony/routing/zipball/82616e59acd3e3d9c916bba798326cb7796d7d31", + "reference": "82616e59acd3e3d9c916bba798326cb7796d7d31", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", @@ -6835,7 +6989,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.1" + "source": "https://github.com/symfony/routing/tree/v6.3.5" }, "funding": [ { @@ -6851,7 +7005,7 @@ "type": "tidelift" } ], - "time": "2023-06-05T15:30:22+00:00" + "time": "2023-09-20T16:05:51+00:00" }, { "name": "symfony/service-contracts", @@ -6937,16 +7091,16 @@ }, { "name": "symfony/string", - "version": "v6.3.0", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", "shasum": "" }, "require": { @@ -7003,7 +7157,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.0" + "source": "https://github.com/symfony/string/tree/v6.3.5" }, "funding": [ { @@ -7019,24 +7173,25 @@ "type": "tidelift" } ], - "time": "2023-03-21T21:06:29+00:00" + "time": "2023-09-18T10:38:32+00:00" }, { "name": "symfony/translation", - "version": "v6.3.0", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f" + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/f72b2cba8f79dd9d536f534f76874b58ad37876f", - "reference": "f72b2cba8f79dd9d536f534f76874b58ad37876f", + "url": "https://api.github.com/repos/symfony/translation/zipball/3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -7097,7 +7252,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.3.0" + "source": "https://github.com/symfony/translation/tree/v6.3.3" }, "funding": [ { @@ -7113,7 +7268,7 @@ "type": "tidelift" } ], - "time": "2023-05-19T12:46:45+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/translation-contracts", @@ -7269,20 +7424,21 @@ }, { "name": "symfony/var-dumper", - "version": "v6.3.1", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515" + "reference": "3d9999376be5fea8de47752837a3e1d1c5f69ef5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c81268d6960ddb47af17391a27d222bd58cf0515", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3d9999376be5fea8de47752837a3e1d1c5f69ef5", + "reference": "3d9999376be5fea8de47752837a3e1d1c5f69ef5", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -7291,6 +7447,7 @@ "require-dev": { "ext-iconv": "*", "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" @@ -7331,7 +7488,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.1" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.5" }, "funding": [ { @@ -7347,146 +7504,74 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-09-12T10:11:35+00:00" }, { - "name": "thecodingmachine/safe", - "version": "v2.5.0", + "name": "tightenco/ziggy", + "version": "v1.6.2", "source": { "type": "git", - "url": "https://github.com/thecodingmachine/safe.git", - "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0" + "url": "https://github.com/tighten/ziggy.git", + "reference": "41eb6384a9f9ae85cf54d6dc8f98dab282b07890" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0", - "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "url": "https://api.github.com/repos/tighten/ziggy/zipball/41eb6384a9f9ae85cf54d6dc8f98dab282b07890", + "reference": "41eb6384a9f9ae85cf54d6dc8f98dab282b07890", "shasum": "" }, "require": { - "php": "^8.0" + "ext-json": "*", + "laravel/framework": ">=5.4@dev" }, "require-dev": { - "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.2", - "thecodingmachine/phpstan-strict-rules": "^1.0" + "orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "phpunit/phpunit": "^6.0 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev" + "laravel": { + "providers": [ + "Tightenco\\Ziggy\\ZiggyServiceProvider" + ] } }, "autoload": { - "files": [ - "deprecated/apc.php", - "deprecated/array.php", - "deprecated/datetime.php", - "deprecated/libevent.php", - "deprecated/misc.php", - "deprecated/password.php", - "deprecated/mssql.php", - "deprecated/stats.php", - "deprecated/strings.php", - "lib/special_cases.php", - "deprecated/mysqli.php", - "generated/apache.php", - "generated/apcu.php", - "generated/array.php", - "generated/bzip2.php", - "generated/calendar.php", - "generated/classobj.php", - "generated/com.php", - "generated/cubrid.php", - "generated/curl.php", - "generated/datetime.php", - "generated/dir.php", - "generated/eio.php", - "generated/errorfunc.php", - "generated/exec.php", - "generated/fileinfo.php", - "generated/filesystem.php", - "generated/filter.php", - "generated/fpm.php", - "generated/ftp.php", - "generated/funchand.php", - "generated/gettext.php", - "generated/gmp.php", - "generated/gnupg.php", - "generated/hash.php", - "generated/ibase.php", - "generated/ibmDb2.php", - "generated/iconv.php", - "generated/image.php", - "generated/imap.php", - "generated/info.php", - "generated/inotify.php", - "generated/json.php", - "generated/ldap.php", - "generated/libxml.php", - "generated/lzf.php", - "generated/mailparse.php", - "generated/mbstring.php", - "generated/misc.php", - "generated/mysql.php", - "generated/network.php", - "generated/oci8.php", - "generated/opcache.php", - "generated/openssl.php", - "generated/outcontrol.php", - "generated/pcntl.php", - "generated/pcre.php", - "generated/pgsql.php", - "generated/posix.php", - "generated/ps.php", - "generated/pspell.php", - "generated/readline.php", - "generated/rpminfo.php", - "generated/rrd.php", - "generated/sem.php", - "generated/session.php", - "generated/shmop.php", - "generated/sockets.php", - "generated/sodium.php", - "generated/solr.php", - "generated/spl.php", - "generated/sqlsrv.php", - "generated/ssdeep.php", - "generated/ssh2.php", - "generated/stream.php", - "generated/strings.php", - "generated/swoole.php", - "generated/uodbc.php", - "generated/uopz.php", - "generated/url.php", - "generated/var.php", - "generated/xdiff.php", - "generated/xml.php", - "generated/xmlrpc.php", - "generated/yaml.php", - "generated/yaz.php", - "generated/zip.php", - "generated/zlib.php" - ], - "classmap": [ - "lib/DateTime.php", - "lib/DateTimeImmutable.php", - "lib/Exceptions/", - "deprecated/Exceptions/", - "generated/Exceptions/" - ] + "psr-4": { + "Tightenco\\Ziggy\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "authors": [ + { + "name": "Daniel Coulbourne", + "email": "daniel@tighten.co" + }, + { + "name": "Jake Bathman", + "email": "jake@tighten.co" + }, + { + "name": "Jacob Baker-Kretzmar", + "email": "jacob@tighten.co" + } + ], + "description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.", + "homepage": "https://github.com/tighten/ziggy", + "keywords": [ + "Ziggy", + "javascript", + "laravel", + "routes" + ], "support": { - "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" + "issues": "https://github.com/tighten/ziggy/issues", + "source": "https://github.com/tighten/ziggy/tree/v1.6.2" }, - "time": "2023-04-05T11:54:14+00:00" + "time": "2023-08-18T20:28:21+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -7701,16 +7786,16 @@ }, { "name": "web-auth/cose-lib", - "version": "4.2.0", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/web-auth/cose-lib.git", - "reference": "9e0cc49810b445ed0aa50d122ee82c63882af9a0" + "reference": "0ecad86d2d034ea22e2205d81c8cdec13d93a991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/9e0cc49810b445ed0aa50d122ee82c63882af9a0", - "reference": "9e0cc49810b445ed0aa50d122ee82c63882af9a0", + "url": "https://api.github.com/repos/web-auth/cose-lib/zipball/0ecad86d2d034ea22e2205d81c8cdec13d93a991", + "reference": "0ecad86d2d034ea22e2205d81c8cdec13d93a991", "shasum": "" }, "require": { @@ -7723,17 +7808,18 @@ }, "require-dev": { "ekino/phpstan-banned-code": "^1.0", - "infection/infection": "^0.26.12", + "infection/infection": "^0.27", "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.7", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.2", - "phpunit/phpunit": "^10.0", + "phpunit/phpunit": "^10.1", "qossmic/deptrac-shim": "^1.0", - "rector/rector": "^0.15", + "rector/rector": "^0.17", "symfony/phpunit-bridge": "^6.1", - "symplify/easy-coding-standard": "^11.0" + "symplify/easy-coding-standard": "^12.0" }, "suggest": { "ext-bcmath": "For better performance, please install either GMP (recommended) or BCMath extension", @@ -7767,7 +7853,7 @@ ], "support": { "issues": "https://github.com/web-auth/cose-lib/issues", - "source": "https://github.com/web-auth/cose-lib/tree/4.2.0" + "source": "https://github.com/web-auth/cose-lib/tree/4.2.3" }, "funding": [ { @@ -7779,20 +7865,20 @@ "type": "patreon" } ], - "time": "2023-02-28T18:05:22+00:00" + "time": "2023-07-26T13:32:03+00:00" }, { "name": "web-auth/metadata-service", - "version": "4.6.2", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-metadata-service.git", - "reference": "b58fbb0df46450acc426329bd87b60d794859da0" + "reference": "33f4481be91c7a9a6809441400a664031e208f5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-metadata-service/zipball/b58fbb0df46450acc426329bd87b60d794859da0", - "reference": "b58fbb0df46450acc426329bd87b60d794859da0", + "url": "https://api.github.com/repos/web-auth/webauthn-metadata-service/zipball/33f4481be91c7a9a6809441400a664031e208f5c", + "reference": "33f4481be91c7a9a6809441400a664031e208f5c", "shasum": "" }, "require": { @@ -7848,7 +7934,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-metadata-service/tree/4.6.2" + "source": "https://github.com/web-auth/webauthn-metadata-service/tree/4.7.1" }, "funding": [ { @@ -7860,20 +7946,20 @@ "type": "patreon" } ], - "time": "2023-06-01T19:06:30+00:00" + "time": "2023-09-08T10:14:34+00:00" }, { "name": "web-auth/webauthn-lib", - "version": "4.6.2", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/web-auth/webauthn-lib.git", - "reference": "e0f85f09b4e1a48169352290e7ccfd29ade93e34" + "reference": "f4d2f3da7160ac1a634aca38e72b31629efdec87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/e0f85f09b4e1a48169352290e7ccfd29ade93e34", - "reference": "e0f85f09b4e1a48169352290e7ccfd29ade93e34", + "url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/f4d2f3da7160ac1a634aca38e72b31629efdec87", + "reference": "f4d2f3da7160ac1a634aca38e72b31629efdec87", "shasum": "" }, "require": { @@ -7888,7 +7974,7 @@ "psr/log": "^1.0|^2.0|^3.0", "spomky-labs/cbor-php": "^3.0", "symfony/uid": "^6.1", - "web-auth/cose-lib": "^4.0.12", + "web-auth/cose-lib": "^4.2.3", "web-auth/metadata-service": "self.version" }, "require-dev": { @@ -7936,7 +8022,7 @@ "webauthn" ], "support": { - "source": "https://github.com/web-auth/webauthn-lib/tree/4.6.2" + "source": "https://github.com/web-auth/webauthn-lib/tree/4.7.1" }, "funding": [ { @@ -7948,20 +8034,20 @@ "type": "patreon" } ], - "time": "2023-06-12T14:32:32+00:00" + "time": "2023-09-08T10:14:34+00:00" }, { "name": "web-token/jwt-core", - "version": "3.2.7", + "version": "3.2.8", "source": { "type": "git", "url": "https://github.com/web-token/jwt-core.git", - "reference": "db58b6ebbe1a7d5869688e989b1cf110c6ab888f" + "reference": "2bc6e99a60910d0f495682acd8b23d3eef9865a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/web-token/jwt-core/zipball/db58b6ebbe1a7d5869688e989b1cf110c6ab888f", - "reference": "db58b6ebbe1a7d5869688e989b1cf110c6ab888f", + "url": "https://api.github.com/repos/web-token/jwt-core/zipball/2bc6e99a60910d0f495682acd8b23d3eef9865a3", + "reference": "2bc6e99a60910d0f495682acd8b23d3eef9865a3", "shasum": "" }, "require": { @@ -8016,7 +8102,7 @@ "symfony" ], "support": { - "source": "https://github.com/web-token/jwt-core/tree/3.2.7" + "source": "https://github.com/web-token/jwt-core/tree/3.2.8" }, "funding": [ { @@ -8024,11 +8110,11 @@ "type": "patreon" } ], - "time": "2023-02-02T17:35:17+00:00" + "time": "2023-08-23T09:49:09+00:00" }, { "name": "web-token/jwt-signature", - "version": "3.2.7", + "version": "3.2.8", "source": { "type": "git", "url": "https://github.com/web-token/jwt-signature.git", @@ -8093,7 +8179,7 @@ "symfony" ], "support": { - "source": "https://github.com/web-token/jwt-signature/tree/3.2.7" + "source": "https://github.com/web-token/jwt-signature/tree/3.2.8" }, "funding": [ { @@ -8233,16 +8319,16 @@ }, { "name": "filp/whoops", - "version": "2.15.2", + "version": "2.15.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73" + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", - "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73", + "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", "shasum": "" }, "require": { @@ -8292,7 +8378,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.2" + "source": "https://github.com/filp/whoops/tree/2.15.3" }, "funding": [ { @@ -8300,7 +8386,7 @@ "type": "github" } ], - "time": "2023-04-12T12:00:00+00:00" + "time": "2023-07-13T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -8355,16 +8441,16 @@ }, { "name": "laravel/pint", - "version": "v1.10.3", + "version": "v1.13.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "c472786bca01e4812a9bb7933b23edfc5b6877b7" + "reference": "bbb13460d7f8c5c0cd9a58109beedd79cd7331ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/c472786bca01e4812a9bb7933b23edfc5b6877b7", - "reference": "c472786bca01e4812a9bb7933b23edfc5b6877b7", + "url": "https://api.github.com/repos/laravel/pint/zipball/bbb13460d7f8c5c0cd9a58109beedd79cd7331ff", + "reference": "bbb13460d7f8c5c0cd9a58109beedd79cd7331ff", "shasum": "" }, "require": { @@ -8375,13 +8461,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.18.0", - "illuminate/view": "^10.5.1", - "laravel-zero/framework": "^10.0.2", - "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.5.1", + "friendsofphp/php-cs-fixer": "^3.26.1", + "illuminate/view": "^10.23.1", + "laravel-zero/framework": "^10.1.2", + "mockery/mockery": "^1.6.6", + "nunomaduro/larastan": "^2.6.4", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.4.0" + "pestphp/pest": "^2.18.2" }, "bin": [ "builds/pint" @@ -8417,41 +8503,37 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-06-20T15:55:03+00:00" + "time": "2023-09-19T15:55:02+00:00" }, { "name": "mockery/mockery", - "version": "1.6.2", + "version": "1.6.6", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191" + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/13a7fa2642c76c58fa2806ef7f565344c817a191", - "reference": "13a7fa2642c76c58fa2806ef7f565344c817a191", + "url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e", + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.4 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3", - "psalm/plugin-phpunit": "^0.18", - "vimeo/psalm": "^5.9" + "phpunit/phpunit": "^8.5 || ^9.6.10", + "psalm/plugin-phpunit": "^0.18.4", + "symplify/easy-coding-standard": "^11.5.0", + "vimeo/psalm": "^4.30" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.6.x-dev" - } - }, "autoload": { "files": [ "library/helpers.php", @@ -8469,12 +8551,20 @@ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.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", @@ -8492,10 +8582,13 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.6.2" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2023-06-07T09:07:52+00:00" + "time": "2023-08-09T00:03:52+00:00" }, { "name": "myclabs/deep-copy", @@ -8558,40 +8651,37 @@ }, { "name": "nunomaduro/collision", - "version": "v7.6.0", + "version": "v7.9.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "87faf7bc1c42d7fef7c60ec5c143050ce2a6189a" + "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/87faf7bc1c42d7fef7c60ec5c143050ce2a6189a", - "reference": "87faf7bc1c42d7fef7c60ec5c143050ce2a6189a", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/296d0cf9fe462837ac0da8a568b56fc026b132da", + "reference": "296d0cf9fe462837ac0da8a568b56fc026b132da", "shasum": "" }, "require": { - "filp/whoops": "^2.15.2", + "filp/whoops": "^2.15.3", "nunomaduro/termwind": "^1.15.1", "php": "^8.1.0", - "symfony/console": "^6.3.0" - }, - "conflict": { - "phpunit/phpunit": "<10.1.2" + "symfony/console": "^6.3.4" }, "require-dev": { - "brianium/paratest": "^7.2.0", - "laravel/framework": "^10.13.5", - "laravel/pint": "^1.10.2", - "laravel/sail": "^1.22.0", - "laravel/sanctum": "^3.2.5", - "laravel/tinker": "^2.8.1", - "nunomaduro/larastan": "^2.6.3", - "orchestra/testbench-core": "^8.5.7", - "pestphp/pest": "^2", - "phpunit/phpunit": "^10.2.2", + "brianium/paratest": "^7.2.7", + "laravel/framework": "^10.23.1", + "laravel/pint": "^1.13.1", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.11.0", + "pestphp/pest": "^2.19.1", + "phpunit/phpunit": "^10.3.5", "sebastian/environment": "^6.0.1", - "spatie/laravel-ignition": "^2.1.3" + "spatie/laravel-ignition": "^2.3.0" }, "type": "library", "extra": { @@ -8650,7 +8740,7 @@ "type": "patreon" } ], - "time": "2023-06-15T10:51:08+00:00" + "time": "2023-09-19T10:45:09+00:00" }, { "name": "phar-io/manifest", @@ -8765,16 +8855,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.2", + "version": "10.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e" + "reference": "56f33548fe522c8d82da7ff3824b42829d324364" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e", - "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364", + "reference": "56f33548fe522c8d82da7ff3824b42829d324364", "shasum": "" }, "require": { @@ -8831,7 +8921,7 @@ "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/10.1.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6" }, "funding": [ { @@ -8839,20 +8929,20 @@ "type": "github" } ], - "time": "2023-05-22T09:04:27+00:00" + "time": "2023-09-19T04:59:03+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "4.0.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "5647d65443818959172645e7ed999217360654b6" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/5647d65443818959172645e7ed999217360654b6", - "reference": "5647d65443818959172645e7ed999217360654b6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { @@ -8892,7 +8982,7 @@ "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/4.0.2" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, "funding": [ { @@ -8900,7 +8990,7 @@ "type": "github" } ], - "time": "2023-05-07T09:13:23+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", @@ -8967,16 +9057,16 @@ }, { "name": "phpunit/php-text-template", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/9f3d3709577a527025f55bcf0f7ab8052c8bb37d", - "reference": "9f3d3709577a527025f55bcf0f7ab8052c8bb37d", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { @@ -9014,7 +9104,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, "funding": [ { @@ -9022,7 +9113,7 @@ "type": "github" } ], - "time": "2023-02-03T06:56:46+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", @@ -9085,16 +9176,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.2.2", + "version": "10.3.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1ab521b24b88b88310c40c26c0cc4a94ba40ff95" + "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1ab521b24b88b88310c40c26c0cc4a94ba40ff95", - "reference": "1ab521b24b88b88310c40c26c0cc4a94ba40ff95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503", + "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503", "shasum": "" }, "require": { @@ -9108,7 +9199,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.1", + "phpunit/php-code-coverage": "^10.1.5", "phpunit/php-file-iterator": "^4.0", "phpunit/php-invoker": "^4.0", "phpunit/php-text-template": "^3.0", @@ -9118,8 +9209,8 @@ "sebastian/comparator": "^5.0", "sebastian/diff": "^5.0", "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.0", - "sebastian/global-state": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", "sebastian/object-enumerator": "^5.0", "sebastian/recursion-context": "^5.0", "sebastian/type": "^4.0", @@ -9134,7 +9225,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.2-dev" + "dev-main": "10.3-dev" } }, "autoload": { @@ -9166,7 +9257,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.2" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5" }, "funding": [ { @@ -9182,7 +9273,7 @@ "type": "tidelift" } ], - "time": "2023-06-11T06:15:20+00:00" + "time": "2023-09-19T05:42:37+00:00" }, { "name": "pimple/pimple", @@ -9406,16 +9497,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -9426,7 +9517,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -9470,7 +9561,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -9478,20 +9570,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:16+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/e67d240970c9dc7ea7b2123a6d520e334dd61dc6", - "reference": "e67d240970c9dc7ea7b2123a6d520e334dd61dc6", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -9504,7 +9596,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -9527,7 +9619,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.0" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -9535,7 +9628,7 @@ "type": "github" } ], - "time": "2023-02-03T06:59:47+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -9670,16 +9763,16 @@ }, { "name": "sebastian/exporter", - "version": "5.0.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", - "reference": "f3ec4bf931c0b31e5b413f5b4fc970a7d03338c0", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -9693,7 +9786,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -9735,7 +9828,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -9743,20 +9837,20 @@ "type": "github" } ], - "time": "2023-02-03T07:06:49+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", - "version": "6.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "aab257c712de87b90194febd52e4d184551c2d44" + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/aab257c712de87b90194febd52e4d184551c2d44", - "reference": "aab257c712de87b90194febd52e4d184551c2d44", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", "shasum": "" }, "require": { @@ -9796,7 +9890,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.0" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" }, "funding": [ { @@ -9804,20 +9899,20 @@ "type": "github" } ], - "time": "2023-02-03T07:07:38+00:00" + "time": "2023-07-19T07:19:23+00:00" }, { "name": "sebastian/lines-of-code", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130" + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/17c4d940ecafb3d15d2cf916f4108f664e28b130", - "reference": "17c4d940ecafb3d15d2cf916f4108f664e28b130", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", "shasum": "" }, "require": { @@ -9853,7 +9948,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.0" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" }, "funding": [ { @@ -9861,7 +9957,7 @@ "type": "github" } ], - "time": "2023-02-03T07:08:02+00:00" + "time": "2023-08-31T09:25:50+00:00" }, { "name": "sebastian/object-enumerator", @@ -10149,16 +10245,16 @@ }, { "name": "spatie/backtrace", - "version": "1.4.1", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "47794d19e3215ace9e005a8f200cd7cc7be52572" + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/47794d19e3215ace9e005a8f200cd7cc7be52572", - "reference": "47794d19e3215ace9e005a8f200cd7cc7be52572", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/483f76a82964a0431aa836b6ed0edde0c248e3ab", + "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab", "shasum": "" }, "require": { @@ -10195,7 +10291,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.4.1" + "source": "https://github.com/spatie/backtrace/tree/1.5.3" }, "funding": [ { @@ -10207,26 +10303,27 @@ "type": "other" } ], - "time": "2023-06-13T14:35:04+00:00" + "time": "2023-06-28T12:59:17+00:00" }, { "name": "spatie/flare-client-php", - "version": "1.3.6", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "530ac81255af79f114344286e4275f8869c671e2" + "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/530ac81255af79f114344286e4275f8869c671e2", - "reference": "530ac81255af79f114344286e4275f8869c671e2", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/5f2c6a7a0d2c1d90c12559dc7828fd942911a544", + "reference": "5f2c6a7a0d2c1d90c12559dc7828fd942911a544", "shasum": "" }, "require": { "illuminate/pipeline": "^8.0|^9.0|^10.0", + "nesbot/carbon": "^2.62.1", "php": "^8.0", - "spatie/backtrace": "^1.2", + "spatie/backtrace": "^1.5.2", "symfony/http-foundation": "^5.0|^6.0", "symfony/mime": "^5.2|^6.0", "symfony/process": "^5.2|^6.0", @@ -10243,7 +10340,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.1.x-dev" + "dev-main": "1.3.x-dev" } }, "autoload": { @@ -10268,7 +10365,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.3.6" + "source": "https://github.com/spatie/flare-client-php/tree/1.4.2" }, "funding": [ { @@ -10276,28 +10373,28 @@ "type": "github" } ], - "time": "2023-04-12T07:57:12+00:00" + "time": "2023-07-28T08:07:24+00:00" }, { "name": "spatie/ignition", - "version": "1.8.1", + "version": "1.11.2", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e" + "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/d8eb8ea1ed27f48a694405cff363746ffd37f13e", - "reference": "d8eb8ea1ed27f48a694405cff363746ffd37f13e", + "url": "https://api.github.com/repos/spatie/ignition/zipball/48b23411ca4bfbc75c75dfc638b6b36159c375aa", + "reference": "48b23411ca4bfbc75c75dfc638b6b36159c375aa", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", "php": "^8.0", - "spatie/backtrace": "^1.4", - "spatie/flare-client-php": "^1.1", + "spatie/backtrace": "^1.5.3", + "spatie/flare-client-php": "^1.4.0", "symfony/console": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0" }, @@ -10309,7 +10406,7 @@ "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "psr/simple-cache-implementation": "*", - "symfony/cache": "^6.2", + "symfony/cache": "^6.0", "symfony/process": "^5.4|^6.0", "vlucas/phpdotenv": "^5.5" }, @@ -10359,20 +10456,20 @@ "type": "github" } ], - "time": "2023-06-06T14:14:58+00:00" + "time": "2023-09-19T15:29:52+00:00" }, { "name": "spatie/laravel-ignition", - "version": "2.1.3", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25" + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/35711943d4725aa80f8033e4f1cb3a6775530b25", - "reference": "35711943d4725aa80f8033e4f1cb3a6775530b25", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", "shasum": "" }, "require": { @@ -10382,7 +10479,7 @@ "illuminate/support": "^10.0", "php": "^8.1", "spatie/flare-client-php": "^1.3.5", - "spatie/ignition": "^1.5.0", + "spatie/ignition": "^1.9", "symfony/console": "^6.2.3", "symfony/var-dumper": "^6.2.3" }, @@ -10451,20 +10548,20 @@ "type": "github" } ], - "time": "2023-05-25T11:30:27+00:00" + "time": "2023-08-23T06:24:34+00:00" }, { "name": "spatie/laravel-ray", - "version": "1.32.5", + "version": "1.33.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ray.git", - "reference": "288f30c94c9725dfd78d8a1b82b230521f4d697e" + "reference": "5028ae44a09451b26eb44490e3471998650788e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/288f30c94c9725dfd78d8a1b82b230521f4d697e", - "reference": "288f30c94c9725dfd78d8a1b82b230521f4d697e", + "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/5028ae44a09451b26eb44490e3471998650788e3", + "reference": "5028ae44a09451b26eb44490e3471998650788e3", "shasum": "" }, "require": { @@ -10524,7 +10621,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-ray/issues", - "source": "https://github.com/spatie/laravel-ray/tree/1.32.5" + "source": "https://github.com/spatie/laravel-ray/tree/1.33.0" }, "funding": [ { @@ -10536,7 +10633,7 @@ "type": "other" } ], - "time": "2023-06-23T07:04:32+00:00" + "time": "2023-09-04T10:16:53+00:00" }, { "name": "spatie/macroable", @@ -10590,16 +10687,16 @@ }, { "name": "spatie/ray", - "version": "1.37.2", + "version": "1.39.0", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "dea16182d4bc9d9833adec7e39fbb3d7b553425d" + "reference": "7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/dea16182d4bc9d9833adec7e39fbb3d7b553425d", - "reference": "dea16182d4bc9d9833adec7e39fbb3d7b553425d", + "url": "https://api.github.com/repos/spatie/ray/zipball/7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75", + "reference": "7ab6bd01dc6a8ecdd836b3182d40a04308ae0c75", "shasum": "" }, "require": { @@ -10650,7 +10747,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.37.2" + "source": "https://github.com/spatie/ray/tree/1.39.0" }, "funding": [ { @@ -10662,20 +10759,20 @@ "type": "other" } ], - "time": "2023-05-17T06:35:47+00:00" + "time": "2023-09-18T10:36:07+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "927013f3aac555983a5059aada98e1907d842695" + "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/927013f3aac555983a5059aada98e1907d842695", - "reference": "927013f3aac555983a5059aada98e1907d842695", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6de50471469b8c9afc38164452ab2b6170ee71c1", + "reference": "6de50471469b8c9afc38164452ab2b6170ee71c1", "shasum": "" }, "require": { @@ -10690,7 +10787,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -10729,7 +10826,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.28.0" }, "funding": [ { @@ -10745,7 +10842,7 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/stopwatch", @@ -11076,5 +11173,5 @@ "php": "^8.1" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/config/hashing.php b/config/hashing.php index 8425770..7fe643d 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -29,7 +29,7 @@ return [ */ 'bcrypt' => [ - 'rounds' => env('BCRYPT_ROUNDS', 10), + 'rounds' => env('BCRYPT_ROUNDS', 12), ], /* diff --git a/config/sanctum.php b/config/sanctum.php index 54b5c06..007e101 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -49,6 +49,21 @@ return [ 'expiration' => null, + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of various + | security scanning initiaives maintained by open source platforms + | that alert 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 diff --git a/config/webauthn.php b/config/webauthn.php index e935b3b..9691389 100644 --- a/config/webauthn.php +++ b/config/webauthn.php @@ -39,7 +39,7 @@ return [ | */ - 'username' => 'email', + 'username' => 'username', /* |-------------------------------------------------------------------------- @@ -109,7 +109,7 @@ return [ */ 'redirects' => [ - 'login' => null, + 'login' => '/', 'register' => '/settings', ], @@ -127,8 +127,8 @@ return [ */ 'views' => [ - 'authenticate' => 'webauthn::authenticate', - 'register' => 'webauthn::register', + 'authenticate' => 'vendor.webauthn.authenticate', + 'register' => 'vendor.webauthn.register', ], /* @@ -228,7 +228,7 @@ return [ \Cose\Algorithms::COSE_ALGORITHM_ES256, // ECDSA with SHA-256 \Cose\Algorithms::COSE_ALGORITHM_ES512, // ECDSA with SHA-512 \Cose\Algorithms::COSE_ALGORITHM_RS256, // RSASSA-PKCS1-v1_5 with SHA-256 - \Cose\Algorithms::COSE_ALGORITHM_EdDSA, // EdDSA + \Cose\Algorithms::COSE_ALGORITHM_EDDSA, // EDDSA \Cose\Algorithms::COSE_ALGORITHM_ES384, // ECDSA with SHA-384 ], @@ -264,7 +264,7 @@ return [ | */ - 'user_verification' => 'preferred', + 'user_verification' => 'discouraged', /* |-------------------------------------------------------------------------- diff --git a/config/ziggy.php b/config/ziggy.php new file mode 100644 index 0000000..27239c4 --- /dev/null +++ b/config/ziggy.php @@ -0,0 +1,20 @@ + [ + 'dashboard.*', + 'aliases.*', + 'recipients.*', + 'domains.*', + 'usernames.*', + 'domains.*', + 'failed_deliveries.*', + 'rules.*', + 'settings.*', + 'webauthn.create', + 'verification.notice', + 'verification.resend', + 'logout', + 'account.destroy', + ], +]; diff --git a/database/factories/UsernameFactory.php b/database/factories/UsernameFactory.php index 59b8c76..a23f65e 100644 --- a/database/factories/UsernameFactory.php +++ b/database/factories/UsernameFactory.php @@ -23,7 +23,7 @@ class UsernameFactory extends Factory { return [ 'user_id' => $this->faker->uuid, - 'username' => $this->faker->userName.$this->faker->randomNumber(3), + 'username' => $this->faker->word().$this->faker->randomNumber(3), ]; } } diff --git a/database/migrations/2022_11_11_113130_add_defer_columns_to_users_table.php b/database/migrations/2022_11_11_113130_add_defer_columns_to_users_table.php new file mode 100644 index 0000000..bf87077 --- /dev/null +++ b/database/migrations/2022_11_11_113130_add_defer_columns_to_users_table.php @@ -0,0 +1,34 @@ +timestamp('defer_new_aliases_until')->after('bandwidth')->nullable(); + $table->timestamp('defer_until')->after('bandwidth')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('defer_until'); + $table->dropColumn('defer_new_aliases_until'); + }); + } +}; diff --git a/database/migrations/2022_11_21_164512_add_reject_until_column_to_users_table.php b/database/migrations/2022_11_21_164512_add_reject_until_column_to_users_table.php new file mode 100644 index 0000000..89945ed --- /dev/null +++ b/database/migrations/2022_11_21_164512_add_reject_until_column_to_users_table.php @@ -0,0 +1,32 @@ +timestamp('reject_until')->after('bandwidth')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('reject_until'); + }); + } +}; diff --git a/database/migrations/2022_11_24_135311_create_outbound_messages_table.php b/database/migrations/2022_11_24_135311_create_outbound_messages_table.php new file mode 100644 index 0000000..8f6251f --- /dev/null +++ b/database/migrations/2022_11_24_135311_create_outbound_messages_table.php @@ -0,0 +1,39 @@ +string('id', 12); + $table->uuid('user_id'); + $table->uuid('alias_id')->nullable(); + $table->uuid('recipient_id')->nullable(); + $table->string('email_type', 5); + $table->boolean('bounced')->default(false); + $table->timestamps(); + + $table->primary('id'); + $table->index('user_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('outbound_messages'); + } +}; diff --git a/database/migrations/2023_01_20_111533_add_destination_to_failed_deliveries_table.php b/database/migrations/2023_01_20_111533_add_destination_to_failed_deliveries_table.php new file mode 100644 index 0000000..6f2e01c --- /dev/null +++ b/database/migrations/2023_01_20_111533_add_destination_to_failed_deliveries_table.php @@ -0,0 +1,32 @@ +text('destination')->after('sender')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('failed_deliveries', function (Blueprint $table) { + $table->dropColumn('destination'); + }); + } +}; diff --git a/database/migrations/2023_01_31_110006_drop_postfix_queue_ids_table.php b/database/migrations/2023_01_31_110006_drop_postfix_queue_ids_table.php new file mode 100644 index 0000000..159e416 --- /dev/null +++ b/database/migrations/2023_01_31_110006_drop_postfix_queue_ids_table.php @@ -0,0 +1,34 @@ +uuid('id'); + $table->string('queue_id', 20)->unique(); + + $table->timestamps(); + $table->primary('id'); + }); + } +}; diff --git a/database/migrations/2023_03_24_151053_add_index_for_aliasable_id_to_aliases_table.php b/database/migrations/2023_03_24_151053_add_index_for_aliasable_id_to_aliases_table.php new file mode 100644 index 0000000..f9a0cba --- /dev/null +++ b/database/migrations/2023_03_24_151053_add_index_for_aliasable_id_to_aliases_table.php @@ -0,0 +1,32 @@ +index('aliasable_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('aliases', function (Blueprint $table) { + $table->dropIndex('aliases_aliasable_id_index'); + }); + } +}; diff --git a/database/migrations/2023_05_31_131832_add_from_name_columns.php b/database/migrations/2023_05_31_131832_add_from_name_columns.php new file mode 100644 index 0000000..be3a91a --- /dev/null +++ b/database/migrations/2023_05_31_131832_add_from_name_columns.php @@ -0,0 +1,44 @@ +text('from_name')->after('description')->nullable(); + }); + + Schema::table('usernames', function (Blueprint $table) { + $table->text('from_name')->after('description')->nullable(); + }); + + Schema::table('domains', function (Blueprint $table) { + $table->text('from_name')->after('description')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('aliases', function (Blueprint $table) { + $table->dropColumn('from_name'); + }); + + Schema::table('usernames', function (Blueprint $table) { + $table->dropColumn('from_name'); + }); + + Schema::table('domains', function (Blueprint $table) { + $table->dropColumn('from_name'); + }); + } +}; diff --git a/database/migrations/2023_06_05_111322_add_pending_column_to_recipients_table.php b/database/migrations/2023_06_05_111322_add_pending_column_to_recipients_table.php new file mode 100644 index 0000000..608b635 --- /dev/null +++ b/database/migrations/2023_06_05_111322_add_pending_column_to_recipients_table.php @@ -0,0 +1,28 @@ +boolean('pending')->default(false)->after('fingerprint'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('recipients', function (Blueprint $table) { + $table->dropColumn('pending'); + }); + } +}; diff --git a/database/migrations/2023_06_29_155657_add_can_login_to_usernames_table.php b/database/migrations/2023_06_29_155657_add_can_login_to_usernames_table.php new file mode 100644 index 0000000..773001b --- /dev/null +++ b/database/migrations/2023_06_29_155657_add_can_login_to_usernames_table.php @@ -0,0 +1,28 @@ +boolean('can_login')->after('catch_all')->default(true); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('usernames', function (Blueprint $table) { + $table->dropColumn('can_login'); + }); + } +}; diff --git a/database/migrations/2023_06_30_101305_add_display_from_format_to_users_table.php b/database/migrations/2023_06_30_101305_add_display_from_format_to_users_table.php new file mode 100644 index 0000000..f146bf2 --- /dev/null +++ b/database/migrations/2023_06_30_101305_add_display_from_format_to_users_table.php @@ -0,0 +1,29 @@ +unsignedTinyInteger('display_from_format')->after('banner_location')->default(0); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('display_from_format'); + }); + } +}; diff --git a/package-lock.json b/package-lock.json index dff644a..06cd98c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,19 @@ { - "name": "anonaddy-sh", + "name": "addy-sh", "lockfileVersion": 3, "requires": true, "packages": { "": { "dependencies": { "@headlessui/vue": "^1.6.7", - "@kyvg/vue3-notification": "^2.3.1", + "@heroicons/vue": "^2.0.18", + "@inertiajs/vue3": "^1.0.11", + "@kyvg/vue3-notification": "^3.0.0", + "@tailwindcss/forms": "^0.5.3", "@vueform/multiselect": "^2.3.2", "autoprefixer": "^10.4.1", "axios": "^1.0.0", + "chart.js": "^4.4.0", "cross-env": "^7.0.3", "dayjs": "^1.10.4", "laravel-mix": "^6.0.11", @@ -20,18 +24,21 @@ "resolve-url-loader": "^5.0.0", "tailwindcss": "^3.0.11", "tippy.js": "^6.2.7", - "v-clipboard": "^3.0.0-next.1", "vue": "^3.0.0", + "vue-chartjs": "^5.2.0", "vue-good-table-next": "^0.2.1", "vue-loader": "^17.0.0", "vue-template-compiler": "^2.6.12", "vuedraggable": "^4.1.0" }, "devDependencies": { + "@vitejs/plugin-vue": "^4.2.3", "css-loader": "^6.0.0", "husky": "^8.0.0", - "lint-staged": "^13.0.0", - "prettier": "^2.2.1" + "laravel-vite-plugin": "^0.8.0", + "lint-staged": "^14.0.0", + "prettier": "^3.0.0", + "vite": "^4.3.6" } }, "node_modules/@alloc/quick-lru": { @@ -58,44 +65,101 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", - "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -106,19 +170,19 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -139,56 +203,53 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", - "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", - "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", - "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "semver": "^6.3.0" + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -198,21 +259,21 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz", - "integrity": "sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -222,52 +283,43 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz", - "integrity": "sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -285,43 +337,43 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -344,14 +396,13 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz", - "integrity": "sha512-cU0Sq1Rf4Z55fgz7haOakIyM7+x/uCFwXpLPaeRzfoUtAEAuUZjZvFPjL/rk5rW693dIgn2hng1W7xbT7lWT4g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -361,19 +412,19 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", - "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { @@ -399,9 +450,9 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { "@babel/types": "^7.22.5" }, @@ -418,55 +469,54 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz", - "integrity": "sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "dependencies": { "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -530,9 +580,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -541,9 +591,9 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -555,13 +605,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" + "@babel/plugin-transform-optional-chaining": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -574,6 +624,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -599,21 +650,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -846,13 +882,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.5.tgz", - "integrity": "sha512-gGOEvFzm3fWoyD5uZq7vVTD57pPJ3PczPUD/xCFGjzBpUosnklmXyKnGQbbbGs1NPNPskFex0j93yKbHt0cHyg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", + "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -893,9 +929,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", - "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", + "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -922,11 +958,11 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", - "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -938,18 +974,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz", - "integrity": "sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "engines": { @@ -975,9 +1011,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", - "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", + "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1018,9 +1054,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", - "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -1048,9 +1084,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", - "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -1063,9 +1099,9 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1093,9 +1129,9 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", - "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -1122,9 +1158,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", - "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -1151,11 +1187,11 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", + "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1166,11 +1202,11 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", - "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", + "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -1182,14 +1218,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", - "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", + "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -1243,9 +1279,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", - "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -1258,9 +1294,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", - "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -1273,15 +1309,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", - "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" + "@babel/plugin-transform-parameters": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1306,9 +1342,9 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", - "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -1321,9 +1357,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.5.tgz", - "integrity": "sha512-AconbMKOMkyG+xCng2JogMCDcqW8wedQAqpVIL4cOSescZ7+iW8utC6YDZLMCSUIReEA733gzRSaOSXMAt/4WQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", + "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -1337,9 +1373,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1366,12 +1402,12 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", - "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -1397,12 +1433,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", - "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.1" + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -1426,16 +1462,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.5.tgz", - "integrity": "sha512-bg4Wxd1FWeFx3daHFTWk1pkSWK/AyQuiyAoeZAOkAOUBjnZPH6KT7eMxouV47tQ6hl6ax2zyAWBdWZXbrvXlaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz", + "integrity": "sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g==", "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.3", - "babel-plugin-polyfill-corejs3": "^0.8.1", - "babel-plugin-polyfill-regenerator": "^0.5.0", - "semver": "^6.3.0" + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1445,9 +1481,9 @@ } }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -1524,9 +1560,9 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", - "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1583,16 +1619,16 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.5.tgz", - "integrity": "sha512-fj06hw89dpiZzGZtxn+QybifF07nNiZjZ7sazs2aVDcysAZVGjW7+7iFYxg6GLNM47R/thYfLdrXc+2f11Vi9A==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", + "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", "dependencies": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", + "@babel/compat-data": "^7.22.20", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1613,60 +1649,60 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.15", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.15", "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.15", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.15", + "@babel/plugin-transform-modules-systemjs": "^7.22.11", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5", - "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", "@babel/plugin-transform-reserved-words": "^7.22.5", "@babel/plugin-transform-shorthand-properties": "^7.22.5", "@babel/plugin-transform-spread": "^7.22.5", "@babel/plugin-transform-sticky-regex": "^7.22.5", "@babel/plugin-transform-template-literals": "^7.22.5", "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", "@babel/plugin-transform-unicode-property-regex": "^7.22.5", "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.3", - "babel-plugin-polyfill-corejs3": "^0.8.1", - "babel-plugin-polyfill-regenerator": "^0.5.0", - "core-js-compat": "^3.30.2", - "semver": "^6.3.0" + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.19", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -1676,26 +1712,24 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/regjsgen": { @@ -1704,42 +1738,42 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", - "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1748,12 +1782,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1777,10 +1811,362 @@ "node": ">=10.0.0" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@headlessui/vue": { - "version": "1.7.14", - "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.14.tgz", - "integrity": "sha512-aL9U9Sa7wdOzlrfjx6EjMIYNRCma5mngWcWzQBcHFwznpRZ8g/QZ/AYFtRDrZZUw22Ttttja4D7ZRXFwhONewA==", + "version": "1.7.16", + "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.16.tgz", + "integrity": "sha512-nKT+nf/q6x198SsyK54mSszaQl/z+QxtASmgMEJtpxSX2Q0OPJX0upS/9daDyiECpeAsvjkoOrm2O/6PyBQ+Qg==", "engines": { "node": ">=10" }, @@ -1788,6 +2174,38 @@ "vue": "^3.2.0" } }, + "node_modules/@heroicons/vue": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-2.0.18.tgz", + "integrity": "sha512-BcTC9nq2TkwNSfQuqo96J7ehx4etezypc2YeTq7KsXWxrcrerhkgjLrxGRBnStN0wrXo0Gv4BInybqz5uBG6Cw==", + "peerDependencies": { + "vue": ">= 3" + } + }, + "node_modules/@inertiajs/core": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-1.0.11.tgz", + "integrity": "sha512-EFUvVsq8TvvIaUDrfbL/pvEBvu67gdBvV/cyepDZqCdAolld0N3AO+TZ7i7UtwKwLKw8eAFbixgbkicugojhGA==", + "dependencies": { + "axios": "^1.2.0", + "deepmerge": "^4.0.0", + "nprogress": "^0.2.0", + "qs": "^6.9.0" + } + }, + "node_modules/@inertiajs/vue3": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@inertiajs/vue3/-/vue3-1.0.11.tgz", + "integrity": "sha512-4lqOsXWXxbeqq5dXZhpNSFabNfxHItHjc005+dqd+cKbhZ5n+dr8n2IGSDBfMNcT7bJfDUyUS2XKdY5y1EnAuw==", + "dependencies": { + "@inertiajs/core": "1.0.11", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1802,9 +2220,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "engines": { "node": ">=6.0.0" } @@ -1818,9 +2236,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -1832,23 +2250,23 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" }, "node_modules/@kyvg/vue3-notification": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@kyvg/vue3-notification/-/vue3-notification-2.9.1.tgz", - "integrity": "sha512-FsY8g25tQetr3etnarxHtCeNFKssH8sheFu13LyL2JJmOOel437QqKV5n4RBDDDTIo55iKgIVYXeojliXYdEhw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@kyvg/vue3-notification/-/vue3-notification-3.0.2.tgz", + "integrity": "sha512-t6PWPWggVqehX0BJZbqOttfKe3oUOrkgYoNQWSx8gwz8+pIRygQNT5MrewaIATdiU3bf//Yyto/wEm7vYl17Uw==", "peerDependencies": { "vue": "^3.0.0" } @@ -1899,6 +2317,17 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz", + "integrity": "sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -1908,9 +2337,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -1920,100 +2349,100 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", "dependencies": { "@babel/types": "^7.20.7" } }, "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", + "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.11.tgz", + "integrity": "sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/clean-css": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.6.tgz", - "integrity": "sha512-Ze1tf+LnGPmG6hBFMi0B4TEB0mhF7EiMM5oyjLDNPE9hxrPU0W+5+bHvO+eFPA+bt0iC1zkQMoU/iGdRVjcRbw==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.7.tgz", + "integrity": "sha512-lcoZHjUAANLTACLGi+O/0pN+oKQAQ8zAMWJSxiBRNLxqZG/WE8hfXJUs1eYwJOvOnDJrvxU1kR77UiVJ3+9N0Q==", "dependencies": { "@types/node": "*", "source-map": "^0.6.0" } }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", + "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", + "integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", + "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==" }, "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.18.tgz", + "integrity": "sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2022,9 +2451,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "version": "4.17.37", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", + "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -2042,14 +2471,14 @@ } }, "node_modules/@types/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", + "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==" }, "node_modules/@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "version": "1.17.12", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", + "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", "dependencies": { "@types/node": "*" } @@ -2063,25 +2492,25 @@ } }, "node_modules/@types/imagemin-gifsicle": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.1.tgz", - "integrity": "sha512-kUz6sUh0P95JOS0RGEaaemWUrASuw+dLsWIveK2UZJx74id/B9epgblMkCk/r5MjUWbZ83wFvacG5Rb/f97gyA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/imagemin-gifsicle/-/imagemin-gifsicle-7.0.2.tgz", + "integrity": "sha512-dxcBszAMvcyH1Ao4vQpT5B9lZEleYNoRN4FdiSmXLjKu3B33cZerUPR34nZ0FXkIfo5PECkRjuVAvQWvd9g+NA==", "dependencies": { "@types/imagemin": "*" } }, "node_modules/@types/imagemin-mozjpeg": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.1.tgz", - "integrity": "sha512-kMQWEoKxxhlnH4POI3qfW9DjXlQfi80ux3l2b3j5R3eudSCoUIzKQLkfMjNJ6eMYnMWBcB+rfQOWqIzdIwFGKw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/imagemin-mozjpeg/-/imagemin-mozjpeg-8.0.2.tgz", + "integrity": "sha512-jcCp5KGlmJHpvANnymFraVFjtOhErSKDfByzX1tBJqLsuCEO9JOhTVMC/tzfMyroJzWgs4yl8BYYpzzZgA/Pqw==", "dependencies": { "@types/imagemin": "*" } }, "node_modules/@types/imagemin-optipng": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.1.tgz", - "integrity": "sha512-XCM/3q+HUL7v4zOqMI+dJ5dTxT+MUukY9KU49DSnYb/4yWtSMHJyADP+WHSMVzTR63J2ZvfUOzSilzBNEQW78g==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/imagemin-optipng/-/imagemin-optipng-5.2.2.tgz", + "integrity": "sha512-BaKY+jrzKaFM3mPc0pUwfJOhoZAu9ZfMOmcKt4EXsmtqGm9otu9y+xJUmaZ/N60dJOTGZypOByXLAUyjsXEVeA==", "dependencies": { "@types/imagemin": "*" } @@ -2096,14 +2525,14 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==" }, "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", + "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" }, "node_modules/@types/minimatch": { "version": "5.1.2", @@ -2111,9 +2540,9 @@ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" }, "node_modules/@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" + "version": "20.8.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", + "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -2121,14 +2550,14 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.8", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", + "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==" }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", + "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==" }, "node_modules/@types/retry": { "version": "0.12.0", @@ -2136,26 +2565,26 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", + "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==", "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", - "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", + "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -2163,9 +2592,9 @@ } }, "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.34", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.34.tgz", + "integrity": "sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==", "dependencies": { "@types/node": "*" } @@ -2176,13 +2605,26 @@ "integrity": "sha512-AZU7vQcy/4WFEuwnwsNsJnFwupIpbllH1++LXScN6uxT1Z4zPzdrWG97w4/I7eFKFTvfy/bHFStWjdBAg2Vjug==" }, "node_modules/@types/ws": { - "version": "8.5.5", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", - "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.6.tgz", + "integrity": "sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==", "dependencies": { "@types/node": "*" } }, + "node_modules/@vitejs/plugin-vue": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz", + "integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0", + "vue": "^3.2.25" + } + }, "node_modules/@vue/compiler-core": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", @@ -2477,9 +2919,9 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "bin": { "acorn": "bin/acorn" }, @@ -2507,19 +2949,6 @@ "node": ">=8.9" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2580,15 +3009,15 @@ } }, "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "dependencies": { - "type-fest": "^0.21.3" + "type-fest": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2679,34 +3108,25 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", + "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", "dependencies": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "object.assign": "^4.1.4", + "util": "^0.10.4" } }, "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", "dependencies": { - "inherits": "2.0.1" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" + "inherits": "2.0.3" } }, "node_modules/asynckit": { @@ -2715,9 +3135,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "funding": [ { "type": "opencollective", @@ -2726,12 +3146,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -2747,9 +3171,9 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -2775,47 +3199,47 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz", - "integrity": "sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.4.0", - "semver": "^6.1.1" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz", - "integrity": "sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz", + "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.0", - "core-js-compat": "^3.30.1" + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.32.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz", - "integrity": "sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.4.0" + "@babel/helper-define-polyfill-provider": "^0.4.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/balanced-match": { @@ -3048,9 +3472,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "funding": [ { "type": "opencollective", @@ -3066,10 +3490,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -3160,9 +3584,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001508", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", - "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", + "version": "1.0.30001543", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001543.tgz", + "integrity": "sha512-qxdO8KPWPQ+Zk6bvNpPeQIOH47qZSYdFZd6dXQzb2KzhnSXju4Kd7H1PkSJx6NICSMgo/IhRZRhhfPTHYpJUCA==", "funding": [ { "type": "opencollective", @@ -3201,6 +3625,17 @@ "node": "*" } }, + "node_modules/chart.js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3255,25 +3690,19 @@ "node": ">= 10.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-table3": { @@ -3369,6 +3798,22 @@ "node": ">=12" } }, + "node_modules/cliui/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==", + "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/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -3550,9 +3995,9 @@ } }, "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { "version": "0.5.0", @@ -3568,11 +4013,11 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js-compat": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.0.tgz", - "integrity": "sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw==", + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.0.tgz", + "integrity": "sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==", "dependencies": { - "browserslist": "^4.21.5" + "browserslist": "^4.22.1" }, "funding": { "type": "opencollective", @@ -3698,9 +4143,9 @@ } }, "node_modules/css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", "engines": { "node": "^10 || ^12 || >=14" }, @@ -3902,9 +4347,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.8", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", - "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, "node_modules/de-indent": { "version": "1.0.2", @@ -3927,6 +4372,14 @@ } } }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -4020,6 +4473,19 @@ "node": ">=6" } }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -4028,6 +4494,22 @@ "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4109,9 +4591,9 @@ "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" }, "node_modules/dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -4241,9 +4723,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.440", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", - "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==" + "version": "1.4.540", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.540.tgz", + "integrity": "sha512-aoCqgU6r9+o9/S7wkcSbmPRFi7OWZWiXS9rtjEd+Ouyu/Xyw5RSq2XN8s5Qp8IaFOLiRrhQCphCIjAxgG3eCAg==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -4306,9 +4788,9 @@ } }, "node_modules/envinfo": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.9.0.tgz", - "integrity": "sha512-RODB4txU+xImYDemN5DqaKC0CHk05XSVkOX4pq0hK26Qx+1LChkuOyUDlGEjYb3ACr0n9qBhFjg37hQuJvpkRQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz", + "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==", "bin": { "envinfo": "dist/cli.js" }, @@ -4325,9 +4807,46 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", - "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } }, "node_modules/escalade": { "version": "3.1.1", @@ -4411,9 +4930,10 @@ } }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true }, "node_modules/events": { "version": "3.3.0", @@ -4433,9 +4953,9 @@ } }, "node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -4534,9 +5054,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4694,9 +5214,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -4734,15 +5254,15 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.6.tgz", + "integrity": "sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==", "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { @@ -4767,9 +5287,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", - "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -4777,9 +5297,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -4896,6 +5416,17 @@ "node": ">=8" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "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", @@ -4912,12 +5443,9 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "engines": { "node": ">= 0.4.0" } @@ -4930,6 +5458,17 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", @@ -5206,6 +5745,11 @@ } } }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -5372,15 +5916,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5433,9 +5968,9 @@ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dependencies": { "has": "^1.0.3" }, @@ -5587,9 +6122,9 @@ } }, "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", + "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", "bin": { "jiti": "bin/jiti.js" } @@ -5784,6 +6319,22 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/laravel-vite-plugin": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.1.tgz", + "integrity": "sha512-fxzUDjOA37kOsYq8dP+3oPIlw8/kJVXwu0hOXLun82R1LpV02shGeWGYKx2lbpKffL5I0sfPPjfqbYxuqBluAA==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.0.5" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0" + } + }, "node_modules/launch-editor": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", @@ -5807,39 +6358,36 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/lint-staged": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz", - "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", + "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", "dev": true, "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -5849,12 +6397,12 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/lint-staged/node_modules/yaml": { @@ -5867,22 +6415,20 @@ } }, "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", "dev": true, "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.1.0" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" @@ -5893,45 +6439,6 @@ } } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -5969,6 +6476,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -5990,61 +6502,49 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/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==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/lower-case": { @@ -6064,11 +6564,11 @@ } }, "node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "version": "0.30.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.4.tgz", + "integrity": "sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -6089,9 +6589,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -6429,9 +6929,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6487,6 +6987,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6522,6 +7027,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -6615,21 +7145,6 @@ "node": ">=8" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-pipe": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", @@ -6850,9 +7365,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -7428,15 +7943,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -7662,9 +8177,9 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dependencies": { "regenerate": "^1.4.2" }, @@ -7673,14 +8188,14 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -7763,11 +8278,11 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7820,17 +8335,25 @@ "node": ">=12" } }, + "node_modules/resolve-url-loader/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/restore-cursor/node_modules/mimic-fn": { @@ -7903,6 +8426,22 @@ "inherits": "^2.0.1" } }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7925,15 +8464,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7992,9 +8522,9 @@ } }, "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -8374,9 +8904,9 @@ } }, "node_modules/std-env": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", - "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==" + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" }, "node_modules/stream-browserify": { "version": "2.0.2", @@ -8512,9 +9042,9 @@ } }, "node_modules/sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -8602,9 +9132,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -8626,7 +9156,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -8678,9 +9207,9 @@ } }, "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", "engines": { "node": ">= 14" } @@ -8694,9 +9223,9 @@ } }, "node_modules/terser": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", - "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", + "integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -8784,12 +9313,6 @@ "node": ">=0.8" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -8852,9 +9375,9 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tty-browserify": { "version": "0.0.0", @@ -8862,9 +9385,9 @@ "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==" }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, "engines": { "node": ">=10" @@ -8938,9 +9461,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "funding": [ { "type": "opencollective", @@ -8983,12 +9506,12 @@ } }, "node_modules/url": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz", - "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==", + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", + "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", "dependencies": { "punycode": "^1.4.1", - "qs": "^6.11.0" + "qs": "^6.11.2" } }, "node_modules/util": { @@ -9025,14 +9548,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v-clipboard": { - "version": "3.0.0-next.1", - "resolved": "https://registry.npmjs.org/v-clipboard/-/v-clipboard-3.0.0-next.1.tgz", - "integrity": "sha512-UvCnzetQMlVfk9yoiyew8ldGiCzeER5aYdmXXtZp8LC6rt2QXQS0AayEDn1K7rlXpd3M8d+JeYNUV+ZNgtaS4A==", - "dependencies": { - "vue": "^3.2.45" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -9041,6 +9556,74 @@ "node": ">= 0.8" } }, + "node_modules/vite": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.10.tgz", + "integrity": "sha512-TzIjiqx9BEXF8yzYdF2NTf1kFFbjMjUSV0LFZ3HyHoI3SGSPLnnFUKiIQtL3gl2AjHvMrprOvQ3amzaHgQlAxw==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.0.5.tgz", + "integrity": "sha512-kVZFDFWr0DxiHn6MuDVTQf7gnWIdETGlZh0hvTiMXzRN80vgF4PKbONSq8U1d0WtHsKaFODTQgJeakLacoPZEQ==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + }, + "peerDependencies": { + "vite": "^2 || ^3 || ^4" + } + }, "node_modules/vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -9058,6 +9641,15 @@ "@vue/shared": "3.3.4" } }, + "node_modules/vue-chartjs": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.2.0.tgz", + "integrity": "sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "vue": "^3.0.0-0 || ^2.7.0" + } + }, "node_modules/vue-good-table-next": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/vue-good-table-next/-/vue-good-table-next-0.2.1.tgz", @@ -9167,9 +9759,9 @@ } }, "node_modules/webpack": { - "version": "5.88.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", - "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -9557,30 +10149,93 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==" }, "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==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index d6aaf33..c9e3575 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,22 @@ { "private": true, "scripts": { - "dev": "npm run development", - "development": "mix", - "watch": "mix watch", - "watch-poll": "mix watch -- --watch-options-poll=1000", - "hot": "mix watch --hot", - "prod": "npm run production", - "production": "mix --production", + "dev": "vite --host", + "build": "vite build", "format": "prettier --write 'resources/**/*.{css,js,vue}'", "prepare": "husky install", "pre-commit": "lint-staged" }, "dependencies": { "@headlessui/vue": "^1.6.7", - "@kyvg/vue3-notification": "^2.3.1", + "@heroicons/vue": "^2.0.18", + "@inertiajs/vue3": "^1.0.11", + "@kyvg/vue3-notification": "^3.0.0", + "@tailwindcss/forms": "^0.5.3", "@vueform/multiselect": "^2.3.2", "autoprefixer": "^10.4.1", "axios": "^1.0.0", + "chart.js": "^4.4.0", "cross-env": "^7.0.3", "dayjs": "^1.10.4", "laravel-mix": "^6.0.11", @@ -28,19 +27,22 @@ "resolve-url-loader": "^5.0.0", "tailwindcss": "^3.0.11", "tippy.js": "^6.2.7", - "v-clipboard": "^3.0.0-next.1", "vue": "^3.0.0", + "vue-chartjs": "^5.2.0", "vue-good-table-next": "^0.2.1", "vue-loader": "^17.0.0", "vue-template-compiler": "^2.6.12", "vuedraggable": "^4.1.0" }, "devDependencies": { - "css-loader": "^6.0.0", - "husky": "^8.0.0", - "lint-staged": "^13.0.0", - "prettier": "^2.2.1" - }, + "@vitejs/plugin-vue": "^4.2.3", + "css-loader": "^6.0.0", + "husky": "^8.0.0", + "laravel-vite-plugin": "^0.8.0", + "lint-staged": "^14.0.0", + "prettier": "^3.0.0", + "vite": "^4.3.6" + }, "lint-staged": { "*.{css,js,vue}": [ "prettier --write" diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/postfix/AccessPolicy.php b/postfix/AccessPolicy.php new file mode 100644 index 0000000..8fc3666 --- /dev/null +++ b/postfix/AccessPolicy.php @@ -0,0 +1,352 @@ +allowList([ + 'DB_HOST', + 'DB_PORT', + 'DB_DATABASE', + 'DB_USERNAME', + 'DB_PASSWORD', + 'DB_SOCKET', + 'MYSQL_ATTR_SSL_CA', + 'ACTION_DOES_NOT_EXIST', + 'ACTION_ALIAS_DISCARD', + 'ACTION_USERNAME_DISCARD', + 'ACTION_DOMAIN_DISCARD', + 'ACTION_REJECT', + 'ACTION_DEFER', + 'ACTION_DEFER_NEW', + 'ANONADDY_ALL_DOMAINS', + 'ANONADDY_SECRET', + 'ANONADDY_ADMIN_USERNAME', + ]) + ->make(); + + $dotenv = Dotenv\Dotenv::create($repository, dirname(__DIR__)); + $dotenv->load(); + + $database = new Database(); + + $database->addConnection([ + 'driver' => 'mysql', + 'read' => [ + 'host' => [ + $_ENV['DB_HOST'] ?? '127.0.0.1', + ], + ], + 'write' => [ + 'host' => [ + $_ENV['DB_HOST'] ?? '127.0.0.1', + ], + ], + 'port' => $_ENV['DB_PORT'] ?? '3306', + 'database' => $_ENV['DB_DATABASE'] ?? 'forge', + 'username' => $_ENV['DB_USERNAME'] ?? 'forge', + 'password' => $_ENV['DB_PASSWORD'] ?? '', + 'unix_socket' => $_ENV['DB_SOCKET'] ?? '', + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => $_ENV['MYSQL_ATTR_SSL_CA'] ?? null, + ]) : [], + 'sticky' => true, + ]); + + // Make this instance available globally via static methods + $database->setAsGlobal(); + + // Define actions, these can be overridden by adding the variables to your .env file + // e.g. ACTION_DOES_NOT_EXIST='550 5.1.1 User not found' + define('ACTION_DOES_NOT_EXIST', $_ENV['ACTION_DOES_NOT_EXIST'] ?? '550 5.1.1 Address does not exist'); + define('ACTION_ALIAS_DISCARD', $_ENV['ACTION_ALIAS_DISCARD'] ?? 'DISCARD is inactive alias'); + define('ACTION_USERNAME_DISCARD', $_ENV['ACTION_USERNAME_DISCARD'] ?? 'DISCARD has inactive username'); + define('ACTION_DOMAIN_DISCARD', $_ENV['ACTION_DOMAIN_DISCARD'] ?? 'DISCARD has inactive domain'); + define('ACTION_REJECT', $_ENV['ACTION_REJECT'] ?? '552 5.2.2 User over quota'); + define('ACTION_DEFER', $_ENV['ACTION_DEFER'] ?? '452 4.2.2 User over quota'); + define('ACTION_DEFER_NEW', $_ENV['ACTION_DEFER_NEW'] ?? '450 4.2.1 User over quota'); + + $args = getArgs(); + + $allDomains = explode(',', $_ENV['ANONADDY_ALL_DOMAINS'] ?? ''); + + $adminUsername = $_ENV['ANONADDY_ADMIN_USERNAME'] ?? null; + + $aliasEmail = strtolower($args['recipient']); + + // If no alias email is provided then exit + if (empty($aliasEmail) || empty($allDomains)) { + logData('No alias email or $allDomains not set.'); + exit(0); + } + + //$senderEmail = strtolower($args['sender']); + $aliasDomain = explode('@', $aliasEmail)[1]; + $aliasHasSharedDomain = in_array($aliasDomain, $allDomains); + + // Check if it is a bounce with a valid VERP... + if (substr($aliasEmail, 0, 2) === 'b_') { + if ($outboundMessageId = getIdFromVerp($aliasEmail)) { + // Is a valid bounce + $outboundMessage = Database::table('outbound_messages')->find($outboundMessageId); + + // If there is no outbound message found or no alias_id then reject since we cannot forward this to the user + if (is_null($outboundMessage) || (is_null($outboundMessage?->alias_id) && $outboundMessage->bounced)) { + // Must have been more than 7 days or a notification that has already bounced + sendAction(ACTION_DOES_NOT_EXIST); + } else { + // Allow through, may be an auto-reply etc. + sendAction('DUNNO'); + } + + // exit to prevent running the rest of the script + exit(0); + } + } + + // Check if the alias has a username subdomain + $aliasHasUsernameDomain = ! empty(array_filter($allDomains, fn ($domain) => endsWith($aliasDomain, ".{$domain}"))); + + // If the alias has a plus extension then remove it + if (str_contains($aliasEmail, '+')) { + $aliasEmail = before($aliasEmail, '+').'@'.afterLast($aliasEmail, '@'); + } + + // Check if the alias already exists or not + $noAliasExists = Database::table('aliases')->select('id')->where('email', $aliasEmail)->doesntExist(); + + if ($noAliasExists && $aliasHasSharedDomain) { + // If admin username is set then allow through with catch-all + if ($adminUsername) { + sendAction('DUNNO'); + } else { + sendAction(ACTION_DOES_NOT_EXIST); + } + } else { + $aliasAction = null; + + if (! $noAliasExists) { + $aliasActionQuery = Database::table('aliases') + ->leftJoin('users', 'aliases.user_id', '=', 'users.id') + ->where('aliases.email', $aliasEmail) + ->selectRaw('CASE + WHEN aliases.deleted_at IS NOT NULL THEN ? + WHEN aliases.active = 0 THEN ? + WHEN users.reject_until > NOW() THEN ? + WHEN users.defer_until > NOW() THEN ? + ELSE "DUNNO" + END', [ + ACTION_DOES_NOT_EXIST, + ACTION_ALIAS_DISCARD, + ACTION_REJECT, + ACTION_DEFER, + ]) + ->first(); + + $aliasAction = getAction($aliasActionQuery); + } + + if (in_array($aliasAction, [ACTION_ALIAS_DISCARD, ACTION_DOES_NOT_EXIST])) { + // If the alias is inactive or deleted then increment the blocked count + Database::table('aliases') + ->where('email', $aliasEmail) + ->increment('emails_blocked'); + + sendAction($aliasAction); + } elseif ($aliasHasSharedDomain || in_array($aliasAction, [ACTION_REJECT, ACTION_DEFER])) { + // If the alias has a shared domain then we don't need to check the usernames or domains + + sendAction($aliasAction); + } elseif ($aliasHasUsernameDomain) { + $concatDomainsStatement = array_reduce(array_keys($allDomains), function ($carry, $key) { + $comma = $key === 0 ? '' : ','; + + return "{$carry}{$comma}CONCAT(usernames.username, ?)"; + }, ''); + + $dotDomains = array_map(fn ($domain) => ".{$domain}", $allDomains); + + $usernameActionQuery = Database::table('usernames') + ->leftJoin('users', 'usernames.user_id', '=', 'users.id') + ->whereRaw('? IN ('.$concatDomainsStatement.')', [$aliasDomain, ...$dotDomains]) + ->selectRaw('CASE + WHEN ? AND usernames.catch_all = 0 THEN ? + WHEN usernames.active = 0 THEN ? + WHEN users.reject_until > NOW() THEN ? + WHEN users.defer_until > NOW() THEN ? + WHEN ? AND users.defer_new_aliases_until > NOW() THEN ? + ELSE "DUNNO" + END', [ + $noAliasExists, + ACTION_DOES_NOT_EXIST, + ACTION_USERNAME_DISCARD, + ACTION_REJECT, + ACTION_DEFER, + $noAliasExists, + ACTION_DEFER_NEW, + ]) + ->first(); + + sendAction(getAction($usernameActionQuery)); + } else { + $domainActionQuery = Database::table('domains') + ->leftJoin('users', 'domains.user_id', '=', 'users.id') + ->where('domains.domain', $aliasDomain) + ->selectRaw('CASE + WHEN ? AND domains.catch_all = 0 THEN ? + WHEN domains.active = 0 THEN ? + WHEN users.reject_until > NOW() THEN ? + WHEN users.defer_until > NOW() THEN ? + WHEN ? AND users.defer_new_aliases_until > NOW() THEN ? + ELSE "DUNNO" + END', [ + $noAliasExists, + ACTION_DOES_NOT_EXIST, + ACTION_DOMAIN_DISCARD, + ACTION_REJECT, + ACTION_DEFER, + $noAliasExists, + ACTION_DEFER_NEW, + ]) + ->first(); + + sendAction(getAction($domainActionQuery)); + } + } +} catch (\Exception $e) { + logData($e->getMessage()); + + exit(0); +} + +// Get the arguments sent by Postfix +function getArgs() +{ + $args = []; + while ($line = trim(fgets(STDIN))) { + [$key, $value] = explode('=', $line, 2); + $args[$key] = $value; + } + + return $args; +} + +// Get the action from the action query result +function getAction($actionQuery) +{ + return is_object($actionQuery) ? array_values(get_object_vars($actionQuery))[0] : null; +} + +// Send the action back to Postfix +function sendAction($action) +{ + echo 'action='.$action."\n\n"; +} + +// Get the outbound message ID from the VERP address +function getIdFromVerp($verp) +{ + $localPart = beforeLast($verp, '@'); + + $parts = explode('_', $localPart); + + if (count($parts) !== 3) { + //logData('VERP invalid email: '.$verp); + + return; + } + + try { + $id = Base32::decodeNoPadding($parts[1]); + + $signature = Base32::decodeNoPadding($parts[2]); + } catch (\Exception $e) { + logData('VERP base32 decode failure: '.$verp.' '.$e->getMessage()); + + return; + } + + $expectedSignature = substr(hash_hmac('sha3-224', $id, $_ENV['ANONADDY_SECRET'] ?? ''), 0, 8); + + if ($signature !== $expectedSignature) { + logData('VERP invalid signature: '.$verp); + + return; + } + + return $id; +} + +// Get the portion of a string before the first occurrence of a given value +function before($subject, $search) +{ + if ($search === '') { + return $subject; + } + + $result = strstr($subject, (string) $search, true); + + return $result === false ? $subject : $result; +} + +// Get the portion of a string before the last occurrence of a given value. +function beforeLast($subject, $search) +{ + if ($search === '') { + return $subject; + } + + $pos = mb_strrpos($subject, $search); + + if ($pos === false) { + return $subject; + } + + return mb_substr($subject, 0, $pos, 'UTF-8'); +} + +// Return the remainder of a string after the last occurrence of a given value +function afterLast($subject, $search) +{ + if ($search === '') { + return $subject; + } + + $position = strrpos($subject, (string) $search); + + if ($position === false) { + return $subject; + } + + return substr($subject, $position + strlen($search)); +} + +// Determine if a given string ends with a given substring +function endsWith($haystack, $needles) +{ + if (! is_iterable($needles)) { + $needles = (array) $needles; + } + + foreach ($needles as $needle) { + if ((string) $needle !== '' && str_ends_with($haystack, $needle)) { + return true; + } + } + + return false; +} + +function logData($data) +{ + file_put_contents(__DIR__.'/../storage/logs/postfix-access-policy.log', '['.(new DateTime())->format('Y-m-d H:i:s').'] '.$data.PHP_EOL, FILE_APPEND); +} diff --git a/postfix/composer.json b/postfix/composer.json new file mode 100644 index 0000000..27408cb --- /dev/null +++ b/postfix/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "illuminate/database": "^10.0", + "vlucas/phpdotenv": "^5.5", + "paragonie/constant_time_encoding": "^2.6" + } +} diff --git a/postfix/composer.lock b/postfix/composer.lock new file mode 100644 index 0000000..2abf56e --- /dev/null +++ b/postfix/composer.lock @@ -0,0 +1,1659 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "46d9d11a3c6417de4c53dbd5441cef7a", + "packages": [ + { + "name": "brick/math", + "version": "0.11.0", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", + "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "5.0.0" + }, + "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", + "brick", + "math" + ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.11.0" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-01-15T23:15:59+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "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.0.8" + }, + "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": "2023-06-16T13:40:37+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + }, + "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.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2023-02-25T20:23:15+00:00" + }, + { + "name": "illuminate/collections", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "133f59956c8002448b07a3ff5f4352f3b3de5614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/133f59956c8002448b07a3ff5f4352f3b3de5614", + "reference": "133f59956c8002448b07a3ff5f4352f3b3de5614", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-09-19T22:43:12+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/d0958e4741fc9d6f516a552060fd1b829a85e009", + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-03T08:06:17+00:00" + }, + { + "name": "illuminate/container", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "ddc26273085fad3c471b2602ad820e0097ff7939" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/ddc26273085fad3c471b2602ad820e0097ff7939", + "reference": "ddc26273085fad3c471b2602ad820e0097ff7939", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0", + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1" + }, + "provide": { + "psr/container-implementation": "1.1|2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Container\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Container package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-18T09:12:03+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "6c39fba7b2311e28f5c6ac7d729e3d49a2a98406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/6c39fba7b2311e28f5c6ac7d729e3d49a2a98406", + "reference": "6c39fba7b2311e28f5c6ac7d729e3d49a2a98406", + "shasum": "" + }, + "require": { + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-09-05T19:07:46+00:00" + }, + { + "name": "illuminate/database", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/database.git", + "reference": "f3be980302c06dcc7ed941d4b0079ae849c6c28a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/database/zipball/f3be980302c06dcc7ed941d4b0079ae849c6c28a", + "reference": "f3be980302c06dcc7ed941d4b0079ae849c6c28a", + "shasum": "" + }, + "require": { + "brick/math": "^0.9.3|^0.10.2|^0.11", + "ext-pdo": "*", + "illuminate/collections": "^10.0", + "illuminate/container": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "illuminate/support": "^10.0", + "php": "^8.1" + }, + "suggest": { + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^3.5.1).", + "ext-filter": "Required to use the Postgres database driver.", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.21).", + "illuminate/console": "Required to use the database commands (^10.0).", + "illuminate/events": "Required to use the observers with Eloquent (^10.0).", + "illuminate/filesystem": "Required to use the migrations (^10.0).", + "illuminate/pagination": "Required to paginate the result set (^10.0).", + "symfony/finder": "Required to use Eloquent model factories (^6.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Database\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Database package.", + "homepage": "https://laravel.com", + "keywords": [ + "database", + "laravel", + "orm", + "sql" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-10-02T18:33:10+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-05T12:46:42+00:00" + }, + { + "name": "illuminate/support", + "version": "v10.26.2", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "c7cf96f64ae6766a85fcd17f46afcae5a6a88744" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/c7cf96f64ae6766a85fcd17f46afcae5a6a88744", + "reference": "c7cf96f64ae6766a85fcd17f46afcae5a6a88744", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-mbstring": "*", + "illuminate/collections": "^10.0", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "nesbot/carbon": "^2.67", + "php": "^8.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^10.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", + "ramsey/uuid": "Required to use Str::uuid() (^4.7).", + "symfony/process": "Required to use the composer class (^6.2).", + "symfony/uid": "Required to use Str::ulid() (^6.2).", + "symfony/var-dumper": "Required to use the dd function (^6.2).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-09-21T20:37:39+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.71.0", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "98276233188583f2ff845a0f992a235472d9466a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/98276233188583f2ff845a0f992a235472d9466a", + "reference": "98276233188583f2ff845a0f992a235472d9466a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "*", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "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/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/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": "2023-09-25T11:31:05+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "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": "2022-06-14T06:56:20+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e", + "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "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.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2023-02-25T19:38:58+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/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": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.3.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.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": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/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.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.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/v6.3.3" + }, + "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": "2023-07-31T07:08:24+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/02c24deb352fb0d79db5486c0c79905a85e37e86", + "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "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.3.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": "2023-05-30T17:17:10+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.5.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, + "branch-alias": { + "dev-master": "5.5-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.5.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2022-10-16T01:01:54+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "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": "http://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.1" + }, + "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": "2022-03-08T17:03:00+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png index 8ef8f9e9b0fb71cb7ea13be6984347192d6812dc..09e765f116ee57d2a22eabc15f132214f037a725 100644 GIT binary patch delta 6368 zcmbVRXHXMBw+*2q5Tr^AU3zaKL;*pnf`D|TH>m>B0tA%ady^s}Af3>A=tY`<^d?O( zBp@xJ^JV7E`|)Pp`}1ah?9A@HckY}$d+*NKisu&+NsJ^w006Msb)}ok@d`%lt?@#Y zc$b)&lwGuszs3>(c)+BgqGaH=gwAtvVVSoYx;q?)*AurAC-SJqaPlCZ2%Dt1YBZ%w zd7}CgDt>9`?`oMhNbBuBi)&JkrB(e1aJBrRVM^;ZLywn(&!1C~pu-Ip9#Km(<^2#V zrIDP*r@1LI!h_Aq#$H-!#K69NhuCL$C7sZype81|{13<79saC-gQMmA{2qiavEqSz zw@A0G$aL62`hGzFtX`VoKH7XmaLGS{W3h1Fq)2{+PzCLc|J%gTmw*M2^fHh+Gg?@@ zOk7CndH1eDP%%s)nKTXUj~FYLZi6P zu{9O9W0d}{Z?;=&!XT2^UqsYfHMvZ%B7BqCgKg9PX)1O0LN(b)M6FsBA)ZhYWJdHf z*&Dgb7KiX=DR=Yz!H_#3s%z;EJzpd)E?L;VB5aD(2`aI85f^-@{>L$Mm9e&#@_^l|yW6wN-%)cV|Oh7UZXqPRdJj%@d~J}XAiphghvVZ+W$#TY+~F_sewt9xqzoN{Ept(rWSksh~+ z4#H@=-)AoB$`h^6OaFP{F9zd{{?0nOSQGJw;o9L!`d;* z8=(lSx?3uRZO^`MUPx6lXG~PMBh{Uk&1-%8WmpoLmREI9$9wnM-^}bE=%8NyZfw&n z^t#=&;%W0}JZ5{Hi1Hvsws|2hw!{OjD8=;sVqb0d@6Z-C6hNh1UH}a<&}nRFp<=j4 zd$ZXm#pI0xhJl-!JSeze)!&JwnQVJ`iRHZW%&i&5izszp4{)-nUzK}D<(t7x$OOH$ zA<$RNB3tZ}n|#AEmdB_oUsKU3yJ(zID)F2BHCfehw6L@u)O=|{sCkH#BfX9oz`=bQ zeiMzj#9KH8SNEg?2|<;artcoE8}x#M9!cLGk3=_%Am`N%6j<0^!Z*_9pn_i8)Bsa9 zN(ETU++B2)vXt#w{xvh4|Ic!}2=u&T)x=Cj#N`K-`szAFse<+k5K}vKI*eV{>iQRe zF|dd(5<2&j@pWUrK{{ppP)Aer^X4@0-dVU*Yuq8FR(vQt3lw|QWFo+;grQ9iF-zFOL;vz8&5*t-5zf#zZm`o z$vsNGd!%r@`B}lH94kNhJ)4W+EIPNtj*z3j7INV##WdFSVeWuj1eBTHyFGh$oYP_g z^)%gNd&_#a2-h4CxEbBzS`SFCC})Irb5y;fGJS`h*gE#eRxH$UlXeE}jvQVK1i`kg z?YJ_64yT8G*M7(KeTQ5ie)N5I8GDQ0qRFm#4XPs2hG64vcax)K8NSt~2HklgBM-CS z#36@Wv$-v0;j=G*7cxRL<@hD}If-WpH>aP%V>kNmz@vG4GA}ftIDeoQGU))|d!L!dT<$P}wfUh|=+1ZRyz00@OG1M!!Wcjw1~L7Y~`$ zk2j=rD#Gx>LUcLxlnFU?0b9KCXP?W$6S%eF5)uF2d6>xqswHt_-!e{eg1<%Rrw8Cj zpzUiyguQ{?_&J{XXT#sThs$}3#hXmHBBNe^CGU(2U1xE~^2h1TKS5X+6&Zl~dOFWy zzP|Nz2haIyPM6egi)G~-iTix__g1v?e@^A8`05o5ONjMXgvXC+1dmArC<}|}zXHZt zKgpVNB`p}sy#^C3n;07TI+AO(HRz^0}v}Y z#uO%hE) z>2s+zcBRQw9DA9b+M3RWgLCB7_oE^LBM=po533Su16I$KoOI!^C%r^P?|ca~RmqtS zv1OErrfgQOEhxdCQp&^9dFF4pr~*?qYLHx8b3;aw?{zo5#_P+R2{^x6p0|%B!1wHz zF2=d%tTVzC%^Nqe({|#_v1EtZH4&oe zp4yDAWRS_^@5I55;N;IA`U#Bco8%alQJU6<71TWTC9J;h`1aARHl7oCbd@-q|2ZE# zSbHE&_@u#WDg*>j4ql_`3^E^_?Xg;WsvS84d=@>cI?-PR6pv1&v+{SQp}34`ES)$X zGld?|iz`PjJlKjC2VANcB0OF{V4_()c_dDgN!s6IVw?u^k-Ernm~2CWp~s&~u-}5& zI!h^7kd`0w5CRU2?`Pxg;ZXA$w5h8rjsLYG&NSPI8M}@eERq7xrt;qy)Z~6p(-K(W zOgdbX`J4RO+NPhwM#I6SEDkaQP4Q3<^eFOy@PDY(oy08V9Syv{{7%k9p@O(p-%i9% zCDrYrTVboB;@^j{HAV5S(8qwf1UCk+2E`uf!3ozFYL1F;b1>Q&^R^mozS5-U4JSd(*!R>gX`>?2a%v!qD~RBC zl%IIs>D*FqsF*J*E;BXEtWu&%_8x^gN*QB&^?he?BJM71(v%w(=H8?)$T2=`(MO zgFX&d%Pfk-6K%@Jk-{Wl=eE?OF%++sm@?<2NClY{U-3ha`3jaKgG+{n6$;b4WkQt` z*SQA!=o-`zaxzCtWv9!#3$+wQ-G9_}135=|>sl-N9<8P0IZAP(-wGbCbM`CK!QjL! z-(-XimcMoeO=oZ~&Cov&v>so7qC^Yl#5J9XSh6i?f2V-I_M1Jiy@DTH#9j zu4ARCv7ya07|_pKm;8Rtp$`@YmLcgUjSkv@k6g5Bj&O~+gXS0a5S`hVJ@N{>EiWl~ zl4pKx+vsd=hBt~^X|f5xx9*ktY!m6oH{5(_aAzQBqWuRn4P zx)#(r&>iYWbp7yR(q6z1nNtnJJlx-M|D+g-udmPXxdp>*;U{;c)C@aIwjgOpIEaS9 z^8SwD`da36(0*lN$1L!^iNS%{Yd!Er^(3TdJ;-?q_e$B{o4B6bkh{>$fUr=>REO<+ zLRQfaH&=xVq>PmQQT5nuqdC)!4Ct7tg#e;-=;w?(J@RIO{O!xy84E*QSIn_nE{uQK z*fzHqAJjP^{CkrjVD(xw`O>H9%BPCjY(LCc`j6ow zlV5s0^#aT<{~AU7N?k=pS8t{dlzC&)i@Hj|IZqv87V#Bboa*T7 zM2B=2{TEoI0?Wp1`#y{pH;yU>Mvpqwq{Zr?@xHw6GW09H=kMFzhaN#BflySv+mKJ5+;C$zvXy}ji{5h)dOD-aA|Y9{Q)-%ygeCTlwe4oUSGK8+T@ zcdk*K)qk3Bd_|8mq9IhqpiOt^p~vpu#={9V=V|KeiwvNEr0&g597}TZ7>bwhDKoP8 zsOlCBL0BBys=tdSu{?QfH3s(Z7+ET36a+oDoyvj${CYG99l7^7alT09B7Q$ZX-60c zFf#o|%$e}(FPwp>IljtuT`X#YMJ4&(&aYD`k)0)csy07D2{(|axTEiaen4=#v(8am zxl{(0*pvh+cG4~aFAXaQJ3)ue0{t;#q)i_0EQi6)(sj3F-dC!BF|-Jjs0j3c8gFP3 zf3!xi*}`BtpK;wc9v&;%*Yx5vi_U7BS%=#-T%9$~sK z%@#f`;7@{~sL&T@ioRO@$#PPfWUJB`TAdUnXyxqi@d#b6gU)6)Od>buU#x|MqfU>? z{ZOW+$o-6jnZ_#G2u~D$Crg9`x=it-fGY^VsWn&wk<(&ufS2O-&@leipU%aQdgZcQ zNY-1-atI{1Z;37fU8Y`H3&~;y{(zTCFwsSIzd`Z)nTxYXIfA+eXVTU6$`(BCouK#w z7^!V$2T>b<=R$cFqe#IdC1_0E-vL_?pio4@d2$-P@w+J2h@A$L=F=3Pu`pgT8Upcs zDnfBlY@99*1=KRb`smd4Mi%tQaKUK5)bP9{5w`~c$VIF}kN?V8>WmbNaW#h9Q|miH z6{G9g{!WO!%E8rV8NX5Om>&byp=0Co<_dl^AIATrNDBrPFL-5c2~>Ozzt&VBs)n!x z@`b8ekbQEPe3PI>yZF>0IKvZ#*A1;U>%WFJ(q;C>EpeB5x;19W#7j_&I}vbLTjc_>{QT5l5i;h5x0XHlkl) z+UwKROme?s?nXBf1MXRLY5b;r) z)cLK+$DmZfG`qwNwTS^ckWLA#;qV5?WfP&JGypG?SF=O+tU%sa>cCaTZ6Z&1>WD*( zYl%#@fB+x3+D+!2PaUMzm_0+AIiE1JY4BcHW&Yl;$P_ntB<~BLX zXFk7DFJK1QH;d0qO>bZ&5WqL-7lfbwWEXVF3KBe;1uLQs3TDECnKDL765jmiJf=h} z)OR}+Nf=*}e*--PaA888880dpab&bOa~R4mrr(^sUsr4HTg3d<&a}wi^{t3ca1g;b zwAUPc9UYY?btY`f5W^8hSG|4S-r}ht`}L{L`lpEqW;+1Y6oZ$sha1lw?+w1dU|Y^yCoS@qfI z=^!jh*nj8l+t4hX*8yB$?&kTtRy455AOVE^L-RahlS1 zf+yG{VYwY~QNw%R^7h-`6q=VnF7k?@Mx!9tCZ2i)*g^mCdn-9%tP+Fe&-`n%oqABr zvf*Ni1=y?B%HTOB@5CP0aQP?{ycAbf?1Q!;f7lIFeN6(@gC6a)!&piAQV&OQ(!)m* zgO#<3A<0VL;e^1GtbU?UlI{-=-Cgm?ms0PZk$ihm)gWH-hN;#s{C=X73l&Mo8f>)L zeao8xCWepEUcU>A$hz((+BOLKOAg0q#sTq!lFp3WKC3s?_nhIHn(1=x~Rw zGCk{=2nMGQJPZZ+?38$~n#lg`0l&kmU^gPsxOC#Np>5CXf2QX7h2M(KQz z_Rfa@NQzZxHHLMNHr}u6Tf9ECIO7A!+_;lpqklZ!wJcQxG)z<+=ToRq=Q&L(K!GY5 z;d6Gq2QR)o?yfc${V8wBZ&|OXzI(c_;WV@@RXow`&=yHlm?kwpgWgYK*?LAQfAZ51 zhN362{6tEv*WX0MuqfG?H=I)IL`TY3S?})jgke#X3V>Ed)HY=gmao`Np871Y@KQ9Q z0zp}y1Bwg@)JO;hXSedE2MtYG0p|8PU_E9IipI;umv#Ns&n9pvmpJ2ShVd!ic8*xF zowQXoa1G-lLj(>SR5s8Kkr4dTehHJG5p;9n3NF}3jkp(%pP`-uWix(CHhv-ib`MD+ z;iH>Nf3mR>E&cfl4UN%xEr1VYQU{N<*a5@kOYl+UD+#8uAMPH>kl!KPinM+X1*;d1 z_a|sbLdq-DL=~yR(&L*W+gp0_ub)!a*zt<8H*+J)Sox^{lsDFh;IoXDE|{G?r11o; z)OGf3)<1DJmU!u-J|PhBuP5xXhsTtm9D@MC3K5Su1QiN5|m_C7?1Rx=YUa;$Q zS9f$(?A|5ANqz%EA{Ou}L`^QgkklekA>P zSpx$A;6JrrmUx@7ArlJN9{RvNcq+MyES#zIO-g*JNUiY`q?~IFA|^S%Dn(6{6AmS) z`U*ys{M`*W^Xdes>vFtkY!FdKepPlQc#SvAdHrOV`hM8Rjc`3NEJQ*2kL{~1&ro|D z-q(6h+kb&9H4HZ7@a5@qPmERmUz+=W%koEeNS=E?W$c|=a32ml-m1plwpQMDk8M2d za1%g6OhQsvLrhj!Le@Y`?y;oAV<}ldF|o&DViX?V_Wy^6tGlg}z5joC{BI6{!YoFq I9h~m}0l^+!m;e9( literal 9003 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 literal 0 HcmV?d00001 diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index 40ac823944a45ea3134b1f8ac387320d865a95d4..b7f3c3150ce2cc818077f951b6b7007dbb30eea4 100644 GIT binary patch delta 6361 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 WbcWeldHo0R9{|s^6FpiC?EHT_nggQ% literal 8358 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 Gb3OpQGvFWq diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png index 664e14da3012f3f4427d46761391021f576040ce..7ed3c54e67d53eab1bcffafaae217fe4e8e29654 100644 GIT binary patch delta 1307 zcmZ{hc~BEq9LE>12F6j2YAGP$5J(`|BqV_Vb`(*pCzQjs4p564(6N#zu^0pvTP?~} z6xz{h5d{X#Xgs*W<^IsptFGg;Vo5*~?%#j`8ozoBEOO(h-(}42 z3I<6e00^AIg}2C{MN#@a&?=~vC_^k|ilww))S)Orv-7C}uHk`ActE|9Cc!8&6&Uwv z7zt^yW05b91>M1dZYhGK3ZUp)JBEO?LFT{{@W;V*0P3Hp-^Iv!OfUxt0B+CofySN_ z2}{|W?3a6n^s)oAwvP{?&-Hk++)`mGT>d7&s7 zNl1Zc)%e^Tb~enp#%ew5+|}V+T^071CdEBPW2K%UJc#cla_wB|4*9itcSA33Hsig9 zMW?OS)1B=Vr9sA&rAw2P)>V}r6BUD7pH6Jvftx0b%U*lT<3xouu&mu_X3|`;2%Bgs z3#p=8YG$OA#oaGgR$C;r$|tk05F$%Fc=4keQ>6=8lx!>i>rZoPH}q$huaFHm(r1I?GLv34+ZKdH4`)a3td?~fPg2&-JVCRk ze%LX&#xnUM6G3I&`N5Hr#&p3|_oMI-lJS}OrB7-_{aeL>LvHy-92rGj?XxqlDR+~KQEap zgyh%O=X_W6)G58rOsabW8i&Rq(}zzTwXV=m)3Oev73nt#m*pNexfZ`pDY4|AioUq2 zKE2KY)fUJPmPcopPaLbaK!G{ggY!k(+}3<~ckw>YKcX(2vrs!163->R=j+Y1Stei9 z44c^%3*MN}cK-Z*>rV+Ut$i1(%$hf1uS^4JcuL4%JoFl%ePQZc2<_s}E6fo*G{bYP zjf4oFP4@^k&b$RL94w0eT(mb+#E;o?m=89H&0=$477u3gQd!Y49Ci$sO=GcQSgc)} z%C`My;Gkgd{w(qT4=`46eg^|yc7+r{mMAatFdxdx%VW6h&pD90CsW902o7goFn#1* Ioxe!%H;U_zSpWb4 delta 1035 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 M`=(5g^M$tm2Uhgnr~m)} literal 7406 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 diff --git a/public/logo.png b/public/logo.png index a31eb0fd80d441f071568e6d63dd795b2e4a2b80..a07820b128f9f5eecd45bd5be2b340d54fa09376 100644 GIT binary patch literal 15227 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 Jk+Rvl{{bySk1GHG literal 6831 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 diff --git a/public/mstile-144x144.png b/public/mstile-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..40b55850be756dbfc5c4eae29686d0bf9de783e8 GIT binary patch literal 6042 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 literal 0 HcmV?d00001 diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png index 5e1a954444022f878ca4e75cf2b61f53a2cc80da..4333065183543265ac9758a18d47f1e4361680ee 100644 GIT binary patch delta 5981 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 kmW~$57Jh1k>02`uU{364z{(ZPi_@% literal 0 HcmV?d00001 diff --git a/public/mstile-310x310.png b/public/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..96c7faced05d7bfb1a882c7a12062a92cf30de15 GIT binary patch literal 13437 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 Ls#kL5_JjWeq$_rd literal 0 HcmV?d00001 diff --git a/public/mstile-70x70.png b/public/mstile-70x70.png new file mode 100644 index 0000000000000000000000000000000000000000..930cf90a91d2f1945c6f34aefa0a9092c552f876 GIT binary patch literal 4521 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 literal 0 HcmV?d00001 diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg index 7a9d4c6..d3b5615 100644 --- a/public/safari-pinned-tab.svg +++ b/public/safari-pinned-tab.svg @@ -2,30 +2,35 @@ -Created by potrace 1.11, written by Peter Selinger 2001-2013 +Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - + diff --git a/public/site.webmanifest b/public/site.webmanifest index c2c7e3f..ee41308 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -1,6 +1,6 @@ { - "name": "AnonAddy", - "short_name": "AnonAddy", + "name": "addy.io", + "short_name": "addy.io", "icons": [ { "src": "/android-chrome-192x192.png", @@ -8,12 +8,12 @@ "type": "image/png" }, { - "src": "/android-chrome-256x256.png", - "sizes": "256x256", + "src": "/android-chrome-512x512.png", + "sizes": "512x512", "type": "image/png" } ], - "theme_color": "#ffffff", - "background_color": "#ffffff", + "theme_color": "#19216c", + "background_color": "#19216c", "display": "standalone" } diff --git a/public/svg/icon-logo.svg b/public/svg/icon-logo.svg index 6a2e6a8..3a03347 100644 --- a/public/svg/icon-logo.svg +++ b/public/svg/icon-logo.svg @@ -1,19 +1,54 @@ - - -image/svg+xml + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/svg/logo-dark.svg b/public/svg/logo-dark.svg index 4df3414..c3223a7 100644 --- a/public/svg/logo-dark.svg +++ b/public/svg/logo-dark.svg @@ -1,41 +1,97 @@ - - -image/svg+xml + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + diff --git a/public/svg/logo.svg b/public/svg/logo.svg index b7ef16f..77ebee7 100644 --- a/public/svg/logo.svg +++ b/public/svg/logo.svg @@ -1,41 +1,97 @@ - - -image/svg+xml + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + diff --git a/resources/css/app.css b/resources/css/app.css index 4bc4ad4..de99df2 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -10,6 +10,32 @@ html { display: none; } +[type='search']::-webkit-search-cancel-button, +[type='search']::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +.horizontal-scroll::-webkit-scrollbar, +.vgt-responsive::-webkit-scrollbar { + -webkit-appearance: none; +} + +.horizontal-scroll::-webkit-scrollbar:horizontal, +.vgt-responsive::-webkit-scrollbar:horizontal { + @apply h-1; +} + +.horizontal-scroll::-webkit-scrollbar-thumb, +.vgt-responsive::-webkit-scrollbar-thumb { + @apply bg-indigo-500 rounded-full; +} + +.horizontal-scroll::-webkit-scrollbar-track, +.vgt-responsive::-webkit-scrollbar-track { + @apply bg-indigo-50; +} + @tailwind components; /* Add custom components here... */ diff --git a/resources/js/Components/BannerNotification.vue b/resources/js/Components/BannerNotification.vue new file mode 100644 index 0000000..5b3c479 --- /dev/null +++ b/resources/js/Components/BannerNotification.vue @@ -0,0 +1,44 @@ +
+
+
+
+
+ + + +

+ +

+
+
+ +
+
+
+
+
+ + + diff --git a/resources/js/components/Icon.vue b/resources/js/Components/Icon.vue similarity index 97% rename from resources/js/components/Icon.vue rename to resources/js/Components/Icon.vue index 5dd1b90..725fa71 100644 --- a/resources/js/components/Icon.vue +++ b/resources/js/Components/Icon.vue @@ -22,21 +22,6 @@ > - - - - + + + + +
-
+
diff --git a/resources/js/components/Toggle.vue b/resources/js/Components/Toggle.vue similarity index 100% rename from resources/js/components/Toggle.vue rename to resources/js/Components/Toggle.vue diff --git a/resources/js/Layouts/AppLayout.vue b/resources/js/Layouts/AppLayout.vue new file mode 100644 index 0000000..6875720 --- /dev/null +++ b/resources/js/Layouts/AppLayout.vue @@ -0,0 +1,535 @@ + + + diff --git a/resources/js/Layouts/SettingsLayout.vue b/resources/js/Layouts/SettingsLayout.vue new file mode 100644 index 0000000..9022e49 --- /dev/null +++ b/resources/js/Layouts/SettingsLayout.vue @@ -0,0 +1,98 @@ + + + diff --git a/resources/js/Pages/Aliases/Edit.vue b/resources/js/Pages/Aliases/Edit.vue new file mode 100644 index 0000000..5ee9a1f --- /dev/null +++ b/resources/js/Pages/Aliases/Edit.vue @@ -0,0 +1,233 @@ + + + diff --git a/resources/js/Pages/Aliases/Index.vue b/resources/js/Pages/Aliases/Index.vue new file mode 100644 index 0000000..39e9b1b --- /dev/null +++ b/resources/js/Pages/Aliases/Index.vue @@ -0,0 +1,2320 @@ + + + diff --git a/resources/js/Pages/Auth/Verify.vue b/resources/js/Pages/Auth/Verify.vue new file mode 100644 index 0000000..8b2c186 --- /dev/null +++ b/resources/js/Pages/Auth/Verify.vue @@ -0,0 +1,59 @@ + + + diff --git a/resources/js/Pages/Dashboard/Index.vue b/resources/js/Pages/Dashboard/Index.vue new file mode 100644 index 0000000..2c778a2 --- /dev/null +++ b/resources/js/Pages/Dashboard/Index.vue @@ -0,0 +1,349 @@ + + + diff --git a/resources/js/Pages/Dashboard/OutboundMessagesGraph.vue b/resources/js/Pages/Dashboard/OutboundMessagesGraph.vue new file mode 100644 index 0000000..053ee91 --- /dev/null +++ b/resources/js/Pages/Dashboard/OutboundMessagesGraph.vue @@ -0,0 +1,144 @@ + + + diff --git a/resources/js/Pages/Dashboard/OutboundMessagesPie.vue b/resources/js/Pages/Dashboard/OutboundMessagesPie.vue new file mode 100644 index 0000000..5cc50df --- /dev/null +++ b/resources/js/Pages/Dashboard/OutboundMessagesPie.vue @@ -0,0 +1,41 @@ + + + diff --git a/resources/js/Pages/Domains/Edit.vue b/resources/js/Pages/Domains/Edit.vue new file mode 100644 index 0000000..e2aad7e --- /dev/null +++ b/resources/js/Pages/Domains/Edit.vue @@ -0,0 +1,273 @@ + + + diff --git a/resources/js/Pages/Domains/Index.vue b/resources/js/Pages/Domains/Index.vue new file mode 100644 index 0000000..63de2c9 --- /dev/null +++ b/resources/js/Pages/Domains/Index.vue @@ -0,0 +1,1037 @@ + + + diff --git a/resources/js/Pages/FailedDeliveries.vue b/resources/js/Pages/FailedDeliveries.vue new file mode 100644 index 0000000..c6f5304 --- /dev/null +++ b/resources/js/Pages/FailedDeliveries.vue @@ -0,0 +1,342 @@ + + + diff --git a/resources/js/Pages/Recipients/Edit.vue b/resources/js/Pages/Recipients/Edit.vue new file mode 100644 index 0000000..7b2af59 --- /dev/null +++ b/resources/js/Pages/Recipients/Edit.vue @@ -0,0 +1,305 @@ + + + diff --git a/resources/js/Pages/Recipients/Index.vue b/resources/js/Pages/Recipients/Index.vue new file mode 100644 index 0000000..f28f8c0 --- /dev/null +++ b/resources/js/Pages/Recipients/Index.vue @@ -0,0 +1,862 @@ + + + diff --git a/resources/js/pages/Rules.vue b/resources/js/Pages/Rules.vue similarity index 50% rename from resources/js/pages/Rules.vue rename to resources/js/Pages/Rules.vue index 10e5892..418c57f 100644 --- a/resources/js/pages/Rules.vue +++ b/resources/js/Pages/Rules.vue @@ -1,19 +1,34 @@