diff --git a/app/Http/Controllers/Api/PokemonController.php b/app/Http/Controllers/Api/PokemonController.php new file mode 100644 index 00000000..7af74757 --- /dev/null +++ b/app/Http/Controllers/Api/PokemonController.php @@ -0,0 +1,95 @@ +json(['pokemons' => $pokemons], 200); + } + //show: obtiene un pokemon por id + public function show($id) + { + $pokemon = pokemon::find($id); + if ($pokemon) { + return response()->json(['pokemon' => $pokemon], 200); + } else { + return response()->json(['message' => 'Pokemon not found'], 404); + } + } + //store: crea un nuevo pokemon + public function store(Request $request) + { + $validator = Validator::make($request->all(), [ + 'name' => 'required|string|max:255', + 'image' => 'required|string|url', + ]); + + if ($validator->fails()) { + return response()->json(['error' => $validator->errors()], 422); + } + + $pokemon = pokemon::create($request->all()); + return response()->json(['pokemon' => $pokemon], 201); + } + //update: actualiza un pokemon por id + public function update(Request $request, $id) + { + $pokemon = pokemon::find($id); + if ($pokemon) { + $validator = Validator::make($request->all(), [ + 'name' => 'string|max:255', + 'image' => 'url', + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } + + $pokemon->update($request->all()); + return response()->json(['pokemon' => $pokemon], 200); + } else { + return response()->json(['message' => 'Pokemon not found'], 404); + } + } + //updatePartial: actualiza parcialmente un pokemon por id + public function updatePartial(Request $request, $id) + { + $pokemon = pokemon::find($id); + if ($pokemon) { + $validator = Validator::make($request->all(), [ + 'name' => 'string|max:255', + 'image' => 'url', + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } + + $pokemon->update($request->only(array_keys($request->all()))); + return response()->json(['pokemon' => $pokemon], 200); + } else { + return response()->json(['message' => 'Pokemon not found'], 404); + } + } + //destroy: elimina un pokemon por id + public function destroy($id) + { + $pokemon = pokemon::find($id); + if ($pokemon) { + $pokemon->delete(); + return response()->json(['message' => 'Pokemon deleted successfully'], 200); + } else { + return response()->json(['message' => 'Pokemon not found'], 404); + } + } +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php new file mode 100644 index 00000000..38eafb1d --- /dev/null +++ b/app/Http/Controllers/AuthController.php @@ -0,0 +1,107 @@ +all(), [ + 'name' => 'required|string|max:255', + 'email' => 'required|string|email|max:255|unique:users', + 'password' => 'required|string|min:8|confirmed', + 'role' => 'string|in:user,admin', + ]); + if ($validator->fails()) { + return response()->json($validator->errors(), 422); + } + //create user + $user = \App\Models\User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => bcrypt($request->password), + 'role' => $request->role ? $request->role : 'user', + ]); + //return user + return response()->json([ + 'user' => $user, + 'message' => 'User created successfully', + ], 201); + } + + public function login(Request $request) + { + //validate the request with library + $validator = Validator::make($request->all(), [ + 'email' => 'required|string|email', + 'password' => 'required|string|min:8', + ]); + if ($validator->fails()) { + return response()->json($validator->errors(), 422); + } + //check if user exists + $user = \App\Models\User::where('email', $request->email)->first(); + if (!$user || !Hash::check($request->password, $user->password)) { + return response()->json([ + 'message' => 'Invalid credentials', + ], 401); + } + //create token + $token = JWTAuth::attempt($request->only('email', 'password')); + return response()->json([ + 'token' => $token, + 'user' => $user, + ]); + //validate the credentials with library + try { + if(!$token=JWTAuth::attempt($credentials)){ + return response()->json(['error' => 'invalid_credentials'], 401); + + } + return response()->json([ + 'message' => 'User logged in successfully', + 'token' => $token, + ], 200); + } catch (JWTException $e) { + return response()->json(['error' => 'could_not_create_token'], 500); + } + + + } + + //get user + public function getUser() + { + $user= Auth::user(); + return response()->json([ + 'message' => 'User retrieved successfully', + 'data' => $user, + ],200); + } + + //logout + public function logout() + { + try { + JWTAuth::invalidate(JWTAuth::getToken()); + return response()->json([ + 'message' => 'User logged out successfully', + ], 200); + } catch (JWTException $e) { + return response()->json([ + 'message' => 'Failed to logout, please try again', + ], 500); + } + } + + + +} diff --git a/app/Http/Middleware/IsAdmin.php b/app/Http/Middleware/IsAdmin.php new file mode 100644 index 00000000..887bbba0 --- /dev/null +++ b/app/Http/Middleware/IsAdmin.php @@ -0,0 +1,27 @@ +user(); + if ($user && $user->role == 'admin') { + return $next($request); + } else { + return response()->json([ + 'message' => 'Unauthorized' + ], 403); + } + } +} diff --git a/app/Http/Middleware/IsUserAuth.php b/app/Http/Middleware/IsUserAuth.php new file mode 100644 index 00000000..9f7ec99e --- /dev/null +++ b/app/Http/Middleware/IsUserAuth.php @@ -0,0 +1,27 @@ +user()){ + return $next($request); + }else{ + return response()->json([ + 'message' => 'Unauthorized' + ], 401); + } + + } +} diff --git a/app/Models/User.php b/app/Models/User.php index def621f4..6a323b08 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,8 +6,9 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Tymon\JWTAuth\Contracts\JWTSubject; -class User extends Authenticatable +class User extends Authenticatable implements JWTSubject { use HasFactory, Notifiable; @@ -20,6 +21,7 @@ class User extends Authenticatable 'name', 'email', 'password', + 'role', ]; /** @@ -44,4 +46,19 @@ protected function casts(): array 'password' => 'hashed', ]; } + public function getJWTIdentifier() + { + return $this->getKey(); + } + + /** + * Return a key value array, containing any custom claims to be added to the JWT. + * + * @return array + */ + public function getJWTCustomClaims() + { + return []; + } } + diff --git a/app/Models/pokemon.php b/app/Models/pokemon.php new file mode 100644 index 00000000..183cb7df --- /dev/null +++ b/app/Models/pokemon.php @@ -0,0 +1,18 @@ +withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) diff --git a/composer.json b/composer.json index 4b7e1832..632be726 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,9 @@ "require": { "php": "^8.2", "laravel/framework": "^11.9", - "laravel/tinker": "^2.9" + "laravel/sanctum": "^4.0", + "laravel/tinker": "^2.9", + "tymon/jwt-auth": "^2.2" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index 54822899..93b64632 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": "7e8c3c14ff33b199b4a0838993eb8423", + "content-hash": "dcaf67af36ac756c5834645d100333a1", "packages": [ { "name": "brick/math", @@ -1322,6 +1322,70 @@ }, "time": "2024-09-30T14:27:51+00:00" }, + { + "name": "laravel/sanctum", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/ec1dd9ddb2ab370f79dfe724a101856e0963f43c", + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c", + "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-01-26T19:34:36+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.5", @@ -1449,6 +1513,144 @@ }, "time": "2024-09-23T13:32:56+00:00" }, + { + "name": "lcobucci/clock", + "version": "3.3.1", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.29", + "lcobucci/coding-standard": "^11.1.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^11.3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.3.1" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2024-09-24T20:45:14+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "lcobucci/clock": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.21", + "lcobucci/coding-standard": "^6.0", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/php-invoker": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-01-02T13:28:00+00:00" + }, { "name": "league/commonmark", "version": "2.5.3", @@ -5396,6 +5598,90 @@ }, "time": "2023-12-08T13:03:43+00:00" }, + { + "name": "tymon/jwt-auth", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/tymondesigns/jwt-auth.git", + "reference": "42381e56db1bf887c12e5302d11901d65cc74856" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tymondesigns/jwt-auth/zipball/42381e56db1bf887c12e5302d11901d65cc74856", + "reference": "42381e56db1bf887c12e5302d11901d65cc74856", + "shasum": "" + }, + "require": { + "illuminate/auth": "^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^9.0|^10.0|^11.0|^12.0", + "lcobucci/jwt": "^4.0", + "nesbot/carbon": "^2.69|^3.0", + "php": "^8.0" + }, + "require-dev": { + "illuminate/console": "^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^9.0|^10.0|^11.0|^12.0", + "illuminate/routing": "^9.0|^10.0|^11.0|^12.0", + "mockery/mockery": "^1.6", + "phpunit/phpunit": "^9.4" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "JWTAuth": "Tymon\\JWTAuth\\Facades\\JWTAuth", + "JWTFactory": "Tymon\\JWTAuth\\Facades\\JWTFactory" + }, + "providers": [ + "Tymon\\JWTAuth\\Providers\\LaravelServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.0-dev", + "dev-develop": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Tymon\\JWTAuth\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sean Tymon", + "email": "tymon148@gmail.com", + "homepage": "https://tymon.xyz", + "role": "Developer" + } + ], + "description": "JSON Web Token Authentication for Laravel and Lumen", + "homepage": "https://github.com/tymondesigns/jwt-auth", + "keywords": [ + "Authentication", + "JSON Web Token", + "auth", + "jwt", + "laravel" + ], + "support": { + "issues": "https://github.com/tymondesigns/jwt-auth/issues", + "source": "https://github.com/tymondesigns/jwt-auth" + }, + "funding": [ + { + "url": "https://www.patreon.com/seantymon", + "type": "patreon" + } + ], + "time": "2025-04-16T22:22:54+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.6.1", diff --git a/config/auth.php b/config/auth.php index 0ba5d5d8..80b200df 100644 --- a/config/auth.php +++ b/config/auth.php @@ -14,7 +14,7 @@ */ 'defaults' => [ - 'guard' => env('AUTH_GUARD', 'web'), + 'guard' => env('AUTH_GUARD', 'api'), 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), ], @@ -40,6 +40,10 @@ 'driver' => 'session', 'provider' => 'users', ], + 'api' => [ + 'driver' => 'jwt', + 'provider' => 'users', + ], ], /* diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 00000000..f83234d1 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Secret + |-------------------------------------------------------------------------- + | + | Don't forget to set this in your .env file, as it will be used to sign + | your tokens. A helper command is provided for this: + | `php artisan jwt:secret` + | + | Note: This will be used for Symmetric algorithms only (HMAC), + | since RSA and ECDSA use a private/public key combo (See below). + | + */ + + 'secret' => env('JWT_SECRET'), + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Keys + |-------------------------------------------------------------------------- + | + | The algorithm you are using, will determine whether your tokens are + | signed with a random string (defined in `JWT_SECRET`) or using the + | following public & private keys. + | + | Symmetric Algorithms: + | HS256, HS384 & HS512 will use `JWT_SECRET`. + | + | Asymmetric Algorithms: + | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. + | + */ + + 'keys' => [ + + /* + |-------------------------------------------------------------------------- + | Public Key + |-------------------------------------------------------------------------- + | + | A path or resource to your public key. + | + | E.g. 'file://path/to/public/key' + | + */ + + 'public' => env('JWT_PUBLIC_KEY'), + + /* + |-------------------------------------------------------------------------- + | Private Key + |-------------------------------------------------------------------------- + | + | A path or resource to your private key. + | + | E.g. 'file://path/to/private/key' + | + */ + + 'private' => env('JWT_PRIVATE_KEY'), + + /* + |-------------------------------------------------------------------------- + | Passphrase + |-------------------------------------------------------------------------- + | + | The passphrase for your private key. Can be null if none set. + | + */ + + 'passphrase' => env('JWT_PASSPHRASE'), + + ], + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour. + | + | You can also set this to null, to yield a never expiring token. + | Some people may want this behaviour for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. + | + */ + + 'ttl' => env('JWT_TTL', 60), + + /* + |-------------------------------------------------------------------------- + | Refresh time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token can be refreshed + | within. I.E. The user can refresh their token within a 2 week window of + | the original token being created until they must re-authenticate. + | Defaults to 2 weeks. + | + | You can also set this to null, to yield an infinite refresh time. + | Some may want this instead of never expiring tokens for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | + */ + + 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), + + /* + |-------------------------------------------------------------------------- + | JWT hashing algorithm + |-------------------------------------------------------------------------- + | + | Specify the hashing algorithm that will be used to sign the token. + | + */ + + 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256), + + /* + |-------------------------------------------------------------------------- + | Required Claims + |-------------------------------------------------------------------------- + | + | Specify the required claims that must exist in any token. + | A TokenInvalidException will be thrown if any of these claims are not + | present in the payload. + | + */ + + 'required_claims' => [ + 'iss', + 'iat', + 'exp', + 'nbf', + 'sub', + 'jti', + ], + + /* + |-------------------------------------------------------------------------- + | Persistent Claims + |-------------------------------------------------------------------------- + | + | Specify the claim keys to be persisted when refreshing a token. + | `sub` and `iat` will automatically be persisted, in + | addition to the these claims. + | + | Note: If a claim does not exist then it will be ignored. + | + */ + + 'persistent_claims' => [ + // 'foo', + // 'bar', + ], + + /* + |-------------------------------------------------------------------------- + | Lock Subject + |-------------------------------------------------------------------------- + | + | This will determine whether a `prv` claim is automatically added to + | the token. The purpose of this is to ensure that if you have multiple + | authentication models e.g. `App\User` & `App\OtherPerson`, then we + | should prevent one authentication request from impersonating another, + | if 2 tokens happen to have the same id across the 2 different models. + | + | Under specific circumstances, you may want to disable this behaviour + | e.g. if you only have one authentication model, then you would save + | a little on token size. + | + */ + + 'lock_subject' => true, + + /* + |-------------------------------------------------------------------------- + | Leeway + |-------------------------------------------------------------------------- + | + | This property gives the jwt timestamp claims some "leeway". + | Meaning that if you have any unavoidable slight clock skew on + | any of your servers then this will afford you some level of cushioning. + | + | This applies to the claims `iat`, `nbf` and `exp`. + | + | Specify in seconds - only if you know you need it. + | + */ + + 'leeway' => env('JWT_LEEWAY', 0), + + /* + |-------------------------------------------------------------------------- + | Blacklist Enabled + |-------------------------------------------------------------------------- + | + | In order to invalidate tokens, you must have the blacklist enabled. + | If you do not want or need this functionality, then set this to false. + | + */ + + 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), + + /* + | ------------------------------------------------------------------------- + | Blacklist Grace Period + | ------------------------------------------------------------------------- + | + | When multiple concurrent requests are made with the same JWT, + | it is possible that some of them fail, due to token regeneration + | on every request. + | + | Set grace period in seconds to prevent parallel request failure. + | + */ + + 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), + + /* + |-------------------------------------------------------------------------- + | Cookies encryption + |-------------------------------------------------------------------------- + | + | By default Laravel encrypt cookies for security reason. + | If you decide to not decrypt cookies, you will have to configure Laravel + | to not encrypt your cookie token by adding its name into the $except + | array available in the middleware "EncryptCookies" provided by Laravel. + | see https://laravel.com/docs/master/responses#cookies-and-encryption + | for details. + | + | Set it to true if you want to decrypt cookies. + | + */ + + 'decrypt_cookies' => false, + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist. + | + */ + + 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, + + ], + +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 00000000..764a82fa --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | 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/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 05fb5d9e..422011e6 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -19,6 +19,7 @@ public function up(): void $table->string('password'); $table->rememberToken(); $table->timestamps(); + $table->string('role',20)->default('user'); }); Schema::create('password_reset_tokens', function (Blueprint $table) { diff --git a/database/migrations/2025_04_03_161241_create_personal_access_tokens_table.php b/database/migrations/2025_04_03_161241_create_personal_access_tokens_table.php new file mode 100644 index 00000000..e828ad81 --- /dev/null +++ b/database/migrations/2025_04_03_161241_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2025_04_03_162213_create_student_table.php b/database/migrations/2025_04_03_162213_create_student_table.php new file mode 100644 index 00000000..dc73b7f7 --- /dev/null +++ b/database/migrations/2025_04_03_162213_create_student_table.php @@ -0,0 +1,31 @@ +id(); + $table->timestamps(); + $table->string('name'); + $table->string('email')->unique(); + $table->string('phone')->nullable(); + $table->string('address')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('student'); + } +}; diff --git a/database/migrations/2025_04_24_161700_create_pokemons_table.php b/database/migrations/2025_04_24_161700_create_pokemons_table.php new file mode 100644 index 00000000..dfd137c9 --- /dev/null +++ b/database/migrations/2025_04_24_161700_create_pokemons_table.php @@ -0,0 +1,29 @@ +id(); + // $table->timestamps(); + $table->string('name'); + $table->string('image'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pokemons'); + } +}; diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 00000000..d76b524e --- /dev/null +++ b/routes/api.php @@ -0,0 +1,67 @@ +user(); +})->middleware('auth:sanctum'); + + + +// Rutas de la API --------------------------------------- + +//Devuelve todos los Pokemons +Route::get('/pokemon', [PokemonController::class, 'index']); + +//Devuelve un Pokemon por ID +Route::get('/pokemon/{id}', [PokemonController::class, 'show']); + +//Crea un Pokemon +Route::post('/pokemon', [PokemonController::class, 'store']); + +//Actualiza un Pokemon +Route::put('/pokemon/{id}', [PokemonController::class, 'update']); + +//Elimina un Pokemon +Route::delete('/pokemon/{id}', [PokemonController::class, 'destroy']); + +//Actualiza parcialmente un Pokemon +Route::patch('/pokemon/{id}', [PokemonController::class, 'updatePartial']); + +//PUBLIC ROUTES +Route::post('register', [AuthController::class, 'register']); +Route::post('login', [AuthController::class, 'login']); +Route::get('/pokemon', [PokemonController::class, 'index']); +Route::get('/pokemon/{id}', [PokemonController::class, 'show']); + + + +//PROTECTED ROUTES +Route::middleware([IsUserAuth::class])->group(function () { + Route::get('me', [AuthController::class, 'getUser']); + Route::post('/logout', [AuthController::class, 'logout']); + Route::post('/pokemon', [PokemonController::class, 'store']); +}); + +//ADMIN ROUTES +Route::middleware(\App\Http\Middleware\IsAdmin::class)->group(function () { + Route::post('/pokemon', [PokemonController::class, 'store']); + Route::put('/pokemon/{id}', [PokemonController::class, 'update']); + Route::delete('/pokemon/{id}', [PokemonController::class, 'destroy']); + Route::patch('/pokemon/{id}', [PokemonController::class, 'updatePartial']); +}); + + +?> + + + + + + diff --git a/routes/web.php b/routes/web.php index 86a06c53..e97e8ca8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,7 +1,29 @@ input('numero1'); + $numero2 = $request->input('numero2'); + $resultado = $numero1 + $numero2; + + return view('sumar', ['resultado' => $resultado]); +}); + + + +//web.php son rutas que devuelven vistas +?> +