From 1ec7e580886da65b464c14722dbaceae20ed44b9 Mon Sep 17 00:00:00 2001 From: kosipov Date: Thu, 20 Nov 2025 00:15:17 +0300 Subject: [PATCH] add login logic --- .../Controllers/Api/CommentsController.php | 19 +++++ app/Http/Controllers/Api/PostsController.php | 67 +++++++++++++++ app/Http/Controllers/ProjectsController.php | 30 +++++++ app/Http/Controllers/UsersController.php | 25 ++++++ app/Models/Project.php | 15 ++++ app/Models/User.php | 3 +- bootstrap/app.php | 2 +- composer.json | 3 +- composer.lock | 66 ++++++++++++++- config/sanctum.php | 84 +++++++++++++++++++ ...025_10_29_154421_create_projects_table.php | 31 +++++++ ...48_create_personal_access_tokens_table.php | 33 ++++++++ routes/api.php | 34 +++++--- 13 files changed, 396 insertions(+), 16 deletions(-) create mode 100644 app/Http/Controllers/Api/CommentsController.php create mode 100644 app/Http/Controllers/Api/PostsController.php create mode 100644 app/Http/Controllers/ProjectsController.php create mode 100644 app/Http/Controllers/UsersController.php create mode 100644 app/Models/Project.php create mode 100644 config/sanctum.php create mode 100644 database/migrations/2025_10_29_154421_create_projects_table.php create mode 100644 database/migrations/2025_11_19_170048_create_personal_access_tokens_table.php diff --git a/app/Http/Controllers/Api/CommentsController.php b/app/Http/Controllers/Api/CommentsController.php new file mode 100644 index 0000000..83874a1 --- /dev/null +++ b/app/Http/Controllers/Api/CommentsController.php @@ -0,0 +1,19 @@ +json([ + 'id' => 1, + 'text' => $request->get('text'), + 'post_id' => $id + ], Response::HTTP_CREATED); + } +} diff --git a/app/Http/Controllers/Api/PostsController.php b/app/Http/Controllers/Api/PostsController.php new file mode 100644 index 0000000..1de9711 --- /dev/null +++ b/app/Http/Controllers/Api/PostsController.php @@ -0,0 +1,67 @@ +json([ + [ + 'id' => 1, + 'title' => 'My first post', + ], + [ + 'id' => 2, + 'title' => 'New post', + ] + ]); + } + + public function destroy(int $id): JsonResponse + { + // мы бы здесь написали вызов запроса delete из БД + return response()->json([ + 'success' => true, + ], Response::HTTP_ACCEPTED); + } + + public function store(Request $request): JsonResponse + { + return response()->json([ + 'title' => $request->get('title'), + 'text' => $request->get('text'), + ], Response::HTTP_CREATED); + } + + public function update(Request $request, int $id): JsonResponse + { + return response()->json([ + 'title' => $request->get('title'), + 'text' => $request->get('text'), + ], Response::HTTP_ACCEPTED); + } + + public function show(int $id): JsonResponse + { + if ($id === 5) { + return response()->json( + [ + 'status' => 'Not found' + ], + Response::HTTP_NOT_FOUND + ); + } + + return response()->json([ + 'id' => $id, + 'title' => 'My first post', + 'text' => 'My first post text', + ]); + } +} diff --git a/app/Http/Controllers/ProjectsController.php b/app/Http/Controllers/ProjectsController.php new file mode 100644 index 0000000..f1982ee --- /dev/null +++ b/app/Http/Controllers/ProjectsController.php @@ -0,0 +1,30 @@ +json(Project::all()->toJson()); + } + + public function create(Request $request) + { + $title = $request->get('title'); + $description = $request->get('description'); + $creatorUserId = 1; + + $project = new Project(); + $project->title = $title; + $project->description = $description; + $project->creator_user_id = $creatorUserId; + + $project->save(); + + return response()->json($project->toJson()); + } +} diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php new file mode 100644 index 0000000..f31c413 --- /dev/null +++ b/app/Http/Controllers/UsersController.php @@ -0,0 +1,25 @@ +get('name'); + $email = $request->get('email'); + $password = Hash::make($request->get('password')); + + $user->name = $name; + $user->email = $email; + $user->password = $password; + $user->save(); + + return ['token' => $user->createToken('frontend')->plainTextToken]; + } +} diff --git a/app/Models/Project.php b/app/Models/Project.php new file mode 100644 index 0000000..372c516 --- /dev/null +++ b/app/Models/Project.php @@ -0,0 +1,15 @@ + */ - use HasFactory, Notifiable; + use HasFactory, Notifiable, HasApiTokens; /** * The attributes that are mass assignable. diff --git a/bootstrap/app.php b/bootstrap/app.php index fbe249f..da33920 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -8,8 +8,8 @@ return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', - api: __DIR__.'/../routes/api.php', health: '/up', + api: __DIR__.'/../routes/api.php', ) ->withMiddleware(function (Middleware $middleware) { // diff --git a/composer.json b/composer.json index 88b33c4..96007e4 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", + "laravel/sanctum": "^4.0", "laravel/tinker": "^2.10.1" }, "require-dev": { @@ -71,4 +72,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 137e05a..63f30d8 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": "88970a0117c062eed55fa8728fc43833", + "content-hash": "85c1d2065f70e38b0d6bf66559fb13c5", "packages": [ { "name": "brick/math", @@ -1328,6 +1328,70 @@ }, "time": "2025-02-11T13:34:40+00:00" }, + { + "name": "laravel/sanctum", + "version": "v4.2.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/fd6df4f79f48a72992e8d29a9c0ee25422a0d677", + "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "php": "^8.2", + "symfony/console": "^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2025-07-09T19:45:24+00:00" + }, { "name": "laravel/serializable-closure", "version": "v2.0.4", diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..44527d6 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,84 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort(), + // Sanctum::currentRequestHost(), + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/database/migrations/2025_10_29_154421_create_projects_table.php b/database/migrations/2025_10_29_154421_create_projects_table.php new file mode 100644 index 0000000..2685376 --- /dev/null +++ b/database/migrations/2025_10_29_154421_create_projects_table.php @@ -0,0 +1,31 @@ +id(); + $table->timestamps(); + $table->string('title'); + $table->text('description'); + $table->foreignId('creator_user_id')->constrained('users'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('projects'); + } +}; diff --git a/database/migrations/2025_11_19_170048_create_personal_access_tokens_table.php b/database/migrations/2025_11_19_170048_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/database/migrations/2025_11_19_170048_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->text('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable()->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/routes/api.php b/routes/api.php index 1aebfde..3abbb52 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,17 +1,27 @@ json([ - 'status' => 'success',]); +Route::prefix('posts')->group(function () { + Route::get('/', [PostsController::class, 'index']); + Route::post('/', [PostsController::class, 'store']); + Route::prefix('{id}')->group(function () { + Route::delete('/', [PostsController::class, 'destroy']); + Route::put('/', [PostsController::class, 'update']); + Route::get('/', [PostsController::class, 'show']); + + Route::prefix('comments')->group(function () { + Route::post('/', [CommentsController::class, 'store']); + + }); + }); }); -Route::get('/test-level', function () { - return response()->json([ - 'level1' => [ - [ - 'level2' => [ - 'level3' => 'success', - ]] - ],]); -}); \ No newline at end of file + +Route::get('projects', [ProjectsController::class, 'index']); +Route::post('projects', [ProjectsController::class, 'create']); + +Route::post('users', [UsersController::class, 'create']);