This commit is contained in:
2025-12-28 01:19:52 +01:00
parent 50d803dade
commit 6ba658262f
2 changed files with 108 additions and 0 deletions

View File

@@ -9,5 +9,16 @@ declare(strict_types=1);
// Force scope flag so ApiKernel can limit available actions if needed // Force scope flag so ApiKernel can limit available actions if needed
$_GET['scope'] = $_GET['scope'] ?? 'external'; $_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 // Re-use the existing kernel bootstrap
require_once __DIR__ . '/../public/api.php'; require_once __DIR__ . '/../public/api.php';

View File

@@ -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 = '<p>(Dieses Template enthält noch keine HTML-Inhalte.)</p>';
}
$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 private function prepareEmailHtml(string $html): string
{ {
$html = trim($html); $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 private function dispatchTestMail(string $to, string $subject, string $html, ?array $sender = null): bool
{ {
if (!function_exists('mail')) { if (!function_exists('mail')) {
@@ -814,6 +908,9 @@ class ApiKernel
switch ($this->action) { switch ($this->action) {
case 'health': case 'health':
$this->respond(['ok' => true, 'time' => date('c')]); $this->respond(['ok' => true, 'time' => date('c')]);
case 'external.render':
$this->handleExternalRender();
break;
/* ---------- AUTH ---------- */ /* ---------- AUTH ---------- */
case 'auth.login': case 'auth.login':