From ccfd5b996b247810f03a5e6e028ddaced828ce31 Mon Sep 17 00:00:00 2001 From: w33b Date: Sun, 18 Jan 2026 18:37:08 +0100 Subject: [PATCH] Replace captcha system --- .../Controllers/Api/DownloadApiController.php | 9 +- .../Auth/RegisteredUserController.php | 3 + app/Http/Controllers/ContactController.php | 9 +- app/Http/Requests/Auth/LoginRequest.php | 3 + composer.json | 2 +- composer.lock | 200 +++++++++++------- config/captcha.php | 50 ----- package-lock.json | 50 ++++- package.json | 1 + resources/css/app.css | 29 +++ resources/js/app.js | 3 + resources/views/auth/login.blade.php | 10 + .../partials/submit-contact-form.blade.php | 28 +-- .../partials/download-captcha.blade.php | 42 ++-- routes/web.php | 3 - 15 files changed, 242 insertions(+), 200 deletions(-) delete mode 100644 config/captcha.php diff --git a/app/Http/Controllers/Api/DownloadApiController.php b/app/Http/Controllers/Api/DownloadApiController.php index f18083e..0a30c67 100644 --- a/app/Http/Controllers/Api/DownloadApiController.php +++ b/app/Http/Controllers/Api/DownloadApiController.php @@ -8,6 +8,8 @@ use App\Models\Episode; use Illuminate\Http\Request; use App\Http\Controllers\Controller; +use GrantHolle\Altcha\Rules\ValidAltcha; + class DownloadApiController extends Controller { /** @@ -16,11 +18,12 @@ class DownloadApiController extends Controller public function getDownload(Request $request) { $validated = $request->validate([ - 'episode_id' => 'required', - 'captcha' => 'required|captcha' + 'episode_id' => ['required'], + 'captcha' => ['required', new ValidAltcha], ]); - $episode = Episode::where('id', $request->input('episode_id'))->firstOrFail(); + $episode = Episode::where('id', $request->input('episode_id')) + ->firstOrFail(); // Increase download count, as we assume the user // downloads after submitting the captcha diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index 88b67e1..33bb10b 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -11,6 +11,8 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules; +use GrantHolle\Altcha\Rules\ValidAltcha; + class RegisteredUserController extends Controller { /** @@ -24,6 +26,7 @@ class RegisteredUserController extends Controller 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 'password' => ['required', 'confirmed', Rules\Password::defaults()], + 'altcha' => ['required', new ValidAltcha], ]); $user = User::create([ diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php index e214735..e22814a 100644 --- a/app/Http/Controllers/ContactController.php +++ b/app/Http/Controllers/ContactController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers; use App\Models\Contact; use Illuminate\Http\Request; +use GrantHolle\Altcha\Rules\ValidAltcha; + class ContactController extends Controller { /** @@ -25,7 +27,7 @@ class ContactController extends Controller 'email' => 'required|max:50', 'message' => 'required|max:1000', 'subject' => 'required|max:50', - 'captcha' => 'required|captcha', + 'altcha' => ['required', new ValidAltcha], ]); $contact = new Contact(); @@ -37,9 +39,4 @@ class ContactController extends Controller return back()->with('status', 'contact-submitted'); } - - public function reloadCaptcha(): \Illuminate\Http\JsonResponse - { - return response()->json(['captcha'=> captcha_img()]); - } } diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php index 2574642..a5da0d6 100644 --- a/app/Http/Requests/Auth/LoginRequest.php +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -9,6 +9,8 @@ use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; +use GrantHolle\Altcha\Rules\ValidAltcha; + class LoginRequest extends FormRequest { /** @@ -29,6 +31,7 @@ class LoginRequest extends FormRequest return [ 'email' => ['required', 'string', 'email'], 'password' => ['required', 'string'], + 'altcha' => ['required', new ValidAltcha], ]; } diff --git a/composer.json b/composer.json index 4b23e2e..1b5c676 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "license": "MIT", "require": { "php": "^8.2", + "grantholle/laravel-altcha": "^2.1", "guzzlehttp/guzzle": "^7.8.1", "hisorange/browser-detect": "^5.0", "http-interop/http-factory-guzzle": "^1.2", @@ -22,7 +23,6 @@ "livewire/livewire": "^3.7.0", "maize-tech/laravel-markable": "^2.3.0", "meilisearch/meilisearch-php": "^1.16", - "mews/captcha": "^3.4.4", "predis/predis": "^2.2", "realrashid/sweet-alert": "^7.2", "rtconner/laravel-tagging": "^5.0", diff --git a/composer.lock b/composer.lock index 4b1bb36..e3b29c9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,55 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9287e7ef1f943600ac3e2b78bc9cd7c8", + "content-hash": "1664694fd60e8e74305716bd3253c59e", "packages": [ + { + "name": "altcha-org/altcha", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/altcha-org/altcha-lib-php.git", + "reference": "9e9e70c864a9db960d071c77c778be0c9ff1a4d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/altcha-org/altcha-lib-php/zipball/9e9e70c864a9db960d071c77c778be0c9ff1a4d0", + "reference": "9e9e70c864a9db960d071c77c778be0c9ff1a4d0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.72", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "AltchaOrg\\Altcha\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Regeci", + "email": "536331+ovx@users.noreply.github.com" + } + ], + "support": { + "issues": "https://github.com/altcha-org/altcha-lib-php/issues", + "source": "https://github.com/altcha-org/altcha-lib-php/tree/v1.3.1" + }, + "time": "2025-12-13T10:03:53+00:00" + }, { "name": "brick/math", "version": "0.14.1", @@ -776,6 +823,82 @@ ], "time": "2025-12-27T19:43:20+00:00" }, + { + "name": "grantholle/laravel-altcha", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/grantholle/laravel-altcha.git", + "reference": "c0dcc6d0805e8640d46709e5f8d05c7c65b2687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grantholle/laravel-altcha/zipball/c0dcc6d0805e8640d46709e5f8d05c7c65b2687c", + "reference": "c0dcc6d0805e8640d46709e5f8d05c7c65b2687c", + "shasum": "" + }, + "require": { + "altcha-org/altcha": "^1.3.1", + "illuminate/contracts": "^10.0|^11.0|^12.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.14.0" + }, + "require-dev": { + "laravel/pint": "^1.0", + "nunomaduro/collision": "^8.1.1||^7.10.0", + "orchestra/testbench": "^10.0.0||^9.0.0||^8.22.0", + "pestphp/pest": "^3.0||^2.0", + "pestphp/pest-plugin-arch": "^3.0||^2.0", + "pestphp/pest-plugin-laravel": "^3.0||^2.0", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Altcha": "GrantHolle\\Altcha\\Facades\\Altcha" + }, + "providers": [ + "GrantHolle\\Altcha\\AltchaServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "GrantHolle\\Altcha\\": "src/", + "GrantHolle\\Altcha\\Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grant Holle", + "email": "hollegrant@gmail.com", + "role": "Developer" + } + ], + "description": "A Laravel server implementation for Altcha.", + "homepage": "https://github.com/grantholle/laravel-altcha", + "keywords": [ + "Grant Holle", + "laravel", + "laravel-altcha" + ], + "support": { + "issues": "https://github.com/grantholle/laravel-altcha/issues", + "source": "https://github.com/grantholle/laravel-altcha/tree/2.1.1" + }, + "funding": [ + { + "url": "https://github.com/Grant Holle", + "type": "github" + } + ], + "time": "2025-12-16T03:39:06+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "7.10.0", @@ -3216,79 +3339,6 @@ }, "time": "2025-09-18T10:15:45+00:00" }, - { - "name": "mews/captcha", - "version": "3.4.7", - "source": { - "type": "git", - "url": "https://github.com/mewebstudio/captcha.git", - "reference": "2622c4f90dd621f19fe57e03e45f6f099509e839" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mewebstudio/captcha/zipball/2622c4f90dd621f19fe57e03e45f6f099509e839", - "reference": "2622c4f90dd621f19fe57e03e45f6f099509e839", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "illuminate/config": "~5|^6|^7|^8|^9|^10|^11|^12", - "illuminate/filesystem": "~5|^6|^7|^8|^9|^10|^11|^12", - "illuminate/hashing": "~5|^6|^7|^8|^9|^10|^11|^12", - "illuminate/session": "~5|^6|^7|^8|^9|^10|^11|^12", - "illuminate/support": "~5|^6|^7|^8|^9|^10|^11|^12", - "intervention/image": "^3.7", - "php": "^7.2|^8.1|^8.2|^8.3" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^8.5|^9.5.10|^10.5|^11" - }, - "type": "package", - "extra": { - "laravel": { - "aliases": { - "Captcha": "Mews\\Captcha\\Facades\\Captcha" - }, - "providers": [ - "Mews\\Captcha\\CaptchaServiceProvider" - ] - } - }, - "autoload": { - "files": [ - "src/helpers.php" - ], - "psr-4": { - "Mews\\Captcha\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Muharrem ERİN", - "email": "me@mewebstudio.com", - "homepage": "https://github.com/mewebstudio", - "role": "Developer" - } - ], - "description": "Laravel 5/6/7/8/9/10/11/12 Captcha Package", - "homepage": "https://github.com/mewebstudio/captcha", - "keywords": [ - "captcha", - "laravel12 Captcha", - "laravel12 Security", - "laravel5 Security" - ], - "support": { - "issues": "https://github.com/mewebstudio/captcha/issues", - "source": "https://github.com/mewebstudio/captcha/tree/3.4.7" - }, - "time": "2025-10-11T14:42:33+00:00" - }, { "name": "mobiledetect/mobiledetectlib", "version": "4.8.10", @@ -11673,5 +11723,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/captcha.php b/config/captcha.php deleted file mode 100644 index f11ec87..0000000 --- a/config/captcha.php +++ /dev/null @@ -1,50 +0,0 @@ - ['2', '3', '4', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'X', 'Y', 'Z'], - 'default' => [ - 'length' => 5, - 'width' => 120, - 'height' => 36, - 'quality' => 90, - 'math' => false, - 'expire' => 60, - 'encrypt' => false, - ], - 'math' => [ - 'length' => 9, - 'width' => 120, - 'height' => 36, - 'quality' => 90, - 'math' => true, - ], - - 'flat' => [ - 'length' => 6, - 'width' => 160, - 'height' => 46, - 'quality' => 90, - 'lines' => 6, - 'bgImage' => false, - 'bgColor' => '#ecf2f4', - 'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'], - 'contrast' => -5, - ], - 'mini' => [ - 'length' => 3, - 'width' => 60, - 'height' => 32, - ], - 'inverse' => [ - 'length' => 5, - 'width' => 120, - 'height' => 36, - 'quality' => 90, - 'sensitive' => true, - 'angle' => 12, - 'sharpen' => 10, - 'blur' => 2, - 'invert' => true, - 'contrast' => -5, - ] -]; diff --git a/package-lock.json b/package-lock.json index 72c23ec..c421571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "@fortawesome/fontawesome-free": "^6.5.1", "@jellyfin/libass-wasm": "^4.1.1", "@yaireo/tagify": "^4.21.2", + "altcha": "^2.3.0", "chart.js": "^4.5.0", "dashjs": "^5.0.0", "hammerjs": "^2.0.8", @@ -40,6 +41,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@altcha/crypto": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@altcha/crypto/-/crypto-0.0.1.tgz", + "integrity": "sha512-qZMdnoD3lAyvfSUMNtC2adRi666Pxdcw9zqfMU5qBOaJWqpN9K+eqQGWqeiKDMqL0SF+EytNG4kR/Pr/99GJ6g==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -1161,6 +1168,31 @@ "@vue/reactivity": "~3.1.1" } }, + "node_modules/altcha": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/altcha/-/altcha-2.3.0.tgz", + "integrity": "sha512-vl8I0dQvSQB7/Mx09XuWZ1+LdSP7vEda6OLbg9kUQ2ZO2LT7MzgUyLK7Iips+GAV6c0ntVcS1XWOqhEPpwbDhQ==", + "license": "MIT", + "dependencies": { + "@altcha/crypto": "^0.0.1" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "4.18.0" + } + }, + "node_modules/altcha/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -3064,6 +3096,15 @@ "postcss": "^8.0.9" } }, + "node_modules/tw-elements/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/ua-parser-js": { "version": "1.0.41", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", @@ -3302,15 +3343,6 @@ "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "license": "MIT" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } } } } diff --git a/package.json b/package.json index bbb40f4..c5fdac1 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@fortawesome/fontawesome-free": "^6.5.1", "@jellyfin/libass-wasm": "^4.1.1", "@yaireo/tagify": "^4.21.2", + "altcha": "^2.3.0", "chart.js": "^4.5.0", "dashjs": "^5.0.0", "hammerjs": "^2.0.8", diff --git a/resources/css/app.css b/resources/css/app.css index 8f2396e..e4367ec 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -122,4 +122,33 @@ input:checked~.dot { font-display: swap; src: url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff) format('woff'); unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* Captcha */ +:root { + --altcha-border-width: 1px; + --altcha-border-radius: 0.375rem; + --altcha-color-base: #333; + --altcha-color-border: #a0a0a0; + --altcha-color-text: #fff; + --altcha-color-border-focus: currentColor; + --altcha-color-error-text: #f23939; + --altcha-color-footer-bg: #141414; + --altcha-max-width: 260px; +} + +.altcha-footer { + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; +} + +input[type="checkbox"] { + background-color: #ffffff; + border-color: #a0a0a0; + color: rgb(225,29,72); +} + +input[type="checkbox"]:checked { + background-color: rgb(225,29,72); + box-shadow: 0 0 0 0px #fff, 0 0 0 calc(2px + 0px) rgba(246, 59, 118, 0.5), 0 0 #0000; } \ No newline at end of file diff --git a/resources/js/app.js b/resources/js/app.js index d163bbc..ec2e288 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -12,6 +12,9 @@ import { initTE, } from "tw-elements"; +// Captcha +import 'altcha'; + // import Alpine from 'alpinejs'; // window.Alpine = Alpine; diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index f422cda..8126840 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -69,6 +69,11 @@ +
+ + +
+
@if (Route::has('password.request')) @@ -127,6 +132,11 @@
+
+ + +
+
{{ __('Register') }} diff --git a/resources/views/contact/partials/submit-contact-form.blade.php b/resources/views/contact/partials/submit-contact-form.blade.php index 9a31808..5e8615e 100644 --- a/resources/views/contact/partials/submit-contact-form.blade.php +++ b/resources/views/contact/partials/submit-contact-form.blade.php @@ -33,19 +33,7 @@
-
- -
-
- {!! captcha_img() !!} -
- -
-
- -
+
{{ __('Submit') }} @@ -65,18 +53,4 @@ @endif
- - \ No newline at end of file diff --git a/resources/views/modals/partials/download-captcha.blade.php b/resources/views/modals/partials/download-captcha.blade.php index fe12ae7..9a713b6 100644 --- a/resources/views/modals/partials/download-captcha.blade.php +++ b/resources/views/modals/partials/download-captcha.blade.php @@ -8,18 +8,7 @@

-
- {!! captcha_img() !!} -
- -
-
- - +

@@ -51,21 +40,12 @@ diff --git a/routes/web.php b/routes/web.php index 82c8eb0..77b1674 100644 --- a/routes/web.php +++ b/routes/web.php @@ -42,9 +42,6 @@ Route::post('/contact', [ContactController::class, 'store'])->name('contact.stor Route::get('/playlists', [PlaylistController::class, 'index'])->name('playlist.index'); Route::get('/playlist/{playlist_id}', [PlaylistController::class, 'show'])->name('playlist.show'); -// Captcha Reload -Route::get('/reload-captcha', [ContactController::class, 'reloadCaptcha']); - // Download Route::post('/get-download', [DownloadApiController::class, 'getDownload']);