| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- <?php
- namespace Controllers;
- use Libs\ExecLib;
- use Libs\ResponseLib;
- use Psr\Http\Message\ServerRequestInterface;
- use function React\Async\await;
- class MailSendController
- {
- public function __invoke(ServerRequestInterface $request)
- {
- $body = json_decode((string) $request->getBody(), true);
- if (!is_array($body)) {
- return ResponseLib::sendFail('Invalid JSON body', [], 'E_VALIDATE')->withStatus(400);
- }
- $templateName = trim((string)($body['mail_template'] ?? ''));
- $config = $body['mail_config'] ?? null;
- if ($templateName === '' || !is_array($config)) {
- return ResponseLib::sendFail('Missing mail_template or mail_config', [], 'E_VALIDATE')->withStatus(400);
- }
- // Required fields for sending
- $toEmail = trim((string)($config['to_email'] ?? ''));
- $toName = trim((string)($config['to_name'] ?? ''));
- $subject = trim((string)($config['subject'] ?? ''));
- if ($toEmail === '' || $subject === '') {
- return ResponseLib::sendFail('Missing to_email or subject', [], 'E_VALIDATE')->withStatus(400);
- }
- $fromEmail = trim((string)($_ENV['MAIL_FROM'] ?? 'no-reply@example.com'));
- $fromName = trim((string)($_ENV['MAIL_FROM_NAME'] ?? 'No Reply'));
- // Load template file from templates/{name}.tpl
- $tplPath = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $templateName . '.tpl';
- if (!is_file($tplPath)) {
- return ResponseLib::sendFail('Template not found', ['template' => $templateName], 'E_NOT_FOUND')->withStatus(404);
- }
- $tpl = file_get_contents($tplPath);
- if ($tpl === false) {
- return ResponseLib::sendFail('Failed to load template', ['template' => $templateName], 'E_IO')->withStatus(500);
- }
- // Simple placeholder substitution: {{key}}
- $replacements = [];
- foreach ($config as $k => $v) {
- if (is_scalar($v)) {
- $replacements['{{' . $k . '}}'] = (string)$v;
- }
- }
- $bodyText = strtr($tpl, $replacements);
- // Build RFC 5322 message
- $headers = [];
- $headers[] = sprintf('From: %s <%s>', $fromName !== '' ? $fromName : $fromEmail, $fromEmail);
- $headers[] = sprintf('To: %s <%s>', $toName !== '' ? $toName : $toEmail, $toEmail);
- $headers[] = sprintf('Subject: %s', $subject);
- $headers[] = 'MIME-Version: 1.0';
- $headers[] = 'Content-Type: text/plain; charset=UTF-8';
- $headers[] = 'Content-Transfer-Encoding: 8bit';
- $message = implode("\r\n", $headers) . "\r\n\r\n" . $bodyText . "\r\n";
- // Write to temp file
- $tmpFile = tempnam(sys_get_temp_dir(), 'mail_');
- if ($tmpFile === false) {
- return ResponseLib::sendFail('Failed to create temp file', [], 'E_IO')->withStatus(500);
- }
- file_put_contents($tmpFile, $message);
- // Execute sendmail (postfix)
- $sendmail = $_ENV['SENDMAIL_PATH'] ?? '/usr/sbin/sendmail';
- $cmd = sprintf("%s -t -i < %s", escapeshellcmd($sendmail), escapeshellarg($tmpFile));
- try {
- [$code, $output] = await(ExecLib::exec($cmd, (float)($_ENV['SENDMAIL_TIMEOUT'] ?? 10)));
- } catch (\Throwable $e) {
- @unlink($tmpFile);
- return ResponseLib::sendFail('Sendmail failed', ['error' => $e->getMessage()], 'E_SENDMAIL')->withStatus(500);
- } finally {
- @unlink($tmpFile);
- }
- if ($code !== 0) {
- return ResponseLib::sendFail('Sendmail exited with non-zero', ['code' => $code, 'output' => $output], 'E_SENDMAIL')->withStatus(500);
- }
- return ResponseLib::sendOk([
- 'status' => 'sent',
- 'to' => $toEmail,
- 'template' => $templateName,
- 'code' => $code,
- ]);
- }
- }
|