diff --git a/.env.example b/.env.example index 16c84c7..73a840e 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,8 @@ CLOUDFLARE_AUTH_BEARER="YOUR_CLOUDFLARE_AUTH_BEARER" GTAG_MEASUREMENT_ID="G-XXXXXXXXXX" +DISCORD_ALERT_WEBHOOK="https://discord.com/api/webhooks/000000000000000000/" + LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug diff --git a/app/Enums/ContactCategory.php b/app/Enums/ContactCategory.php new file mode 100644 index 0000000..b201670 --- /dev/null +++ b/app/Enums/ContactCategory.php @@ -0,0 +1,23 @@ + 'Question', + ContactCategory::Advertising => 'Advertising', + ContactCategory::BugReport => 'Bug Report', + ContactCategory::Feedback => 'Feedback', + ContactCategory::Other => 'Other', + }; + } +} diff --git a/app/Http/Requests/ContactSubmissionRequest.php b/app/Http/Requests/ContactSubmissionRequest.php new file mode 100644 index 0000000..28bf099 --- /dev/null +++ b/app/Http/Requests/ContactSubmissionRequest.php @@ -0,0 +1,25 @@ + ['required', 'max:254'], + 'email' => ['required', 'email', 'max:254'], + 'discord' => ['nullable', 'min:2', 'max:34'], + 'category' => ['required', 'in:question,advertising,bug_report,feedback,other'], + 'subject' => ['required', 'max:254'], + 'message' => ['required', 'unique:contact_submissions', 'max:3500'], + ]; + } + + public function authorize(): bool + { + return true; + } +} diff --git a/app/Models/ContactSubmission.php b/app/Models/ContactSubmission.php new file mode 100644 index 0000000..8f4fa4c --- /dev/null +++ b/app/Models/ContactSubmission.php @@ -0,0 +1,23 @@ + ContactCategory::class, + ]; +} diff --git a/composer.json b/composer.json index ee83ae8..bed340c 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,8 @@ "saade/blade-iconsax": "^1.1", "simonhamp/the-og": "^0.5.4", "spatie/laravel-backup": "^8.3", + "spatie/laravel-discord-alerts": "^1.3", + "spatie/laravel-honeypot": "^4.4", "spatie/laravel-query-builder": "^5.6", "spatie/sheets": "^1.10", "sqids/sqids": "^0.4.1" diff --git a/composer.lock b/composer.lock index 2be319e..001204a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "086abffea6e85ef81611cdc1d9cfdc5d", + "content-hash": "2af8ed1dad29aeeb6bd0674017bde595", "packages": [ { "name": "andcarpi/laravel-popper", @@ -4975,6 +4975,170 @@ ], "time": "2024-02-06T20:39:11+00:00" }, + { + "name": "spatie/laravel-discord-alerts", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-discord-alerts.git", + "reference": "68bcafdd8774ad3e62185566a1c37eff3c0e1047" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-discord-alerts/zipball/68bcafdd8774ad3e62185566a1c37eff3c0e1047", + "reference": "68bcafdd8774ad3e62185566a1c37eff3c0e1047", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.73|^9.0|^10.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "nunomaduro/collision": "^5.10|^6.0|^7.0", + "nunomaduro/larastan": "^1.0|^2.0", + "orchestra/testbench": "^6.22|^7.0|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1|^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\DiscordAlerts\\DiscordAlertsServiceProvider" + ], + "aliases": { + "Discord": "DiscordAlert" + } + } + }, + "autoload": { + "psr-4": { + "Spatie\\DiscordAlerts\\": "src", + "Spatie\\DiscordAlerts\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rias Van der Veken", + "email": "rias@spatie.be", + "role": "Developer" + }, + { + "name": "Niels Vanpachtenbeke", + "email": "niels@spatie.be", + "role": "Developer" + }, + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Send a message to Discord", + "homepage": "https://github.com/spatie/laravel-discord-alerts", + "keywords": [ + "laravel", + "laravel-discord-alerts", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-discord-alerts/issues", + "source": "https://github.com/spatie/laravel-discord-alerts/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-07-28T06:55:26+00:00" + }, + { + "name": "spatie/laravel-honeypot", + "version": "4.4.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-honeypot.git", + "reference": "85728128acb3ff53ffb23c86b9cc2c3d58355050" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-honeypot/zipball/85728128acb3ff53ffb23c86b9cc2c3d58355050", + "reference": "85728128acb3ff53ffb23c86b9cc2c3d58355050", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0|^9.0|^10.0", + "illuminate/encryption": "^8.0|^9.0|^10.0", + "illuminate/http": "^8.0|^9.0|^10.0", + "illuminate/support": "^8.0|^9.0|^10.0", + "illuminate/validation": "^8.0|^9.0|^10.0", + "nesbot/carbon": "^2.0", + "php": "^8.0", + "spatie/laravel-package-tools": "^1.9", + "symfony/http-foundation": "^5.1.2|^6.0" + }, + "require-dev": { + "livewire/livewire": "^2.10", + "orchestra/testbench": "^6.23|^7.0|^8.0", + "pestphp/pest-plugin-livewire": "^1.0", + "phpunit/phpunit": "^9.4", + "spatie/pest-plugin-snapshots": "^1.1", + "spatie/phpunit-snapshot-assertions": "^4.2", + "spatie/test-time": "^1.2.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Honeypot\\HoneypotServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\Honeypot\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Preventing spam submitted through forms", + "homepage": "https://github.com/spatie/laravel-honeypot", + "keywords": [ + "laravel-honeypot", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/laravel-honeypot/tree/4.4.0" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2023-12-01T10:30:39+00:00" + }, { "name": "spatie/laravel-package-tools", "version": "1.16.2", diff --git a/config/discord-alerts.php b/config/discord-alerts.php new file mode 100644 index 0000000..cd8a1f6 --- /dev/null +++ b/config/discord-alerts.php @@ -0,0 +1,16 @@ + [ + 'default' => env('DISCORD_ALERT_WEBHOOK'), + ], + + /* + * This job will send the message to Discord. You can extend this + * job to set timeouts, retries, etc... + */ + 'job' => Spatie\DiscordAlerts\Jobs\SendToDiscordChannelJob::class, +]; diff --git a/config/honeypot.php b/config/honeypot.php new file mode 100644 index 0000000..7daf9c3 --- /dev/null +++ b/config/honeypot.php @@ -0,0 +1,66 @@ + env('HONEYPOT_ENABLED', true), + + /* + * Here you can specify name of the honeypot field. Any requests that submit a non-empty + * value for this name will be discarded. Make sure this name does not + * collide with a form field that is actually used. + */ + 'name_field_name' => env('HONEYPOT_NAME', 'my_name'), + + /* + * When this is activated there will be a random string added + * to the name_field_name. This improves the + * protection against bots. + */ + 'randomize_name_field_name' => env('HONEYPOT_RANDOMIZE', true), + + /* + * When this is activated, requests will be checked if + * form is submitted faster than this amount of seconds + */ + 'valid_from_timestamp' => env('HONEYPOT_VALID_FROM_TIMESTAMP', true), + + /* + * This field contains the name of a form field that will be used to verify + * if the form wasn't submitted too quickly. Make sure this name does not + * collide with a form field that is actually used. + */ + 'valid_from_field_name' => env('HONEYPOT_VALID_FROM', 'valid_from'), + + /* + * If the form is submitted faster than this amount of seconds + * the form submission will be considered invalid. + */ + 'amount_of_seconds' => env('HONEYPOT_SECONDS', 2), + + /* + * This class is responsible for sending a response to requests that + * are detected as being spammy. By default a blank page is shown. + * + * A valid responder is any class that implements + * `Spatie\Honeypot\SpamResponder\SpamResponder` + */ + 'respond_to_spam_with' => BlankPageResponder::class, + + /* + * When activated, requests will be checked if honeypot fields are missing, + * if so the request will be stamped as spam. Be careful! When using the + * global middleware be sure to add honeypot fields to each form. + */ + 'honeypot_fields_required_for_all_forms' => false, + + /* + * This class is responsible for applying all spam protection + * rules for a request. In most cases, you shouldn't change + * this value. + */ + 'spam_protection' => \Spatie\Honeypot\SpamProtection::class, +]; diff --git a/database/migrations/2024_02_24_162055_create_contact_submissions_table.php b/database/migrations/2024_02_24_162055_create_contact_submissions_table.php new file mode 100644 index 0000000..565d47d --- /dev/null +++ b/database/migrations/2024_02_24_162055_create_contact_submissions_table.php @@ -0,0 +1,26 @@ +id(); + $table->string('name'); + $table->string('email'); + $table->string('discord')->nullable(); + $table->enum('category', ['question', 'advertising', 'bug_report', 'feedback', 'other']); + $table->string('subject'); + $table->text('message')->unique(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('contact_submissions'); + } +};