Skip to content

Commit

Permalink
Merge branch 'main' into 159a6-integrate-google-captcha
Browse files Browse the repository at this point in the history
  • Loading branch information
chiragchhatrala authored Dec 18, 2024
2 parents 60c720d + 7365479 commit 6b0c671
Show file tree
Hide file tree
Showing 27 changed files with 610 additions and 34 deletions.
17 changes: 14 additions & 3 deletions api/app/Http/Controllers/Auth/RegisterController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Rules\ValidHCaptcha;

class RegisterController extends Controller
{
Expand All @@ -27,6 +28,9 @@ class RegisterController extends Controller
public function __construct()
{
$this->middleware('guest');

$this->middleware('throttle:5,1')->only('register'); // 5 attempts per minute
$this->middleware('throttle:30,60')->only('register'); // 30 attempts per hour
}

/**
Expand Down Expand Up @@ -56,16 +60,22 @@ protected function registered(Request $request, User $user)
*/
protected function validator(array $data)
{
return Validator::make($data, [
$rules = [
'name' => 'required|max:255',
'email' => 'required|email:filter|max:255|unique:users|indisposable',
'password' => 'required|min:6|confirmed',
'hear_about_us' => 'required|string',
'agree_terms' => ['required', Rule::in([true])],
'appsumo_license' => ['nullable'],
'invite_token' => ['nullable', 'string'],
'utm_data' => ['nullable', 'array']
], [
'utm_data' => ['nullable', 'array'],
];

if (config('services.h_captcha.secret_key')) {
$rules['h-captcha-response'] = [new ValidHCaptcha()];
}

return Validator::make($data, $rules, [
'agree_terms' => 'Please agree with the terms and conditions.',
]);
}
Expand All @@ -84,6 +94,7 @@ protected function create(array $data)
'password' => bcrypt($data['password']),
'hear_about_us' => $data['hear_about_us'],
'utm_data' => array_key_exists('utm_data', $data) ? $data['utm_data'] : null,
'meta' => ['registration_ip' => request()->ip()],
]);

// Add relation with user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Requests\Integration;

use App\Models\Forms\Form;
use App\Models\Integration\FormIntegration;
use App\Rules\IntegrationLogicRule;
use Illuminate\Foundation\Http\FormRequest;
Expand All @@ -14,9 +15,11 @@ class FormIntegrationsRequest extends FormRequest
public array $integrationRules = [];

private ?string $integrationClassName = null;
private ?Form $form = null;

public function __construct(Request $request)
{
$this->form = Form::findOrFail(request()->route('id'));
if ($request->integration_id) {
// Load integration class, and get rules
$integration = FormIntegration::getIntegration($request->integration_id);
Expand Down Expand Up @@ -77,7 +80,7 @@ protected function isOAuthRequired(): bool

private function loadIntegrationRules()
{
foreach ($this->integrationClassName::getValidationRules() as $key => $value) {
foreach ($this->integrationClassName::getValidationRules($this->form) as $key => $value) {
$this->integrationRules['settings.' . $key] = $value;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function handle(): void
Http::throw()->post($this->getWebhookUrl(), $this->getWebhookData());
}

abstract public static function getValidationRules(): array;
abstract public static function getValidationRules(?Form $form): array;

public static function isOAuthRequired(): bool
{
Expand Down
3 changes: 2 additions & 1 deletion api/app/Integrations/Handlers/DiscordIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

namespace App\Integrations\Handlers;

use App\Models\Forms\Form;
use App\Open\MentionParser;
use App\Service\Forms\FormSubmissionFormatter;
use Illuminate\Support\Arr;
use Vinkla\Hashids\Facades\Hashids;

class DiscordIntegration extends AbstractIntegrationHandler
{
public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [
'discord_webhook_url' => 'required|url|starts_with:https://discord.com/api/webhooks',
Expand Down
34 changes: 31 additions & 3 deletions api/app/Integrations/Handlers/EmailIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@

namespace App\Integrations\Handlers;

use App\Models\Forms\Form;
use App\Models\Integration\FormIntegration;
use App\Notifications\Forms\FormEmailNotification;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Open\MentionParser;
use App\Service\Forms\FormSubmissionFormatter;
use Illuminate\Validation\ValidationException;

class EmailIntegration extends AbstractEmailIntegrationHandler
{
public const RISKY_USERS_LIMIT = 120;

public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [
'send_to' => 'required',
$rules = [
'send_to' => ['required'],
'sender_name' => 'required',
'sender_email' => 'email|nullable',
'subject' => 'required',
Expand All @@ -24,6 +27,31 @@ public static function getValidationRules(): array
'include_hidden_fields_submission_data' => ['nullable', 'boolean'],
'reply_to' => 'nullable',
];

if ($form->is_pro || config('app.self_hosted')) {
return $rules;
}

// Free plan users can only send to a single email address (avoid spam)
$rules['send_to'][] = function ($attribute, $value, $fail) use ($form) {
if (count(explode("\n", trim($value))) > 1 || count(explode(',', $value)) > 1) {
$fail('You can only send to a single email address on the free plan. Please upgrade to the Pro plan to create a new integration.');
}
};

// Free plan users can only have a single email integration per form (avoid spam)
if (!request()->route('integrationid')) {
$existingEmailIntegrations = FormIntegration::where('form_id', $form->id)
->where('integration_id', 'email')
->count();
if ($existingEmailIntegrations > 0) {
throw ValidationException::withMessages([
'settings.send_to' => ['Free users are limited to 1 email integration per form.']
]);
}
}

return $rules;
}

protected function shouldRun(): bool
Expand Down
7 changes: 3 additions & 4 deletions api/app/Integrations/Handlers/GoogleSheetsIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Events\Forms\FormSubmitted;
use App\Integrations\Google\Google;
use App\Models\Forms\Form;
use App\Models\Integration\FormIntegration;
use Exception;
use Illuminate\Support\Facades\Log;
Expand All @@ -22,11 +23,9 @@ public function __construct(
$this->client = new Google($formIntegration);
}

public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [

];
return [];
}

public static function isOAuthRequired(): bool
Expand Down
3 changes: 2 additions & 1 deletion api/app/Integrations/Handlers/SlackIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

namespace App\Integrations\Handlers;

use App\Models\Forms\Form;
use App\Open\MentionParser;
use App\Service\Forms\FormSubmissionFormatter;
use Illuminate\Support\Arr;
use Vinkla\Hashids\Facades\Hashids;

class SlackIntegration extends AbstractIntegrationHandler
{
public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [
'slack_webhook_url' => 'required|url|starts_with:https://hooks.slack.com/',
Expand Down
4 changes: 3 additions & 1 deletion api/app/Integrations/Handlers/WebhookIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace App\Integrations\Handlers;

use App\Models\Forms\Form;

class WebhookIntegration extends AbstractIntegrationHandler
{
public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [
'webhook_url' => 'required|url'
Expand Down
3 changes: 2 additions & 1 deletion api/app/Integrations/Handlers/ZapierIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Integrations\Handlers;

use App\Events\Forms\FormSubmitted;
use App\Models\Forms\Form;
use App\Models\Integration\FormIntegration;
use Exception;

Expand All @@ -16,7 +17,7 @@ public function __construct(
parent::__construct($event, $formIntegration, $integration);
}

public static function getValidationRules(): array
public static function getValidationRules(?Form $form): array
{
return [];
}
Expand Down
3 changes: 3 additions & 0 deletions api/app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class User extends Authenticatable implements JWTSubject
'password',
'hear_about_us',
'utm_data',
'meta'
];

/**
Expand All @@ -44,6 +45,7 @@ class User extends Authenticatable implements JWTSubject
'password',
'remember_token',
'hear_about_us',
'meta'
];

/**
Expand All @@ -56,6 +58,7 @@ protected function casts()
return [
'email_verified_at' => 'datetime',
'utm_data' => 'array',
'meta' => 'array',
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
$driver = DB::getDriverName();

Schema::table('users', function (Blueprint $table) use ($driver) {
if ($driver === 'mysql') {
$table->json('meta')->default(new Expression('(JSON_OBJECT())'));
} else {
$table->json('meta')->default('{}');
}
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('meta');
});
}
};
2 changes: 2 additions & 0 deletions api/phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<env name="JWT_SECRET" value="9K6whOetAFaokQgSIdbMQZuJuDV5uS2Y"/>
<env name="STRIPE_KEY" value="TEST_KEY"/>
<env name="STRIPE_SECRET" value="TEST_SECRET"/>
<env name="H_CAPTCHA_SITE_KEY" value="TEST_SITE_KEY"/>
<env name="H_CAPTCHA_SECRET_KEY" value="TEST_SECRET"/>
</php>
<source>
<include>
Expand Down
4 changes: 4 additions & 0 deletions api/resources/data/forms/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"icon": "heroicons:envelope-20-solid",
"section_name": "Notifications",
"file_name": "EmailIntegration",
"actions_file_name": "EmailIntegrationActions",
"is_pro": false,
"crisp_help_page_slug": "can-i-receive-notifications-on-form-submissions-134svqv"
},
Expand All @@ -12,20 +13,23 @@
"icon": "mdi:slack",
"section_name": "Notifications",
"file_name": "SlackIntegration",
"actions_file_name": "SlackIntegrationActions",
"is_pro": true
},
"discord": {
"name": "Discord Notification",
"icon": "ic:baseline-discord",
"section_name": "Notifications",
"file_name": "DiscordIntegration",
"actions_file_name": "DiscordIntegrationActions",
"is_pro": true
},
"webhook": {
"name": "Webhook Notification",
"icon": "material-symbols:webhook",
"section_name": "Automation",
"file_name": "WebhookIntegration",
"actions_file_name": "WebhookIntegrationActions",
"is_pro": false
},
"zapier": {
Expand Down
Loading

0 comments on commit 6b0c671

Please sign in to comment.