From 9703f82788e441c07118e89c76eae05e2f22eeff Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sat, 16 May 2026 21:33:56 +0300 Subject: [PATCH] add domain layer, config, and entry point Domain: User, Session, EmailAddress, DTOs, repositories, services (PasswordHasher, TokenGenerator, Clock). Config: PHP-DI container definitions and Slim routes. Entry point: public/index.php with slim-bridge. --- backend/app/Auth/BcryptPasswordHasher.php | 16 ++++++ backend/app/Auth/Clock.php | 10 ++++ backend/app/Auth/CreateSessionDto.php | 16 ++++++ backend/app/Auth/PasswordHasher.php | 10 ++++ backend/app/Auth/RandomTokenGenerator.php | 11 ++++ backend/app/Auth/Session.php | 41 ++++++++++++++ backend/app/Auth/SessionRepository.php | 12 +++++ backend/app/Auth/SystemClock.php | 14 +++++ backend/app/Auth/TokenGenerator.php | 8 +++ .../app/Exceptions/BadRequestException.php | 7 +++ .../app/Exceptions/UnauthorizedException.php | 7 +++ .../app/Shared/ValueObject/EmailAddress.php | 54 +++++++++++++++++++ backend/app/User/CreateUserDto.php | 13 +++++ backend/app/User/User.php | 29 ++++++++++ backend/app/User/UserRepository.php | 14 +++++ backend/config/container.php | 37 +++++++++++++ backend/config/routes.php | 17 ++++++ backend/public/index.php | 19 +++++++ 18 files changed, 335 insertions(+) create mode 100644 backend/app/Auth/BcryptPasswordHasher.php create mode 100644 backend/app/Auth/Clock.php create mode 100644 backend/app/Auth/CreateSessionDto.php create mode 100644 backend/app/Auth/PasswordHasher.php create mode 100644 backend/app/Auth/RandomTokenGenerator.php create mode 100644 backend/app/Auth/Session.php create mode 100644 backend/app/Auth/SessionRepository.php create mode 100644 backend/app/Auth/SystemClock.php create mode 100644 backend/app/Auth/TokenGenerator.php create mode 100644 backend/app/Exceptions/BadRequestException.php create mode 100644 backend/app/Exceptions/UnauthorizedException.php create mode 100644 backend/app/Shared/ValueObject/EmailAddress.php create mode 100644 backend/app/User/CreateUserDto.php create mode 100644 backend/app/User/User.php create mode 100644 backend/app/User/UserRepository.php create mode 100644 backend/config/container.php create mode 100644 backend/config/routes.php create mode 100644 backend/public/index.php diff --git a/backend/app/Auth/BcryptPasswordHasher.php b/backend/app/Auth/BcryptPasswordHasher.php new file mode 100644 index 0000000..0bc4a46 --- /dev/null +++ b/backend/app/Auth/BcryptPasswordHasher.php @@ -0,0 +1,16 @@ +token; + } + + public function getUser(): User + { + return $this->user; + } + + public function getCreatedAt(): DateTimeImmutable + { + return $this->createdAt; + } + + public function getExpiresAt(): DateTimeImmutable + { + return $this->expiresAt; + } + + public function isExpired(DateTimeImmutable $now): bool + { + return $now >= $this->expiresAt; + } +} diff --git a/backend/app/Auth/SessionRepository.php b/backend/app/Auth/SessionRepository.php new file mode 100644 index 0000000..cabae60 --- /dev/null +++ b/backend/app/Auth/SessionRepository.php @@ -0,0 +1,12 @@ +domain = mb_strtolower($domain); + $normalized = $local.'@'.$this->domain; + + if (filter_var($normalized, FILTER_VALIDATE_EMAIL) === false) { + throw new InvalidArgumentException(self::ERROR_MESSAGE." $email"); + } + + $this->normalized = $normalized; + } + + public function value(): string + { + return $this->normalized; + } + + public function equals(self $other): bool + { + return $this->normalized === $other->normalized; + } + + public function getDomain(): string + { + return $this->domain; + } + + public function __toString(): string + { + return $this->normalized; + } +} diff --git a/backend/app/User/CreateUserDto.php b/backend/app/User/CreateUserDto.php new file mode 100644 index 0000000..d10b373 --- /dev/null +++ b/backend/app/User/CreateUserDto.php @@ -0,0 +1,13 @@ +id; + } + + public function getEmail(): EmailAddress + { + return $this->email; + } + + public function getPasswordHash(): string + { + return $this->passwordHash; + } +} diff --git a/backend/app/User/UserRepository.php b/backend/app/User/UserRepository.php new file mode 100644 index 0000000..975b50d --- /dev/null +++ b/backend/app/User/UserRepository.php @@ -0,0 +1,14 @@ +addDefinitions([ + + // Services + PasswordHasher::class => DI\create(BcryptPasswordHasher::class), + TokenGenerator::class => DI\create(RandomTokenGenerator::class), + Clock::class => DI\create(SystemClock::class), + + // Use cases + AuthenticateUser::class => DI\autowire(), + CreateSession::class => DI\autowire(), + Logout::class => DI\autowire(), + + // HTTP layer + AuthController::class => DI\autowire(), + AuthMiddleware::class => DI\autowire(), + +]); + +return $builder->build(); diff --git a/backend/config/routes.php b/backend/config/routes.php new file mode 100644 index 0000000..cd9f359 --- /dev/null +++ b/backend/config/routes.php @@ -0,0 +1,17 @@ +get('/me', [AuthController::class, 'me']) + ->add(AuthMiddleware::class); + + $app->post('/login', [AuthController::class, 'login']); + + $app->post('/logout', [AuthController::class, 'logout']) + ->add(AuthMiddleware::class); +}; diff --git a/backend/public/index.php b/backend/public/index.php new file mode 100644 index 0000000..0209daf --- /dev/null +++ b/backend/public/index.php @@ -0,0 +1,19 @@ +run(); +})();