This commit is contained in:
2025-12-26 02:44:15 +01:00
parent 9fb863365c
commit 811d3df7a0
11 changed files with 589 additions and 75 deletions

174
src/App/Mailer.php Normal file
View File

@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
namespace App;
final class Mailer
{
public function __construct(private App $app) {}
private function templates(): array
{
$env = $this->app->config()->env;
$root = __DIR__ . '/../../config/emailtemplates.php';
$envPath = __DIR__ . "/../../config/{$env}/emailtemplates.php";
$file = is_file($root) ? $root : $envPath;
$emailtemplates = [];
if (is_file($file)) {
/** @noinspection PhpIncludeInspection */
$emailtemplates = include $file;
}
return is_array($emailtemplates ?? null) ? $emailtemplates : [];
}
private function renderTemplate(string $key, array $vars): array
{
$templates = $this->templates();
$id = $templates[$key] ?? $key;
$apiBase = getenv('EMAILTEMPLATE_API_BASE') ?: '';
$apiToken = getenv('EMAILTEMPLATE_API_TOKEN') ?: '';
if ($apiBase && $apiToken) {
$payload = [
'template' => $id,
'placeholders' => $vars,
];
$payload['token'] = $apiToken;
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'timeout' => 15,
'content' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
],
]);
$resp = @file_get_contents($apiBase, false, $ctx);
if ($resp !== false) {
$decoded = json_decode($resp, true);
if (is_array($decoded) && !empty($decoded['ok']) && !empty($decoded['html'])) {
return [
'subject' => $decoded['subject'] ?? 'Papakind',
'html' => $decoded['html'],
];
}
}
}
// Fallback: einfacher Text
$subject = 'Papa-Kind-Treff';
$body = $id;
foreach ($vars as $k => $v) {
$body = str_replace(['{' . $k . '}', '{{' . $k . '}}'], (string)$v, $body);
}
return [
'subject' => $subject,
'html' => nl2br(htmlspecialchars($body, ENT_QUOTES)),
];
}
public function sendTemplate(string $templateKey, string $to, array $vars = []): void
{
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid recipient email.');
}
$tpl = $this->renderTemplate($templateKey, $vars);
$subject = $tpl['subject'] ?? 'Papa-Kind-Treff';
$html = $tpl['html'] ?? '';
$transport = getenv('MAIL_TRANSPORT') ?: 'mail';
$fromEmail = getenv('MAIL_FROM') ?: 'no-reply@' . $this->app->config()->primaryDomain;
$fromName = getenv('MAIL_FROM_NAME') ?: 'Papa-Kind-Treff';
if ($transport === 'smtp') {
$this->sendSmtp($to, $subject, $html, $fromEmail, $fromName);
} else {
$this->sendMailFn($to, $subject, $html, $fromEmail, $fromName);
}
}
private function sendMailFn(string $to, string $subject, string $html, string $from, string $fromName): void
{
$headers = [];
if ($from) {
$headers[] = 'From: ' . sprintf('"%s" <%s>', addslashes($fromName), $from);
}
$headers[] = 'Content-Type: text/html; charset=utf-8';
if (!@mail($to, $subject, $html, implode("\r\n", $headers))) {
throw new \RuntimeException('mail() transport failed');
}
}
private function sendSmtp(string $to, string $subject, string $html, string $from, string $fromName): void
{
$host = getenv('SMTP_HOST') ?: '';
$port = (int)(getenv('SMTP_PORT') ?: 587);
$user = getenv('SMTP_USER') ?: '';
$pass = getenv('SMTP_PASS') ?: '';
$secure = strtolower(getenv('SMTP_SECURE') ?: 'tls'); // tls|ssl|none
if (!$host) {
$this->sendMailFn($to, $subject, $html, $from, $fromName);
return;
}
$proto = ($secure === 'ssl') ? 'ssl://' : '';
$fp = @stream_socket_client($proto . $host . ':' . $port, $errno, $errstr, 15, STREAM_CLIENT_CONNECT);
if (!$fp) {
$this->sendMailFn($to, $subject, $html, $from, $fromName);
return;
}
stream_set_timeout($fp, 15);
$read = function () use ($fp) {
return fgets($fp, 515);
};
$write = function (string $cmd) use ($fp) {
fwrite($fp, $cmd . "\r\n");
};
$read();
$write('EHLO ' . $this->app->config()->primaryDomain);
$read();
if ($secure === 'tls') {
$write('STARTTLS');
$read();
if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
fclose($fp);
$this->sendMailFn($to, $subject, $html, $from, $fromName);
return;
}
$write('EHLO ' . $this->app->config()->primaryDomain);
$read();
}
if ($user !== '') {
$write('AUTH LOGIN');
$read();
$write(base64_encode($user));
$read();
$write(base64_encode($pass));
$read();
}
$write('MAIL FROM: <' . $from . '>');
$read();
$write('RCPT TO: <' . $to . '>');
$read();
$write('DATA');
$read();
$msg = "From: {$fromName} <{$from}>\r\n";
$msg .= "To: <{$to}>\r\n";
$msg .= "Subject: {$subject}\r\n";
$msg .= "MIME-Version: 1.0\r\n";
$msg .= "Content-Type: text/html; charset=utf-8\r\n\r\n";
$msg .= $html . "\r\n.\r\n";
$write($msg);
$read();
$write('QUIT');
fclose($fp);
}
}