diff --git a/.env.example b/.env.example index c59a2a1..8740ee5 100644 --- a/.env.example +++ b/.env.example @@ -17,4 +17,12 @@ BEE_API_KEY= # Bee Setup INIT_USER_NAME=admin INIT_USER_PASSWORD=password -INIT_USER_MAIL=admin@test.com \ No newline at end of file +INIT_USER_MAIL=admin@test.com + +# Mail +MAIL_DEFAULT_SENDER=info@bee.local +SMTP_USERNAME=smtp@stack-up.de +SMTP_PASSWORD= +SMTP_HOST= #srv-mail-01.lab.jonasf.de +SMTP_PORT=465 +SMTP_ENCRYPTION=ssl \ No newline at end of file diff --git a/composer.json b/composer.json index cf53759..997adfe 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,9 @@ "monolog/monolog": "^3.4", "laminas/laminas-mail": "^2.23", "teewurst/pipeline": "^3.0", - "guzzlehttp/guzzle": "^7.8" + "guzzlehttp/guzzle": "^7.8", + "nette/mail": "^4.0", + "latte/latte": "^3.0" }, "autoload": { "psr-4": { diff --git a/config/autoload/mail.global.php b/config/autoload/mail.global.php new file mode 100644 index 0000000..b7b8ca3 --- /dev/null +++ b/config/autoload/mail.global.php @@ -0,0 +1,16 @@ + [ + 'default' => [ + 'sender' => $_ENV['MAIL_DEFAULT_SENDER'] + ], + 'smtp-server' => [ + 'host' => $_ENV['SMTP_HOST'], + 'port' => (int)$_ENV['SMTP_PORT'] ?? 25, + 'encryption' => $_ENV['SMTP_ENCRYPTION'], + 'username' => $_ENV['SMTP_USERNAME'], + 'password' => $_ENV['SMTP_PASSWORD'], + ] + ] +]; \ No newline at end of file diff --git a/config/config.php b/config/config.php index a4c18d7..1031b20 100644 --- a/config/config.php +++ b/config/config.php @@ -52,6 +52,7 @@ $aggregator = new ConfigAggregator([ \Bee\Infrastructure\Rbac\ConfigProvider::class, \Bee\Infrastructure\Request\ConfigProvider::class, \Bee\Infrastructure\Session\ConfigProvider::class, + \Bee\Infrastructure\Mail\ConfigProvider::class, // HandlingDomain \Bee\Handling\User\ConfigProvider::class, diff --git a/data/mails/registration/assets/icon.png b/data/mails/registration/assets/icon.png new file mode 100644 index 0000000..07e8b9d Binary files /dev/null and b/data/mails/registration/assets/icon.png differ diff --git a/data/mails/registration/template.latte b/data/mails/registration/template.latte new file mode 100644 index 0000000..565a7f5 --- /dev/null +++ b/data/mails/registration/template.latte @@ -0,0 +1,63 @@ + + + + Willkommen beim Beekeeper! + + + + +
+

Hallo {$username},

+
+

Herzlich willkommen beim Beekeeper!

+
+

Bitte klicke auf diesen Link um dein Passwort festzulegen.

+

Danach ist deine Registierung abgeschlossen und du kannst loslegen!

+
+

Mit fleißigen Grüßen,

+

Der Beekeeper

