diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 85448c4..f323392 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -18,9 +18,59 @@ use Illuminate\Validation\Rule; use Laravel\Socialite\Facades\Socialite; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Password; +use Illuminate\Auth\Events\PasswordReset; class UserController extends Controller { + public function requestPasswordReset(Request $request) +{ + $request->validate([ + 'email' => 'required|email', + ]); + + $status = Password::sendResetLink( + $request->only('email') + ); + + if ($status === Password::RESET_LINK_SENT) { + return response()->json(['message' => __($status)]); + } + + return response()->json(['message' => __($status)], 400); +} +public function showResetForm($token) +{ + return view('auth.reset-password', ['token' => $token]); +} + +public function resetPassword(Request $request) +{ + $request->validate([ + 'email' => 'required|email', + 'token' => 'required|string', + 'password' => 'required|string|confirmed|min:6', + ]); + + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user, $password) { + $user->forceFill([ + 'password' => $password, + ])->setRememberToken(Str::random(60)); + + $user->save(); + + event(new PasswordReset($user)); + } + ); + + if ($status === Password::PASSWORD_RESET) { + return response()->json(['message' => __($status)]); + } + + return response()->json(['message' => __($status)], 400); +} public function login(Request $request) { $data = $request->validate([ diff --git a/composer.json b/composer.json index c05daea..1a7fba2 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "laravel/framework": "^12.0", "laravel/sanctum": "^4.0", "laravel/socialite": "^5.6", - "laravel/telescope": "^5.11", "laravel/tinker": "^2.8", "masbug/flysystem-google-drive-ext": "^2.2", "predis/predis": "^2.1", @@ -24,10 +23,10 @@ "fakerphp/faker": "^1.9.1", "laravel/pint": "^1.0", "laravel/sail": "^1.18", + "laravel/telescope": "^5.11", "mockery/mockery": "^1.4.4", "phpunit/phpunit": "^10.1", "spatie/laravel-ignition": "^2.0" - }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index e1ea2fd..31e46d2 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": "9014e7db7181477837583be158dad2c3", + "content-hash": "93b523c73b637a7401d21d02b2b72234", "packages": [ { "name": "beste/clock", @@ -2850,75 +2850,6 @@ }, "time": "2025-07-23T14:16:08+00:00" }, - { - "name": "laravel/telescope", - "version": "v5.11.3", - "source": { - "type": "git", - "url": "https://github.com/laravel/telescope.git", - "reference": "7684604e104e7755b70dcacfeee06888e2470689" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/telescope/zipball/7684604e104e7755b70dcacfeee06888e2470689", - "reference": "7684604e104e7755b70dcacfeee06888e2470689", - "shasum": "" - }, - "require": { - "ext-json": "*", - "laravel/framework": "^8.37|^9.0|^10.0|^11.0|^12.0", - "php": "^8.0", - "symfony/console": "^5.3|^6.0|^7.0", - "symfony/var-dumper": "^5.0|^6.0|^7.0" - }, - "require-dev": { - "ext-gd": "*", - "guzzlehttp/guzzle": "^6.0|^7.0", - "laravel/octane": "^1.4|^2.0|dev-develop", - "orchestra/testbench": "^6.40|^7.37|^8.17|^9.0|^10.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.0|^10.5|^11.5" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Telescope\\TelescopeServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Telescope\\": "src/", - "Laravel\\Telescope\\Database\\Factories\\": "database/factories/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Mohamed Said", - "email": "mohamed@laravel.com" - } - ], - "description": "An elegant debug assistant for the Laravel framework.", - "keywords": [ - "debugging", - "laravel", - "monitoring" - ], - "support": { - "issues": "https://github.com/laravel/telescope/issues", - "source": "https://github.com/laravel/telescope/tree/v5.11.3" - }, - "time": "2025-08-21T14:25:40+00:00" - }, { "name": "laravel/tinker", "version": "v2.10.1", @@ -9608,6 +9539,75 @@ }, "time": "2025-08-25T19:28:31+00:00" }, + { + "name": "laravel/telescope", + "version": "v5.11.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/telescope.git", + "reference": "7684604e104e7755b70dcacfeee06888e2470689" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/telescope/zipball/7684604e104e7755b70dcacfeee06888e2470689", + "reference": "7684604e104e7755b70dcacfeee06888e2470689", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel/framework": "^8.37|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0", + "symfony/console": "^5.3|^6.0|^7.0", + "symfony/var-dumper": "^5.0|^6.0|^7.0" + }, + "require-dev": { + "ext-gd": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "laravel/octane": "^1.4|^2.0|dev-develop", + "orchestra/testbench": "^6.40|^7.37|^8.17|^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.0|^10.5|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Telescope\\TelescopeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Telescope\\": "src/", + "Laravel\\Telescope\\Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Mohamed Said", + "email": "mohamed@laravel.com" + } + ], + "description": "An elegant debug assistant for the Laravel framework.", + "keywords": [ + "debugging", + "laravel", + "monitoring" + ], + "support": { + "issues": "https://github.com/laravel/telescope/issues", + "source": "https://github.com/laravel/telescope/tree/v5.11.3" + }, + "time": "2025-08-21T14:25:40+00:00" + }, { "name": "mockery/mockery", "version": "1.6.12", diff --git a/database/migrations/2018_08_08_100000_create_telescope_entries_table.php b/database/migrations/2018_08_08_100000_create_telescope_entries_table.php new file mode 100644 index 0000000..700a83f --- /dev/null +++ b/database/migrations/2018_08_08_100000_create_telescope_entries_table.php @@ -0,0 +1,70 @@ +getConnection()); + + $schema->create('telescope_entries', function (Blueprint $table) { + $table->bigIncrements('sequence'); + $table->uuid('uuid'); + $table->uuid('batch_id'); + $table->string('family_hash')->nullable(); + $table->boolean('should_display_on_index')->default(true); + $table->string('type', 20); + $table->longText('content'); + $table->dateTime('created_at')->nullable(); + + $table->unique('uuid'); + $table->index('batch_id'); + $table->index('family_hash'); + $table->index('created_at'); + $table->index(['type', 'should_display_on_index']); + }); + + $schema->create('telescope_entries_tags', function (Blueprint $table) { + $table->uuid('entry_uuid'); + $table->string('tag'); + + $table->primary(['entry_uuid', 'tag']); + $table->index('tag'); + + $table->foreign('entry_uuid') + ->references('uuid') + ->on('telescope_entries') + ->onDelete('cascade'); + }); + + $schema->create('telescope_monitoring', function (Blueprint $table) { + $table->string('tag')->primary(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $schema = Schema::connection($this->getConnection()); + + $schema->dropIfExists('telescope_entries_tags'); + $schema->dropIfExists('telescope_entries'); + $schema->dropIfExists('telescope_monitoring'); + } +}; diff --git a/database/migrations/2025_09_13_113828_create_password_resets_table.php b/database/migrations/2025_09_13_113828_create_password_resets_table.php new file mode 100644 index 0000000..4f42fe6 --- /dev/null +++ b/database/migrations/2025_09_13_113828_create_password_resets_table.php @@ -0,0 +1,28 @@ +string('email')->index(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_resets'); + } +}; diff --git a/database/migrations/2025_09_13_124813_add_remember_token_to_users_table.php b/database/migrations/2025_09_13_124813_add_remember_token_to_users_table.php new file mode 100644 index 0000000..fa2b3fe --- /dev/null +++ b/database/migrations/2025_09_13_124813_add_remember_token_to_users_table.php @@ -0,0 +1,28 @@ +rememberToken()->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('remember_token'); + }); + } +}; diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php new file mode 100644 index 0000000..c806234 --- /dev/null +++ b/resources/views/auth/reset-password.blade.php @@ -0,0 +1,117 @@ + + + + + + بازیابی رمز عبور + + + +
+

بازیابی رمز عبور

+

+ لطفاً رمز عبور جدید خود را وارد کنید +

+ +
+ + + + + +
+ + +
+ + + + diff --git a/routes/api.php b/routes/api.php index b0d5ab8..2c789f6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -23,7 +23,13 @@ Route::post('{id}', 'update'); Route::get('', 'index'); }); - + Route::prefix('auth')->controller(UserController::class)->group(function () { + Route::post('forgot-password', 'requestPasswordReset'); // send reset link + Route::post('reset-password', 'resetPassword'); // actually reset + }); + + Route::get('/reset-password/{token}', [UserController::class, 'showResetForm']) + ->name('password.reset'); Route::prefix('auth')->controller(UserController::class)->group(function () { Route::middleware(['throttle:3,1', 'auto-ban'])->post('login-otp', 'loginOTP'); Route::post('check-otp', 'checkOTP');