From 6ba658262f1174a02043ede189145ec0fc44c06b Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sun, 28 Dec 2025 01:19:52 +0100 Subject: [PATCH] sss --- api/index.php | 11 ++++++ src/ApiKernel.php | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/api/index.php b/api/index.php index ce3bed6..78e14a8 100644 --- a/api/index.php +++ b/api/index.php @@ -9,5 +9,16 @@ declare(strict_types=1); // Force scope flag so ApiKernel can limit available actions if needed $_GET['scope'] = $_GET['scope'] ?? 'external'; +// Map /external/render requests to the correct ApiKernel action when no action is given +$reqPath = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH); +if ( + empty($_GET['action']) + && empty($_POST['action']) + && is_string($reqPath) + && preg_match('~^/external/render/?$~', $reqPath) +) { + $_POST['action'] = 'external.render'; +} + // Re-use the existing kernel bootstrap require_once __DIR__ . '/../public/api.php'; diff --git a/src/ApiKernel.php b/src/ApiKernel.php index 59109ae..9b12657 100644 --- a/src/ApiKernel.php +++ b/src/ApiKernel.php @@ -734,6 +734,79 @@ class ApiKernel ]); } + private function handleExternalRender(): void + { + $token = trim((string)($this->in['token'] ?? '')); + if ($token === '') { + $this->fail('Token required', null, 401); + } + + $settingsTable = $this->customerSettingsTable(); + $stmt = $this->pdo->prepare("SELECT `customer_id` FROM `$settingsTable` WHERE `external_api_token` = :t LIMIT 1"); + $stmt->execute([':t' => $token]); + $row = $stmt->fetch(); + $customerId = (int)($row['customer_id'] ?? 0); + if ($customerId <= 0) { + $this->fail('Invalid token', null, 403); + } + + $templatesTable = $this->tableMap['templates'] ?? null; + if (!$templatesTable || !$this->tableExists($templatesTable)) { + $this->fail('Templates table not available', null, 500); + } + + [$idCol, $allCols] = $this->resolveIdCol('templates'); + $nameCol = $this->conf['columns']['templates']['name'] ?? ($this->firstExisting($allCols, ['name']) ?: $idCol); + + $templateKey = $this->val($this->in, ['template', 'template_id', 'id', 'name'], ''); + $templateId = is_numeric($templateKey) ? (int)$templateKey : null; + + $where = "WHERE `customer_id` = :cid "; + $params = [':cid' => $customerId]; + if ($templateId !== null && $templateId > 0) { + $where .= "AND `$idCol` = :id "; + $params[':id'] = $templateId; + } else { + $name = trim((string)$templateKey); + if ($name === '') { + $this->fail('template required', null, 422); + } + $where .= "AND `$nameCol` = :name "; + $params[':name'] = $name; + } + + $sql = "SELECT * FROM `$templatesTable` $where LIMIT 1"; + $stmt = $this->pdo->prepare($sql); + foreach ($params as $k => $v) { + $stmt->bindValue($k, $v, is_int($v) ? PDO::PARAM_INT : PDO::PARAM_STR); + } + $stmt->execute(); + $tpl = $stmt->fetch(); + if (!$tpl) { + $this->fail('Template not found', ['template' => $templateKey], 404); + } + + $htmlCol = $this->resolveHtmlColumn($allCols, 'templates'); + $html = ($htmlCol && isset($tpl[$htmlCol])) ? (string)$tpl[$htmlCol] : ''; + if ($html === '' && !empty($tpl['json_content'])) { + $html = '

(Dieses Template enthält noch keine HTML-Inhalte.)

'; + } + + $auth = ['id' => $customerId, 'customer_id' => $customerId]; + $cache = []; + $stack = []; + $html = $this->renderHtmlWithReferences($html, $auth, $cache, $stack); + $html = $this->replacePlaceholders($html, (array)($this->in['placeholders'] ?? [])); + $html = $this->prepareEmailHtml($html); + + $this->respond([ + 'ok' => true, + 'template_id' => (int)($tpl[$idCol] ?? 0), + 'name' => $tpl[$nameCol] ?? null, + 'html' => $html, + ]); + } + private function prepareEmailHtml(string $html): string { $html = trim($html); @@ -753,6 +826,27 @@ class ApiKernel } } + private function replacePlaceholders(string $html, array $placeholders): string + { + if ($html === '' || !$placeholders) { + return $html; + } + + $map = []; + foreach ($placeholders as $key => $value) { + if (is_array($value) || is_object($value)) continue; + $value = (string)$value; + $trimmedKey = trim((string)$key); + if ($trimmedKey === '') continue; + $map['{{' . $trimmedKey . '}}'] = $value; + $map['{{ ' . $trimmedKey . ' }}'] = $value; + $map['[[' . $trimmedKey . ']]'] = $value; + $map['[[ ' . $trimmedKey . ' ]]'] = $value; + } + + return $map ? strtr($html, $map) : $html; + } + private function dispatchTestMail(string $to, string $subject, string $html, ?array $sender = null): bool { if (!function_exists('mail')) { @@ -814,6 +908,9 @@ class ApiKernel switch ($this->action) { case 'health': $this->respond(['ok' => true, 'time' => date('c')]); + case 'external.render': + $this->handleExternalRender(); + break; /* ---------- AUTH ---------- */ case 'auth.login':