feat: Add Discord alert webhook, ContactCategory enum, form request

- Added Discord alert webhook URL to .env.example
- Created ContactCategory enum with humanReadable method
- Implemented ContactSubmissionRequest form request
- Added ContactSubmission model with fillable and casts properties
- Included configurations for Discord alerts and honeypot protection
This commit is contained in:
Rico van Zelst
2024-02-24 19:58:31 +01:00
parent 13ba72c897
commit c490e87c3e
9 changed files with 348 additions and 1 deletions

View File

@@ -20,6 +20,8 @@ CLOUDFLARE_AUTH_BEARER="YOUR_CLOUDFLARE_AUTH_BEARER"
GTAG_MEASUREMENT_ID="G-XXXXXXXXXX" GTAG_MEASUREMENT_ID="G-XXXXXXXXXX"
DISCORD_ALERT_WEBHOOK="https://discord.com/api/webhooks/000000000000000000/"
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug LOG_LEVEL=debug

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Enums;
enum ContactCategory: string
{
case Question = 'question';
case Advertising = 'advertising';
case BugReport = 'bug_report';
case Feedback = 'feedback';
case Other = 'other';
public function humanReadable(): string
{
return match ($this) {
ContactCategory::Question => 'Question',
ContactCategory::Advertising => 'Advertising',
ContactCategory::BugReport => 'Bug Report',
ContactCategory::Feedback => 'Feedback',
ContactCategory::Other => 'Other',
};
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactSubmissionRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['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;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Enums\ContactCategory;
class ContactSubmission extends Model
{
protected $fillable
= [
'name',
'email',
'discord',
'category',
'subject',
'message',
];
protected $casts = [
'category' => ContactCategory::class,
];
}

View File

@@ -26,6 +26,8 @@
"saade/blade-iconsax": "^1.1", "saade/blade-iconsax": "^1.1",
"simonhamp/the-og": "^0.5.4", "simonhamp/the-og": "^0.5.4",
"spatie/laravel-backup": "^8.3", "spatie/laravel-backup": "^8.3",
"spatie/laravel-discord-alerts": "^1.3",
"spatie/laravel-honeypot": "^4.4",
"spatie/laravel-query-builder": "^5.6", "spatie/laravel-query-builder": "^5.6",
"spatie/sheets": "^1.10", "spatie/sheets": "^1.10",
"sqids/sqids": "^0.4.1" "sqids/sqids": "^0.4.1"

166
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "086abffea6e85ef81611cdc1d9cfdc5d", "content-hash": "2af8ed1dad29aeeb6bd0674017bde595",
"packages": [ "packages": [
{ {
"name": "andcarpi/laravel-popper", "name": "andcarpi/laravel-popper",
@@ -4975,6 +4975,170 @@
], ],
"time": "2024-02-06T20:39:11+00:00" "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", "name": "spatie/laravel-package-tools",
"version": "1.16.2", "version": "1.16.2",

16
config/discord-alerts.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
return [
/*
* The webhook URLs that we'll use to send a message to Discord.
*/
'webhook_urls' => [
'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,
];

66
config/honeypot.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
use Spatie\Honeypot\SpamResponder\BlankPageResponder;
return [
/*
* This switch determines if the honeypot protection should be activated.
*/
'enabled' => 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,
];

View File

@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class () extends Migration {
public function up(): void
{
Schema::create('contact_submissions', function (Blueprint $table) {
$table->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');
}
};