From a30dbdfbbaff5572da91578f41dd40d9bec3f0b7 Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Sat, 4 Apr 2026 19:10:05 +0100 Subject: [PATCH] feat: install Sanctum, scaffold api.php, add FuelType::fromAlias() Co-Authored-By: Claude Sonnet 4.6 --- app/Enums/FuelType.php | 23 +++-- bootstrap/app.php | 1 + config/sanctum.php | 87 +++++++++++++++++++ ...49_create_personal_access_tokens_table.php | 33 +++++++ routes/api.php | 20 +++++ tests/Unit/Enums/FuelTypeTest.php | 28 ++++++ 6 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 config/sanctum.php create mode 100644 database/migrations/2026_04_04_180849_create_personal_access_tokens_table.php create mode 100644 routes/api.php create mode 100644 tests/Unit/Enums/FuelTypeTest.php diff --git a/app/Enums/FuelType.php b/app/Enums/FuelType.php index 8786df6..49bba56 100644 --- a/app/Enums/FuelType.php +++ b/app/Enums/FuelType.php @@ -4,15 +4,28 @@ namespace App\Enums; enum FuelType: string { - case E10 = 'e10'; - case E5 = 'e5'; + case E10 = 'e10'; + case E5 = 'e5'; case B7Standard = 'b7_standard'; - case B7Premium = 'b7_premium'; - case B10 = 'b10'; - case Hvo = 'hvo'; + case B7Premium = 'b7_premium'; + case B10 = 'b10'; + case Hvo = 'hvo'; public static function fromApiValue(string $value): self { return self::from(strtolower($value)); } + + public static function fromAlias(string $alias): self + { + return match (strtolower($alias)) { + 'diesel', 'b7_standard' => self::B7Standard, + 'premium_diesel', 'b7_premium' => self::B7Premium, + 'petrol', 'unleaded', 'e10' => self::E10, + 'premium_unleaded', 'e5' => self::E5, + 'b10' => self::B10, + 'hvo' => self::Hvo, + default => throw new \ValueError("Unknown fuel type alias: {$alias}"), + }; + } } diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..c3928c5 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..cde73cf --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,87 @@ + 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' => AuthenticateSession::class, + 'encrypt_cookies' => EncryptCookies::class, + 'validate_csrf_token' => ValidateCsrfToken::class, + ], + +]; diff --git a/database/migrations/2026_04_04_180849_create_personal_access_tokens_table.php b/database/migrations/2026_04_04_180849_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/database/migrations/2026_04_04_180849_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 new file mode 100644 index 0000000..7039e84 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,20 @@ +group(function (): void { + Route::post('/register', [AuthController::class, 'register']); + Route::post('/login', [AuthController::class, 'login']); + Route::middleware('auth:sanctum')->group(function (): void { + Route::post('/logout', [AuthController::class, 'logout']); + Route::get('/me', [AuthController::class, 'me']); + }); +}); diff --git a/tests/Unit/Enums/FuelTypeTest.php b/tests/Unit/Enums/FuelTypeTest.php new file mode 100644 index 0000000..c7f7261 --- /dev/null +++ b/tests/Unit/Enums/FuelTypeTest.php @@ -0,0 +1,28 @@ +toBe(FuelType::B7Standard); +}); + +it('resolves petrol alias to E10', function () { + expect(FuelType::fromAlias('petrol'))->toBe(FuelType::E10); +}); + +it('resolves unleaded alias to E10', function () { + expect(FuelType::fromAlias('unleaded'))->toBe(FuelType::E10); +}); + +it('resolves premium_unleaded alias to E5', function () { + expect(FuelType::fromAlias('premium_unleaded'))->toBe(FuelType::E5); +}); + +it('accepts canonical enum values as aliases', function () { + expect(FuelType::fromAlias('e10'))->toBe(FuelType::E10); + expect(FuelType::fromAlias('b7_standard'))->toBe(FuelType::B7Standard); +}); + +it('throws ValueError for unknown alias', function () { + FuelType::fromAlias('avgas'); +})->throws(ValueError::class);