generated from flo/template-backend
changes to mail
This commit is contained in:
parent
6d97fc24c4
commit
ed03bb79d6
@ -20,7 +20,8 @@ INIT_USER_PASSWORD=password
|
|||||||
INIT_USER_MAIL=admin@test.com
|
INIT_USER_MAIL=admin@test.com
|
||||||
|
|
||||||
# Mail
|
# Mail
|
||||||
MAIL_DEFAULT_SENDER=info@bee.local
|
MAIL_DEFAULT_SENDER=beekeeper@stack-up.de
|
||||||
|
MAIL_DEFAULT_SENDER_NAME=Beekeeper
|
||||||
SMTP_USERNAME=smtp@stack-up.de
|
SMTP_USERNAME=smtp@stack-up.de
|
||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
SMTP_HOST= #srv-mail-01.lab.jonasf.de
|
SMTP_HOST= #srv-mail-01.lab.jonasf.de
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
return [
|
return [
|
||||||
'mail' => [
|
'mail' => [
|
||||||
'default' => [
|
'default' => [
|
||||||
'sender' => $_ENV['MAIL_DEFAULT_SENDER']
|
'sender' => $_ENV['MAIL_DEFAULT_SENDER'],
|
||||||
|
'senderName' => $_ENV['MAIL_DEFAULT_SENDER_NAME']
|
||||||
],
|
],
|
||||||
'smtp-server' => [
|
'smtp-server' => [
|
||||||
'host' => $_ENV['SMTP_HOST'],
|
'host' => $_ENV['SMTP_HOST'],
|
||||||
|
|||||||
40
data/migrations/business/Version20240911191301.php
Normal file
40
data/migrations/business/Version20240911191301.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Bee\Migrations\Bee;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20240911191301 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return "Create Table 'mail'";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$sql = "CREATE TABLE mail (
|
||||||
|
id binary(16) NOT NULL,
|
||||||
|
template varchar(255) NOT NULL,
|
||||||
|
data json NOT NULL,
|
||||||
|
recipient varchar(255) NOT NULL,
|
||||||
|
sender varchar(255) NULL,
|
||||||
|
sender_name varchar(255) NULL,
|
||||||
|
created_at datetime NOT NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);";
|
||||||
|
|
||||||
|
$this->addSql($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("DROP TABLE mail;");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
use Bee\API\Console\Command\InitializeDataCommand;
|
use Bee\API\Console\Command\InitializeDataCommand;
|
||||||
use Bee\API\Console\Command\RbacUpdateCommand;
|
use Bee\API\Console\Command\RbacUpdateCommand;
|
||||||
|
use Bee\API\Console\Command\SendMailsCommand;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'commands' => [
|
'commands' => [
|
||||||
InitializeDataCommand::class,
|
InitializeDataCommand::class,
|
||||||
RbacUpdateCommand::class,
|
RbacUpdateCommand::class,
|
||||||
|
SendMailsCommand::class,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|||||||
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
use Bee\API\Console\Command\InitializeDataCommand;
|
use Bee\API\Console\Command\InitializeDataCommand;
|
||||||
use Bee\API\Console\Command\RbacUpdateCommand;
|
use Bee\API\Console\Command\RbacUpdateCommand;
|
||||||
|
use Bee\API\Console\Command\SendMailsCommand;
|
||||||
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
use Reinfi\DependencyInjection\Factory\AutoWiringFactory;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'factories' => [
|
'factories' => [
|
||||||
InitializeDataCommand::class => AutoWiringFactory::class,
|
InitializeDataCommand::class => AutoWiringFactory::class,
|
||||||
RbacUpdateCommand::class => AutoWiringFactory::class,
|
RbacUpdateCommand::class => AutoWiringFactory::class,
|
||||||
|
SendMailsCommand::class => AutoWiringFactory::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -75,6 +75,7 @@ class InitializeDataCommand extends Command
|
|||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$io->success("Done");
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,6 +93,7 @@ class RbacUpdateCommand extends Command
|
|||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$io->success("Done");
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/ApiDomain/Console/src/Command/SendMailsCommand.php
Normal file
43
src/ApiDomain/Console/src/Command/SendMailsCommand.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bee\API\Console\Command;
|
||||||
|
|
||||||
|
use Bee\Infrastructure\Logging\Logger\Logger;
|
||||||
|
use Bee\Infrastructure\Mail\Service\MailService;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
#[AsCommand(name: 'mails:send', description: 'Sends all mails left in the mail queue')]
|
||||||
|
class SendMailsCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly MailService $service,
|
||||||
|
private readonly Logger $logger,
|
||||||
|
) {
|
||||||
|
parent::__construct($this->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(
|
||||||
|
InputInterface $input,
|
||||||
|
OutputInterface $output
|
||||||
|
): int {
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->service->send();
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$io->error($e->getMessage());
|
||||||
|
$io->error($e->getTraceAsString());
|
||||||
|
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->success("Done");
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Bee\API\External\Health\Handler;
|
namespace Bee\API\External\Health\Handler;
|
||||||
|
|
||||||
|
use Bee\Infrastructure\Mail\Service\MailService;
|
||||||
use Laminas\Diactoros\Response\JsonResponse;
|
use Laminas\Diactoros\Response\JsonResponse;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
@ -11,7 +12,8 @@ use Psr\Http\Server\RequestHandlerInterface;
|
|||||||
|
|
||||||
class HealthHandler implements RequestHandlerInterface
|
class HealthHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct() {
|
public function __construct(
|
||||||
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||||
@ -19,5 +21,3 @@ class HealthHandler implements RequestHandlerInterface
|
|||||||
return new JsonResponse("I'm fine. :)");
|
return new JsonResponse("I'm fine. :)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
|
|||||||
112
src/DataDomain/Business/src/Entity/Mail.php
Normal file
112
src/DataDomain/Business/src/Entity/Mail.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bee\Data\Business\Entity;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Bee\Infrastructure\UuidGenerator\UuidGenerator;
|
||||||
|
use Ramsey\Uuid\UuidInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass="Bee\Data\Business\Repository\MailRepository")
|
||||||
|
* @ORM\Table(name="mail")
|
||||||
|
*/
|
||||||
|
class Mail {
|
||||||
|
/**
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\Column(name="id", type="uuid_binary_ordered_time")
|
||||||
|
*/
|
||||||
|
private UuidInterface $id;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="template", type="string") */
|
||||||
|
private string $template;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="data", type="json") */
|
||||||
|
private array $data;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="recipient", type="string") */
|
||||||
|
private string $recipient;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="sender", type="string", nullable="true") */
|
||||||
|
private ?string $sender;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="sender_name", type="string", nullable="true") */
|
||||||
|
private ?string $senderName;
|
||||||
|
|
||||||
|
/** @ORM\Column(name="created_at", type="datetime") */
|
||||||
|
private DateTime $createdAt;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->id = UuidGenerator::generate();
|
||||||
|
|
||||||
|
$now = new DateTime();
|
||||||
|
$this->setCreatedAt($now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\PrePersist
|
||||||
|
* @ORM\PreUpdate
|
||||||
|
*/
|
||||||
|
public function updateTimestamps(): void {
|
||||||
|
$now = new DateTime();
|
||||||
|
//$this->setUpdatedAt($now);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getId(): UuidInterface {
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTemplate(): string
|
||||||
|
{
|
||||||
|
return $this->template;
|
||||||
|
}
|
||||||
|
public function setTemplate(string $template): void
|
||||||
|
{
|
||||||
|
$this->template = $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
public function setData(array $data): void
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecipient(): string
|
||||||
|
{
|
||||||
|
return $this->recipient;
|
||||||
|
}
|
||||||
|
public function setRecipient(string $recipient): void
|
||||||
|
{
|
||||||
|
$this->recipient = $recipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSender(): ?string
|
||||||
|
{
|
||||||
|
return $this->sender;
|
||||||
|
}
|
||||||
|
public function setSender(?string $sender): void
|
||||||
|
{
|
||||||
|
$this->sender = $sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSenderName(): ?string
|
||||||
|
{
|
||||||
|
return $this->senderName;
|
||||||
|
}
|
||||||
|
public function setSenderName(?string $senderName): void
|
||||||
|
{
|
||||||
|
$this->senderName = $senderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedAt(): DateTime {
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
public function setCreatedAt(DateTime $createdAt): void {
|
||||||
|
$this->createdAt = $createdAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/DataDomain/Business/src/Repository/MailRepository.php
Normal file
10
src/DataDomain/Business/src/Repository/MailRepository.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bee\Data\Business\Repository;
|
||||||
|
|
||||||
|
use Bee\Data\Business\Entity\User;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
|
class MailRepository extends EntityRepository {
|
||||||
|
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ class SendMailStep implements TaskInterface
|
|||||||
$command = $payload->getCommand();
|
$command = $payload->getCommand();
|
||||||
$registration = $payload->getRegistration();
|
$registration = $payload->getRegistration();
|
||||||
|
|
||||||
$this->mailService->send(
|
$this->mailService->enqueue(
|
||||||
template: 'registration',
|
template: 'registration',
|
||||||
templateData: [
|
templateData: [
|
||||||
'username' => $command->getUsername(),
|
'username' => $command->getUsername(),
|
||||||
@ -40,9 +40,7 @@ class SendMailStep implements TaskInterface
|
|||||||
$registration->getId()->toString()
|
$registration->getId()->toString()
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
recipient: $command->getMail(),
|
recipient: $command->getMail()
|
||||||
sender: 'beekeeper@stack-up.de',
|
|
||||||
senderName: "Beekeeper"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$pipeline->next()($payload, $pipeline);
|
$pipeline->next()($payload, $pipeline);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ class ForgotPasswordCommandHandler
|
|||||||
$this->entityManager->persist($passwordToken);
|
$this->entityManager->persist($passwordToken);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$this->mailService->send(
|
$this->mailService->enqueue(
|
||||||
'reset-password',
|
'reset-password',
|
||||||
templateData: [
|
templateData: [
|
||||||
'username' => $user->getUsername(),
|
'username' => $user->getUsername(),
|
||||||
@ -61,8 +61,6 @@ class ForgotPasswordCommandHandler
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
recipient: $mail,
|
recipient: $mail,
|
||||||
sender: 'beekeeper@stack-up.de',
|
|
||||||
senderName: 'Beekeeper'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return new ForgotPasswordCommandResult();
|
return new ForgotPasswordCommandResult();
|
||||||
|
|||||||
@ -4,6 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Bee\Infrastructure\Mail\Service;
|
namespace Bee\Infrastructure\Mail\Service;
|
||||||
|
|
||||||
|
use Bee\Data\Business\Entity\Mail;
|
||||||
|
use Bee\Data\Business\Manager\EntityManager;
|
||||||
|
use Bee\Data\Business\Repository\MailRepository;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Latte\Engine;
|
use Latte\Engine;
|
||||||
use Nette\Mail\Mailer;
|
use Nette\Mail\Mailer;
|
||||||
@ -18,18 +21,70 @@ class MailService
|
|||||||
{
|
{
|
||||||
private const TEMPLATE_PATH = APP_ROOT . '/data/mails/';
|
private const TEMPLATE_PATH = APP_ROOT . '/data/mails/';
|
||||||
private readonly Engine $engine;
|
private readonly Engine $engine;
|
||||||
|
private readonly MailRepository $mailRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
private readonly EntityManager $entityManager,
|
||||||
private readonly ConfigService $configService,
|
private readonly ConfigService $configService,
|
||||||
private readonly Logger $logger,
|
private readonly Logger $logger,
|
||||||
) {
|
) {
|
||||||
$this->engine = new Engine();
|
$this->engine = new Engine();
|
||||||
|
|
||||||
|
$this->mailRepository = $this->entityManager->getRepository(Mail::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue(
|
||||||
|
string $template,
|
||||||
|
array $templateData,
|
||||||
|
string $recipient,
|
||||||
|
?string $sender = null,
|
||||||
|
?string $senderName = null
|
||||||
|
): void {
|
||||||
|
$mail = new Mail();
|
||||||
|
$mail->setTemplate($template);
|
||||||
|
$mail->setData($templateData);
|
||||||
|
$mail->setRecipient($recipient);
|
||||||
|
$mail->setSender($sender);
|
||||||
|
$mail->setSenderName($senderName);
|
||||||
|
|
||||||
|
$this->entityManager->persist($mail);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(?int $count = null): void {
|
||||||
|
$qb = $this->mailRepository->createQueryBuilder('m');
|
||||||
|
$qb->orderBy('m.createdAt', 'asc');
|
||||||
|
|
||||||
|
if ($count !== null) {
|
||||||
|
$qb->setMaxResults($count);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mailsToSend = $qb->getQuery()->execute();
|
||||||
|
|
||||||
|
/** @var Mail $mailToSend */
|
||||||
|
foreach ($mailsToSend as $mailToSend) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->sendMail(
|
||||||
|
$mailToSend->getTemplate(),
|
||||||
|
$mailToSend->getData(),
|
||||||
|
$mailToSend->getRecipient(),
|
||||||
|
$mailToSend->getSender(),
|
||||||
|
$mailToSend->getSenderName(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->entityManager->remove($mailToSend);
|
||||||
|
} catch (SendMailFailedException $e) {
|
||||||
|
// log is done withing sendMail
|
||||||
|
}
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws SendMailFailedException
|
* @throws SendMailFailedException
|
||||||
*/
|
*/
|
||||||
public function send(
|
private function sendMail(
|
||||||
string $template,
|
string $template,
|
||||||
array $templateData,
|
array $templateData,
|
||||||
string $recipient,
|
string $recipient,
|
||||||
@ -112,9 +167,7 @@ class MailService
|
|||||||
$defaultConfig = $this->configService->resolve('mail.default');
|
$defaultConfig = $this->configService->resolve('mail.default');
|
||||||
|
|
||||||
$from = $sender ?? $defaultConfig['sender'] ?? throw new Exception('Could not determine Sender');
|
$from = $sender ?? $defaultConfig['sender'] ?? throw new Exception('Could not determine Sender');
|
||||||
if ($senderName !== null) {
|
$name =$sender ?? $defaultConfig['senderName'] ?? throw new Exception('Could not determine Sender Name');
|
||||||
$from = sprintf('%s <%s>', $senderName, $from);
|
return sprintf('%s <%s>', $name, $from);
|
||||||
}
|
|
||||||
return $from;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user