+
+ + diff --git a/src/ApiDomain/External/Authentication/config/service_manager.php b/src/ApiDomain/External/Authentication/config/service_manager.php index d90ba16..61124b9 100644 --- a/src/ApiDomain/External/Authentication/config/service_manager.php +++ b/src/ApiDomain/External/Authentication/config/service_manager.php @@ -1,5 +1,7 @@ [ + // Formatter + ConfirmRegistrationFormatter::class => AutoWiringFactory::class, + LoginUserFormatter::class => AutoWiringFactory::class, + // Handler LoginUserHandler::class => AutoWiringFactory::class, LogoutUserHandler::class => AutoWiringFactory::class, diff --git a/src/ApiDomain/External/Authentication/src/Formatter/ConfirmRegistrationFormatter.php b/src/ApiDomain/External/Authentication/src/Formatter/ConfirmRegistrationFormatter.php new file mode 100644 index 0000000..daa01f8 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Formatter/ConfirmRegistrationFormatter.php @@ -0,0 +1,27 @@ + $user->getId()->toString(), + 'username' => $user->getUsername(), + 'roleIdentifier' => $user->getRole()->getIdentifier(), + 'createdAt' => $user->getCreatedAt()->format(DateTime::ATOM), + 'updatedAt' => $user->getUpdatedAt()->format(DateTime::ATOM) + ]; + + $userArray['permissions'] = []; + + foreach ($user->getRole()->getPermissions()->toArray() as $permission) { + $userArray['permissions'][] = $permission->getIdentifier(); + } + + return $userArray; + } +} diff --git a/src/ApiDomain/External/Authentication/src/Formatter/LoginUserFormatter.php b/src/ApiDomain/External/Authentication/src/Formatter/LoginUserFormatter.php new file mode 100644 index 0000000..772dd23 --- /dev/null +++ b/src/ApiDomain/External/Authentication/src/Formatter/LoginUserFormatter.php @@ -0,0 +1,14 @@ + $session->getId()->toString() + ]; + } +} diff --git a/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php b/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php index 9520d11..15b7ee4 100644 --- a/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php +++ b/src/ApiDomain/External/Authentication/src/Handler/ConfirmRegistrationHandler.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Bee\API\External\Authentication\Handler; +use Bee\API\External\Authentication\Formatter\ConfirmRegistrationFormatter; use Bee\API\External\User\Formatter\UserFormatter; use Bee\Handling\Registration\Handler\Command\ConfirmRegistration\ConfirmRegistrationCommandBuilder; use Bee\Handling\Registration\Handler\Command\ConfirmRegistration\ConfirmRegistrationCommandHandler; @@ -20,7 +21,7 @@ class ConfirmRegistrationHandler implements RequestHandlerInterface public function __construct( private readonly ConfirmRegistrationCommandHandler $handler, private readonly ConfirmRegistrationCommandBuilder $builder, - private readonly UserFormatter $userFormatter + private readonly ConfirmRegistrationFormatter $formatter ) { } @@ -36,7 +37,7 @@ class ConfirmRegistrationHandler implements RequestHandlerInterface $result = $this->handler->execute($query); return new JsonResponse( - $this->userFormatter->format($result) + $this->formatter->format($result) ); } } diff --git a/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php index 0d60330..f92f5c1 100644 --- a/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php +++ b/src/ApiDomain/External/Authentication/src/Handler/LoginUserHandler.php @@ -4,9 +4,18 @@ declare(strict_types=1); namespace Bee\API\External\Authentication\Handler; +use Bee\API\External\Authentication\Formatter\LoginUserFormatter; +use Bee\Handling\User\Exception\UserNotFoundByIdentifierException; +use Bee\Handling\User\Exception\UserWrongPasswordException; use Bee\Handling\UserSession\Handler\Command\LoginUser\LoginUserCommandBuilder; use Bee\Handling\UserSession\Handler\Command\LoginUser\LoginUserCommandHandler; +use Bee\Infrastructure\Exception\ErrorCode; +use Bee\Infrastructure\Exception\ErrorDomain; +use Bee\Infrastructure\Exception\Exception\Exception; +use Bee\Infrastructure\Logging\Logger\Logger; use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware; +use Bee\Infrastructure\Response\ErrorResponse; +use Bee\Infrastructure\Response\UnauthorizedResponse; use Bee\Infrastructure\Session\Middleware\SessionMiddleware; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; @@ -17,10 +26,16 @@ class LoginUserHandler implements RequestHandlerInterface { public function __construct( private readonly LoginUserCommandHandler $handler, - private readonly LoginUserCommandBuilder $builder + private readonly LoginUserCommandBuilder $builder, + private readonly LoginUserFormatter $formatter, + private readonly Logger $logger, ) { } + /** + * @throws UserWrongPasswordException + * @throws UserNotFoundByIdentifierException + */ public function handle(ServerRequestInterface $request): ResponseInterface { $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE); @@ -32,9 +47,12 @@ class LoginUserHandler implements RequestHandlerInterface $data['password'], ); - $result = $this->handler->execute($query); - return new JsonResponse([ - 'sessionId' => $result->getId()->toString() - ]); + try { + $result = $this->handler->execute($query); + return new JsonResponse($this->formatter->format($result)); + } catch (Exception $e) { + $this->logger->exception($e); + return new ErrorResponse(ErrorDomain::Login, ErrorCode::SomethingWentWrong); + } } } diff --git a/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php index 0ca164b..81c4de0 100644 --- a/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php +++ b/src/ApiDomain/External/Authentication/src/Handler/LogoutUserHandler.php @@ -30,6 +30,6 @@ class LogoutUserHandler implements RequestHandlerInterface ); $this->handler->execute($query); - return new JsonResponse('OK'); + return new SuccessResponse(); } } diff --git a/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php b/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php index 124c17e..33e110c 100644 --- a/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php +++ b/src/ApiDomain/External/Authentication/src/Handler/RegisterUserHandler.php @@ -4,7 +4,13 @@ declare(strict_types=1); namespace Bee\API\External\Authentication\Handler; +use Bee\Handling\Registration\Exception\RegistrationWithIdentifierAlreadyExistsException; +use Bee\Handling\User\Exception\UserWithIdentifierAlreadyExistsException; +use Bee\Infrastructure\Exception\ErrorCode; +use Bee\Infrastructure\Exception\ErrorDomain; +use Bee\Infrastructure\Mail\Exception\SendMailFailedException; use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware; +use Bee\Infrastructure\Response\ErrorResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -18,7 +24,8 @@ class RegisterUserHandler implements RequestHandlerInterface { public function __construct( private readonly RegisterUserCommandHandler $handler, - private readonly RegisterUserCommandBuilder $builder + private readonly RegisterUserCommandBuilder $builder, + private readonly Logger $logger, ) { } @@ -33,7 +40,19 @@ class RegisterUserHandler implements RequestHandlerInterface $host ); - $this->handler->execute($query); + try { + $this->handler->execute($query); + } catch (RegistrationWithIdentifierAlreadyExistsException $e) { + $this->logger->exception($e); + return new ErrorResponse(ErrorDomain::Registration, ErrorCode::SomethingWentWrong); + } catch (UserWithIdentifierAlreadyExistsException $e) { + $this->logger->exception($e); + return new ErrorResponse(ErrorDomain::Registration, ErrorCode::SomethingWentWrong); + } catch (SendMailFailedException $e) { + $this->logger->exception($e); + return new ErrorResponse(ErrorDomain::Registration, ErrorCode::SomethingWentWrong); + } + return new SuccessResponse(); } } diff --git a/src/ApiDomain/External/User/config/service_manager.php b/src/ApiDomain/External/User/config/service_manager.php index 912487c..8a7dcb9 100644 --- a/src/ApiDomain/External/User/config/service_manager.php +++ b/src/ApiDomain/External/User/config/service_manager.php @@ -1,6 +1,7 @@ [ // Formatter - UserFormatter::class => AutoWiringFactory::class, + UserStateFormatter::class => AutoWiringFactory::class, + CreateUserFormatter::class => AutoWiringFactory::class, // Handler CreateUserHandler::class => AutoWiringFactory::class, diff --git a/src/ApiDomain/External/User/src/Formatter/UserFormatter.php b/src/ApiDomain/External/User/src/Formatter/CreateUserFormatter.php similarity index 65% rename from src/ApiDomain/External/User/src/Formatter/UserFormatter.php rename to src/ApiDomain/External/User/src/Formatter/CreateUserFormatter.php index 5888485..3c4f95c 100644 --- a/src/ApiDomain/External/User/src/Formatter/UserFormatter.php +++ b/src/ApiDomain/External/User/src/Formatter/CreateUserFormatter.php @@ -5,15 +5,15 @@ namespace Bee\API\External\User\Formatter; use DateTime; use Bee\Data\Business\Entity\User; -class UserFormatter { +class CreateUserFormatter { public function format(User $user): array { $userArray = [ 'id' => $user->getId()->toString(), 'username' => $user->getUsername(), - 'role' => $user->getRole()->getIdentifier(), - 'created' => $user->getCreatedAt()->format(DateTime::ATOM), - 'updated' => $user->getUpdatedAt()->format(DateTime::ATOM) + 'roleIdentifier' => $user->getRole()->getIdentifier(), + 'createdAt' => $user->getCreatedAt()->format(DateTime::ATOM), + 'updatedAt' => $user->getUpdatedAt()->format(DateTime::ATOM) ]; $userArray['permissions'] = []; diff --git a/src/ApiDomain/External/User/src/Formatter/UserStateFormatter.php b/src/ApiDomain/External/User/src/Formatter/UserStateFormatter.php new file mode 100644 index 0000000..124ec6b --- /dev/null +++ b/src/ApiDomain/External/User/src/Formatter/UserStateFormatter.php @@ -0,0 +1,28 @@ + $user->getId()->toString(), + 'sessionId' => $user->getSession()->getId()->toString(), + 'username' => $user->getUsername(), + 'roleIdentifier' => $user->getRole()->getIdentifier(), + 'createdAt' => $user->getCreatedAt()->format(DateTime::ATOM), + 'updatedAt' => $user->getUpdatedAt()->format(DateTime::ATOM) + ]; + + $userArray['permissions'] = []; + + foreach ($user->getRole()->getPermissions()->toArray() as $permission) { + $userArray['permissions'][] = $permission->getIdentifier(); + } + + return $userArray; + } +} diff --git a/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php b/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php index afffb0e..21373d3 100644 --- a/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php +++ b/src/ApiDomain/External/User/src/Handler/ChangeUsernameHandler.php @@ -7,6 +7,7 @@ namespace Bee\API\External\User\Handler; use Bee\Data\Business\Entity\User; use Bee\Handling\User\Handler\Command\ChangeUsername\ChangeUsernameCommandBuilder; use Bee\Handling\User\Handler\Command\ChangeUsername\ChangeUsernameCommandHandler; +use Bee\Infrastructure\Request\Middleware\AnalyzeBodyMiddleware; use Bee\Infrastructure\Response\SuccessResponse; use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware; use Psr\Http\Message\ResponseInterface; @@ -25,7 +26,6 @@ class ChangeUsernameHandler implements RequestHandlerInterface { /** @var User $user */ $user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY); - $data = $request->getAttribute(AnalyzeBodyMiddleware::JSON_DATA); $query = $this->builder->build( diff --git a/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php b/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php index 6301366..a0f13fc 100644 --- a/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php +++ b/src/ApiDomain/External/User/src/Handler/CreateUserHandler.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Bee\API\External\User\Handler; +use Bee\API\External\User\Formatter\CreateUserFormatter; use Bee\API\External\User\Formatter\UserFormatter; use Bee\Handling\User\Handler\Command\CreateUser\CreateUserCommandBuilder; use Bee\Handling\User\Handler\Command\CreateUser\CreateUserCommandHandler; @@ -18,7 +19,7 @@ class CreateUserHandler implements RequestHandlerInterface public function __construct( private readonly CreateUserCommandHandler $handler, private readonly CreateUserCommandBuilder $builder, - private readonly UserFormatter $userFormatter, + private readonly CreateUserFormatter $formatter, ) { } @@ -34,7 +35,7 @@ class CreateUserHandler implements RequestHandlerInterface $result = $this->handler->execute($query); return new JsonResponse( - $this->userFormatter->format($result) + $this->formatter->format($result) ); } } diff --git a/src/ApiDomain/External/User/src/Handler/UserStateHandler.php b/src/ApiDomain/External/User/src/Handler/UserStateHandler.php index 0e09b4f..e605bbe 100644 --- a/src/ApiDomain/External/User/src/Handler/UserStateHandler.php +++ b/src/ApiDomain/External/User/src/Handler/UserStateHandler.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace Bee\API\External\User\Handler; -use Bee\API\External\User\Formatter\UserFormatter; +use Bee\API\External\User\Formatter\UserStateFormatter; use Bee\Data\Business\Entity\User; -use Bee\Handling\User\Handler\Command\CreateUser\CreateUserCommandBuilder; -use Bee\Handling\User\Handler\Command\CreateUser\ChangePasswordCommandHandler; use Bee\Infrastructure\Session\Middleware\LoggedInUserMiddleware; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; @@ -17,7 +15,7 @@ use Psr\Http\Server\RequestHandlerInterface; class UserStateHandler implements RequestHandlerInterface { public function __construct( - private readonly UserFormatter $userFormatter, + private readonly UserStateFormatter $userFormatter, ) { } @@ -27,7 +25,6 @@ class UserStateHandler implements RequestHandlerInterface $user = $request->getAttribute(LoggedInUserMiddleware::USER_KEY); $response = $this->userFormatter->format($user); - $response['sessionId'] = $user->getSession()->getId()->toString(); return new JsonResponse($response); } diff --git a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php index 31b2ea7..b99f9b8 100644 --- a/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php +++ b/src/HandlingDomain/Registration/src/Pipeline/RegisterUser/Step/SendMailStep.php @@ -4,7 +4,8 @@ declare(strict_types=1); namespace Bee\Handling\Registration\Pipeline\RegisterUser\Step; -use Bee\Infrastructure\Request\Service\RequestService; +use Bee\Infrastructure\Mail\Exception\SendMailFailedException; +use Bee\Infrastructure\Mail\Service\MailService; use teewurst\Pipeline\PipelineInterface; use teewurst\Pipeline\TaskInterface; @@ -13,10 +14,13 @@ class SendMailStep implements TaskInterface private const CONFIRM_LINK = 'https://%s/auth/registration/%s'; public function __construct( - private RequestService $requestService + private MailService $mailService, ) { } + /** + * @throws SendMailFailedException + */ public function __invoke( $payload, PipelineInterface $pipeline @@ -25,23 +29,19 @@ class SendMailStep implements TaskInterface $command = $payload->getCommand(); $registration = $payload->getRegistration(); - $this->requestService->request( - 'notification', - 'send-mail', - [ - 'bee-identifier' => 'new-account', - 'sender' => 'info@stack-up.de', - 'recipient' => $command->getMail(), - 'data' => [ - 'username' => $command->getUsername(), - 'confirmationLink' => - sprintf( - self::CONFIRM_LINK, - $command->getHost(), - $registration->getId()->toString() - ) - ] - ] + $this->mailService->send( + template: 'registration', + templateData: [ + 'username' => $command->getUsername(), + 'confirmationLink' => + sprintf( + self::CONFIRM_LINK, + $command->getHost(), + $registration->getId()->toString() + ) + ], + recipient: $command->getMail(), + senderName: "Beekeeper" ); $pipeline->next()($payload, $pipeline); diff --git a/src/Infrastructure/Exception/src/ErrorCode.php b/src/Infrastructure/Exception/src/ErrorCode.php index a72072a..456cc08 100644 --- a/src/Infrastructure/Exception/src/ErrorCode.php +++ b/src/Infrastructure/Exception/src/ErrorCode.php @@ -9,4 +9,5 @@ enum ErrorCode : string { case AlreadyExists = 'AlreadyExists'; case WrongCredentials = 'WrongCredentials'; case Mismatch = 'Mismatch'; + case Failed = 'Failed'; } \ No newline at end of file diff --git a/src/Infrastructure/Exception/src/ErrorDomain.php b/src/Infrastructure/Exception/src/ErrorDomain.php index 6c439be..574d2ea 100644 --- a/src/Infrastructure/Exception/src/ErrorDomain.php +++ b/src/Infrastructure/Exception/src/ErrorDomain.php @@ -10,4 +10,6 @@ enum ErrorDomain : string { case UserPassword = 'UserPassword'; case Registration = 'Registration'; case Product = 'Product'; + case Mail = 'Mail'; + case Login = 'Login'; } \ No newline at end of file diff --git a/src/Infrastructure/Mail/config/service_manager.php b/src/Infrastructure/Mail/config/service_manager.php new file mode 100644 index 0000000..47bff82 --- /dev/null +++ b/src/Infrastructure/Mail/config/service_manager.php @@ -0,0 +1,12 @@ + [ + MailService::class => AutoWiringFactory::class, + ], +]; diff --git a/src/Infrastructure/Mail/src/ConfigProvider.php b/src/Infrastructure/Mail/src/ConfigProvider.php new file mode 100644 index 0000000..7bc4a8d --- /dev/null +++ b/src/Infrastructure/Mail/src/ConfigProvider.php @@ -0,0 +1,15 @@ + require __DIR__ . './../config/service_manager.php', + ]; + } +} diff --git a/src/Infrastructure/Mail/src/Exception/SendMailFailedException.php b/src/Infrastructure/Mail/src/Exception/SendMailFailedException.php new file mode 100644 index 0000000..dfe45c8 --- /dev/null +++ b/src/Infrastructure/Mail/src/Exception/SendMailFailedException.php @@ -0,0 +1,32 @@ +getMessage(), + ), + ErrorDomain::Mail, + ErrorCode::Failed + ); + } +} \ No newline at end of file diff --git a/src/Infrastructure/Mail/src/Service/MailService.php b/src/Infrastructure/Mail/src/Service/MailService.php new file mode 100644 index 0000000..a2073f9 --- /dev/null +++ b/src/Infrastructure/Mail/src/Service/MailService.php @@ -0,0 +1,120 @@ +engine = new Engine(); + } + + /** + * @throws SendMailFailedException + */ + public function send( + string $template, + array $templateData, + string $recipient, + ?string $sender = null, + ?string $senderName = null + ): void { + try { + $mail = $this->getMail( + template: $template, + templateData: $templateData, + recipient: $recipient, + sender: $sender, + senderName: $senderName + ); + + $mailer = $this->getMailer(); + + $mailer->send($mail); + } catch (Throwable $e) { + $this->logger->exception($e); + + throw new SendMailFailedException( + $template, + $templateData, + $recipient, + $e + ); + } + } + + private function getMailer(): Mailer + { + $smtpConfig = $this->configService->resolve('mail.smtp-server'); + + return new SmtpMailer( + host: $smtpConfig['host'], + username: $smtpConfig['username'], + password: $smtpConfig['password'], + encryption: $smtpConfig['encryption'], + port: $smtpConfig['port'], + ); + } + + private function getMail( + string $template, + array $templateData, + string $recipient, + ?string $sender = null, + ?string $senderName = null + ): Message { + $templatePath = self::TEMPLATE_PATH . $template . '/template.latte'; + $assetsPath = self::TEMPLATE_PATH . $template . "/"; + + if (!file_exists($templatePath)) { + throw new Exception("Template File does not exist"); + } + + if (!is_dir($assetsPath)) { + $assetsPath = null; + } + + $mail = new Message(); + $mail->setFrom($this->getSenderName($sender, $senderName)); + $mail->addTo($recipient); + $mail->setHtmlBody( + $this->engine->renderToString( + $templatePath, + $templateData, + ), + $assetsPath + ); + + return $mail; + } + + private function getSenderName( + ?string $sender, + ?string $senderName + ): string { + $defaultConfig = $this->configService->resolve('mail.default'); + + $from = $sender ?? $defaultConfig['sender'] ?? throw new Exception('Could not determine Sender'); + if ($senderName !== null) { + $from = sprintf('%s <%s>', $senderName, $from); + } + return $from; + } +} \ No newline at end of file