diff --git a/backend/app/Providers/AppServiceProvider.php b/backend/app/Providers/AppServiceProvider.php index 452e6b6..c70e47e 100644 --- a/backend/app/Providers/AppServiceProvider.php +++ b/backend/app/Providers/AppServiceProvider.php @@ -2,23 +2,29 @@ namespace App\Providers; +use App\Auth\BcryptPasswordHasher; +use App\Auth\Clock; +use App\Auth\PasswordHasher; +use App\Auth\RandomTokenGenerator; +use App\Auth\SystemClock; +use App\Auth\TokenGenerator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { - /** - * Register any application services. - */ public function register(): void { - // - } - - /** - * Bootstrap any application services. - */ - public function boot(): void - { - // + $this->app->bind( + PasswordHasher::class, + BcryptPasswordHasher::class, + ); + $this->app->bind( + TokenGenerator::class, + RandomTokenGenerator::class, + ); + $this->app->bind( + Clock::class, + SystemClock::class, + ); } } diff --git a/backend/app/Providers/RepositoryServiceProvider.php b/backend/app/Providers/RepositoryServiceProvider.php new file mode 100644 index 0000000..b4f42b2 --- /dev/null +++ b/backend/app/Providers/RepositoryServiceProvider.php @@ -0,0 +1,24 @@ +app->bind( + UserRepository::class, + EloquentUserRepository::class + ); + $this->app->bind( + SessionRepository::class, + EloquentSessionRepository::class + ); + } +} diff --git a/backend/bootstrap/app.php b/backend/bootstrap/app.php index cb7c01a..aea4ba1 100644 --- a/backend/bootstrap/app.php +++ b/backend/bootstrap/app.php @@ -6,7 +6,8 @@ use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( - commands: __DIR__.'/../routes/console.php', + api: __DIR__ . '/../routes/api.php', + commands: __DIR__ . '/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { diff --git a/backend/bootstrap/providers.php b/backend/bootstrap/providers.php index fc94ae6..37b94e7 100644 --- a/backend/bootstrap/providers.php +++ b/backend/bootstrap/providers.php @@ -1,7 +1,9 @@ ['login', 'logout', 'me'], + 'paths' => ['api/*'], 'allowed_methods' => ['GET', 'POST', 'OPTIONS'], 'allowed_origins' => $allowedOrigins, 'allowed_origins_patterns' => [], - 'allowed_headers' => ['Content-Type', 'X-Requested-With', 'Accept', 'Origin'], + 'allowed_headers' => [ + 'Content-Type', + 'X-Requested-With', + 'Accept', + 'Origin', + ], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => true, diff --git a/backend/database/migrations/2026_05_23_194646_users_table.php b/backend/database/migrations/2026_05_23_194646_users_table.php new file mode 100644 index 0000000..d699783 --- /dev/null +++ b/backend/database/migrations/2026_05_23_194646_users_table.php @@ -0,0 +1,22 @@ +id(); + $table->string('email')->unique(); + $table->string('password_hash'); + }); + } + + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/backend/database/migrations/2026_05_23_194647_sessions_table.php b/backend/database/migrations/2026_05_23_194647_sessions_table.php new file mode 100644 index 0000000..51b4603 --- /dev/null +++ b/backend/database/migrations/2026_05_23_194647_sessions_table.php @@ -0,0 +1,23 @@ +string('token')->primary(); + $table->foreignId('user_id')->constrained('users'); + $table->dateTime('created_at'); + $table->dateTime('expires_at'); + }); + } + + public function down(): void + { + Schema::dropIfExists('sessions'); + } +}; diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php index 2fb9b0e..0da53e0 100644 --- a/backend/database/seeders/DatabaseSeeder.php +++ b/backend/database/seeders/DatabaseSeeder.php @@ -8,5 +8,8 @@ class DatabaseSeeder extends Seeder { public function run(): void { + $this->call([ + UserSeeder::class, + ]); } } diff --git a/backend/database/seeders/UserSeeder.php b/backend/database/seeders/UserSeeder.php new file mode 100644 index 0000000..1a05bec --- /dev/null +++ b/backend/database/seeders/UserSeeder.php @@ -0,0 +1,22 @@ +create(new CreateUserDto( + email: new EmailAddress('test@example.com'), + passwordHash: $passwordHasher->hash('password123!@#'), + )); + } +} diff --git a/backend/routes/api.php b/backend/routes/api.php new file mode 100644 index 0000000..9e1c55e --- /dev/null +++ b/backend/routes/api.php @@ -0,0 +1,10 @@ +middleware(AuthMiddleware::class); diff --git a/backend/tests/Feature/CorsTest.php b/backend/tests/Feature/CorsTest.php index e86bf54..b5ab81d 100644 --- a/backend/tests/Feature/CorsTest.php +++ b/backend/tests/Feature/CorsTest.php @@ -12,7 +12,7 @@ class CorsTest extends TestCase 'Origin' => 'https://rabbigerzi.com', 'Access-Control-Request-Method' => 'POST', 'Access-Control-Request-Headers' => 'content-type', - ])->options('/login'); + ])->options('/api/login'); $response->assertNoContent(); $response->assertHeader( diff --git a/frontend/rabbi_gerzi/src/stores/auth.ts b/frontend/rabbi_gerzi/src/stores/auth.ts index e39185e..c6e9bc8 100644 --- a/frontend/rabbi_gerzi/src/stores/auth.ts +++ b/frontend/rabbi_gerzi/src/stores/auth.ts @@ -20,7 +20,7 @@ export const useAuthStore = defineStore('auth', () => { isSubmitting.value = true try { - const response = await fetch(`${API_BASE_URL}/login`, { + const response = await fetch(`${API_BASE_URL}/api/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', @@ -46,7 +46,7 @@ export const useAuthStore = defineStore('auth', () => { async function fetchUser(): Promise { try { - const response = await fetch(`${API_BASE_URL}/me`, { + const response = await fetch(`${API_BASE_URL}/api/me`, { credentials: 'include', }) @@ -64,7 +64,7 @@ export const useAuthStore = defineStore('auth', () => { async function logout(): Promise { try { - await fetch(`${API_BASE_URL}/logout`, { + await fetch(`${API_BASE_URL}/api/logout`, { method: 'POST', credentials: 'include', })