diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d18902d --- /dev/null +++ b/composer.json @@ -0,0 +1,40 @@ +{ + "name": "kor-elf/captcha-rule-for-laravel", + "description": "Validation Rule Captcha for Laravel", + "license": "MIT", + "type": "library", + "keywords": [ + "captcha", + "laravel", + "validation" + ], + "homepage": "https://git.kor-elf.net/kor-elf/captcha-rule-for-laravel", + "authors": [ + { + "name": "Leonid Nikitin", + "email": "i@kor-elf.net", + "homepage": "https://git.kor-elf.net/kor-elf", + "role": "Developer" + } + ], + "require": { + "php": "^8.2", + "illuminate/support": "^10.0", + "guzzlehttp/guzzle": "^7.0.1" + }, + "extra": { + "laravel": { + "providers": [ + "korElf\\CaptchaRuleForLaravel\\CaptchaProvider" + ] + } + }, + "autoload": { + "psr-4": { + "korElf\\CaptchaRuleForLaravel\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + } +} diff --git a/config/captcha.php b/config/captcha.php new file mode 100644 index 0000000..4ecd1ba --- /dev/null +++ b/config/captcha.php @@ -0,0 +1,58 @@ + env('CAPTCHA_API_DOMAIN'), + + /* + * Приватный токен для проверки получаемого ключа после успешной проверки от бота. + */ + 'api_private_token' => env('CAPTCHA_PRIVATE_TOKEN'), + + /* + * Curl timeout в секундах. + */ + 'curl_timeout' => (int) env('CAPTCHA_CURL_TIMEOUT', 10), + + /* + * Включает Blade::directive "captcha". + */ + 'enable_blade_captcha' => (bool) env('CAPTCHA_ENABLE_BLADE_CAPTCHA', true), + + /* + * Публичный токен для начало проверки я не робот. + */ + 'api_public_token' => env('CAPTCHA_PUBLIC_TOKEN'), + + /** + * Указываем путь к статике, на данный момент это к стилям. + * Примеры: /captcha, https://captcha.localhost/captcha + */ + 'static_path' => env('CAPTCHA_STATIC_PATH', env('CAPTCHA_API_DOMAIN') . '/captcha'), + + /* + * Используется в переводах. + */ + 'error_message_key' => 'validation.captcha', + + /* + * Имя Validator::extendImplicit. + */ + 'rule_name' => 'captcha', + + /* + * Name в input после успешной проверки. + */ + 'captcha_verified_name' => 'captcha-verified', +]; diff --git a/src/CaptchaProvider.php b/src/CaptchaProvider.php new file mode 100644 index 0000000..ea326d6 --- /dev/null +++ b/src/CaptchaProvider.php @@ -0,0 +1,60 @@ +addValidationRule(); + $this->publishes([ + __DIR__ . '/../config/captcha.php' => config_path('captcha.php'), + ], 'config'); + + if (config('captcha.enable_blade_captcha')) { + Blade::directive('captcha', function () { + return ""; + }); + } + } + + /** + * Extends Validator to include a captcha type + */ + public function addValidationRule(): Void + { + $message = trans(config('captcha.error_message_key')); + Validator::extendImplicit(config('captcha.rule_name'), function ($attribute, $value) { + if (!is_string($value)) { + return false; + } + return app(CaptchaService::class)->validate($value, request()->userAgent()); + }, $message); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register(): Void + { + $this->mergeConfigFrom( + __DIR__ . '/../config/captcha.php', + 'captcha' + ); + + $this->app->singleton(CaptchaService::class, function (Application $app) { + return new CaptchaService( + privateToken: config('captcha.api_private_token', ''), + domainApi: config('captcha.api_domain', ''), + curlTimeout: config('captcha.curl_timeout', ''), + ); + }); + } +} diff --git a/src/CaptchaService.php b/src/CaptchaService.php new file mode 100644 index 0000000..1c5a608 --- /dev/null +++ b/src/CaptchaService.php @@ -0,0 +1,35 @@ + $this->privateToken, + ]) + ->timeout($this->curlTimeout) + ->post($this->domainApi . '/api/v1/captcha/' . $token, $post); + + return ($response->json('status') === true); + } catch (\Throwable $e) { + + } + return false; + } +} \ No newline at end of file diff --git a/src/helpers.php b/src/helpers.php new file mode 100644 index 0000000..cd647f6 --- /dev/null +++ b/src/helpers.php @@ -0,0 +1,8 @@ +'; +}