sdasd
This commit is contained in:
@@ -642,6 +642,7 @@ class ApiKernel
|
||||
if ($subject === '') {
|
||||
$subject = 'Testversand';
|
||||
}
|
||||
$senderId = (int)$this->val($this->in, ['sender_id'], 0);
|
||||
|
||||
$t = $this->tableMap['templates'];
|
||||
[$idCol, $allCols] = $this->resolveIdCol('templates');
|
||||
@@ -668,7 +669,15 @@ class ApiKernel
|
||||
$html = $this->renderHtmlWithReferences($html, $auth, $renderCache, $renderStack);
|
||||
$html = $this->prepareEmailHtml($html);
|
||||
|
||||
if (!$this->dispatchTestMail($recipient, $subject, $html)) {
|
||||
$sender = null;
|
||||
if ($senderId > 0) {
|
||||
$customerId = (int)($auth['customer_id'] ?? 0);
|
||||
if ($customerId > 0) {
|
||||
$sender = $this->fetchSenderRow($customerId, $senderId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->dispatchTestMail($recipient, $subject, $html, $sender)) {
|
||||
$this->fail('Send failed', null, 500);
|
||||
}
|
||||
|
||||
@@ -677,6 +686,7 @@ class ApiKernel
|
||||
'template_id' => $templateId,
|
||||
'to' => $recipient,
|
||||
'subject' => $subject,
|
||||
'sender_id' => $senderId > 0 ? $senderId : null,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -699,20 +709,24 @@ class ApiKernel
|
||||
}
|
||||
}
|
||||
|
||||
private function dispatchTestMail(string $to, string $subject, string $html): bool
|
||||
private function dispatchTestMail(string $to, string $subject, string $html, ?array $sender = null): bool
|
||||
{
|
||||
if (!function_exists('mail')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$smtp = $this->conf['smtp'] ?? [];
|
||||
$fromEmail = $smtp['from_email'] ?? 'no-reply@example.com';
|
||||
$fromName = $smtp['from_name'] ?? 'EmailTemplate';
|
||||
$fromEmail = $sender['from_email'] ?? ($smtp['from_email'] ?? 'no-reply@example.com');
|
||||
$fromName = $sender['from_name'] ?? ($sender['label'] ?? ($smtp['from_name'] ?? 'EmailTemplate'));
|
||||
$replyTo = $sender['reply_to'] ?? '';
|
||||
$headers = [
|
||||
'MIME-Version: 1.0',
|
||||
'Content-Type: text/html; charset=UTF-8',
|
||||
'From: ' . $this->formatEmailAddress($fromEmail, $fromName),
|
||||
];
|
||||
if ($replyTo !== '') {
|
||||
$headers[] = 'Reply-To: ' . $this->formatEmailAddress($replyTo, $fromName ?: $fromEmail);
|
||||
}
|
||||
|
||||
$encodedSubject = function_exists('mb_encode_mimeheader')
|
||||
? mb_encode_mimeheader($subject, 'UTF-8')
|
||||
@@ -797,12 +811,24 @@ class ApiKernel
|
||||
case 'account.users.delete':
|
||||
$this->handleAccountUsersDelete();
|
||||
break;
|
||||
case 'account.senders.list':
|
||||
$this->handleAccountSendersList();
|
||||
break;
|
||||
case 'account.senders.save':
|
||||
$this->handleAccountSenderSave();
|
||||
break;
|
||||
case 'account.senders.delete':
|
||||
$this->handleAccountSenderDelete();
|
||||
break;
|
||||
case 'downloads.bridge':
|
||||
$this->handleDownloadFile('bridge');
|
||||
break;
|
||||
case 'downloads.sender':
|
||||
$this->handleDownloadFile('sender');
|
||||
break;
|
||||
case 'account.bridge.test':
|
||||
$this->handleAccountBridgeTest();
|
||||
break;
|
||||
case 'placeholders.schema':
|
||||
$this->handlePlaceholderSchema();
|
||||
break;
|
||||
@@ -1080,9 +1106,15 @@ class ApiKernel
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = $this->getCustomerSettings($customerId);
|
||||
$tables = $schema['tables'] ?? [];
|
||||
if (!empty($settings['bridge_tables'])) {
|
||||
$tables = $this->filterSchemaTables($tables, $settings['bridge_tables']);
|
||||
}
|
||||
|
||||
$this->respond([
|
||||
'ok' => true,
|
||||
'tables' => $schema['tables'] ?? [],
|
||||
'tables' => $tables,
|
||||
'fetched' => $schema['fetched'] ?? date(DATE_ATOM),
|
||||
]);
|
||||
}
|
||||
@@ -1237,7 +1269,7 @@ class ApiKernel
|
||||
private function handleAccountSettingsGet(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureOwner($user);
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
$settings = $this->ensureSettingsTokens($customerId, $this->getCustomerSettings($customerId));
|
||||
$this->respond(['ok' => true, 'settings' => $settings]);
|
||||
@@ -1246,7 +1278,7 @@ class ApiKernel
|
||||
private function handleAccountSettingsUpdate(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureOwner($user);
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
if ($customerId <= 0) $this->fail('Customer context missing', null, 500);
|
||||
|
||||
@@ -1254,6 +1286,8 @@ class ApiKernel
|
||||
$bridgeToken = trim((string)($this->in['bridge_token'] ?? ''));
|
||||
$senderToken = trim((string)($this->in['sender_token'] ?? ''));
|
||||
$externalToken = trim((string)($this->in['external_api_token'] ?? ''));
|
||||
$bridgeTablesInput = $this->in['bridge_tables'] ?? null;
|
||||
$bridgeTables = $this->normalizeBridgeTables($bridgeTablesInput);
|
||||
$rotateBridge = !empty($this->in['rotate_bridge_token']);
|
||||
$rotateSender = !empty($this->in['rotate_sender_token']);
|
||||
$rotateExternal = !empty($this->in['rotate_external_token']);
|
||||
@@ -1272,6 +1306,7 @@ class ApiKernel
|
||||
'bridge_token' => $bridgeToken,
|
||||
'sender_token' => $senderToken,
|
||||
'external_api_token' => $externalToken,
|
||||
'bridge_tables' => $bridgeTables,
|
||||
]);
|
||||
|
||||
$this->respond(['ok' => true, 'settings' => $settings]);
|
||||
@@ -1473,10 +1508,86 @@ class ApiKernel
|
||||
$this->respond(['ok' => true, 'deleted' => true]);
|
||||
}
|
||||
|
||||
private function handleAccountSendersList(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
if ($customerId <= 0) $this->fail('Customer context missing', null, 500);
|
||||
$table = $this->senderTable();
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM `$table` WHERE `customer_id` = :cid ORDER BY `label` ASC");
|
||||
$stmt->execute([':cid' => $customerId]);
|
||||
$items = [];
|
||||
while ($row = $stmt->fetch()) {
|
||||
$items[] = $this->formatSenderRow($row);
|
||||
}
|
||||
$this->respond(['ok' => true, 'items' => $items]);
|
||||
}
|
||||
|
||||
private function handleAccountSenderSave(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
|
||||
$senderId = (int)($this->in['sender_id'] ?? 0);
|
||||
$label = trim((string)($this->in['label'] ?? ''));
|
||||
$fromName = trim((string)($this->in['from_name'] ?? ''));
|
||||
$fromEmail = trim((string)($this->in['from_email'] ?? ''));
|
||||
$replyTo = trim((string)($this->in['reply_to'] ?? ''));
|
||||
if ($label === '') $label = $fromName ?: $fromEmail;
|
||||
if ($fromEmail === '' || !filter_var($fromEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->fail('Gültige Absender-Adresse erforderlich', null, 422);
|
||||
}
|
||||
if ($replyTo !== '' && !filter_var($replyTo, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->fail('Ungültige Reply-To-Adresse', null, 422);
|
||||
}
|
||||
|
||||
$table = $this->senderTable();
|
||||
if ($senderId > 0) {
|
||||
$stmt = $this->pdo->prepare("UPDATE `$table` SET `label`=:label,`from_name`=:fname,`from_email`=:fmail,`reply_to`=:reply,`updated_at`=NOW() WHERE `id`=:id AND `customer_id`=:cid LIMIT 1");
|
||||
$stmt->execute([
|
||||
':label' => $label,
|
||||
':fname' => $fromName ?: null,
|
||||
':fmail' => $fromEmail,
|
||||
':reply' => $replyTo ?: null,
|
||||
':id' => $senderId,
|
||||
':cid' => $customerId,
|
||||
]);
|
||||
if ($stmt->rowCount() === 0) $this->fail('Absender nicht gefunden', null, 404);
|
||||
} else {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO `$table` (`customer_id`,`label`,`from_name`,`from_email`,`reply_to`,`created_at`,`updated_at`) VALUES (:cid,:label,:fname,:fmail,:reply,NOW(),NOW())");
|
||||
$stmt->execute([
|
||||
':cid' => $customerId,
|
||||
':label' => $label,
|
||||
':fname' => $fromName ?: null,
|
||||
':fmail' => $fromEmail,
|
||||
':reply' => $replyTo ?: null,
|
||||
]);
|
||||
$senderId = (int)$this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
$sender = $this->fetchSenderRow($customerId, $senderId);
|
||||
$this->respond(['ok' => true, 'sender' => $sender]);
|
||||
}
|
||||
|
||||
private function handleAccountSenderDelete(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
$senderId = (int)($this->in['sender_id'] ?? 0);
|
||||
if ($senderId <= 0) $this->fail('Ungültige Sender-ID', null, 422);
|
||||
$table = $this->senderTable();
|
||||
$stmt = $this->pdo->prepare("DELETE FROM `$table` WHERE `id` = :id AND `customer_id` = :cid LIMIT 1");
|
||||
$stmt->execute([':id' => $senderId, ':cid' => $customerId]);
|
||||
if ($stmt->rowCount() === 0) $this->fail('Absender nicht gefunden', null, 404);
|
||||
$this->respond(['ok' => true, 'deleted' => true]);
|
||||
}
|
||||
|
||||
private function handleDownloadFile(string $type): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureOwner($user);
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
if ($customerId <= 0) $this->fail('Customer context missing', null, 500);
|
||||
|
||||
@@ -1511,6 +1622,34 @@ class ApiKernel
|
||||
]);
|
||||
}
|
||||
|
||||
private function handleAccountBridgeTest(): void
|
||||
{
|
||||
$user = $this->authService->requireAuth();
|
||||
$this->ensureRole($user, ['owner', 'admin']);
|
||||
$customerId = (int)($user['customer_id'] ?? 0);
|
||||
$bridgeUrl = trim((string)($this->in['bridge_url'] ?? ''));
|
||||
$bridgeToken = trim((string)($this->in['bridge_token'] ?? ''));
|
||||
if ($bridgeUrl === '' || $bridgeToken === '') {
|
||||
$settings = $this->getCustomerSettings($customerId);
|
||||
if ($bridgeUrl === '') $bridgeUrl = (string)($settings['bridge_url'] ?? '');
|
||||
if ($bridgeToken === '') $bridgeToken = (string)($settings['bridge_token'] ?? '');
|
||||
}
|
||||
if ($bridgeUrl === '' || $bridgeToken === '') {
|
||||
$this->fail('Bridge nicht konfiguriert', null, 422);
|
||||
}
|
||||
try {
|
||||
$schema = $this->fetchPlaceholderSchema($bridgeUrl, $bridgeToken, 0);
|
||||
} catch (Throwable $e) {
|
||||
$this->fail('Bridge request failed', $e->getMessage(), 502);
|
||||
return;
|
||||
}
|
||||
$this->respond([
|
||||
'ok' => true,
|
||||
'tables' => $schema['tables'] ?? [],
|
||||
'fetched' => $schema['fetched'] ?? date(DATE_ATOM),
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveBridgeConfig(?int $customerId): array
|
||||
{
|
||||
$fileConf = $this->conf['placeholders']['bridge'] ?? [];
|
||||
@@ -1528,15 +1667,19 @@ class ApiKernel
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM `$table` WHERE `customer_id` = :id LIMIT 1");
|
||||
$stmt->execute([':id' => $customerId]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: [];
|
||||
return $row ? $this->formatCustomerSettingsRow($row) : [];
|
||||
}
|
||||
|
||||
private function saveCustomerSettings(int $customerId, array $data): array
|
||||
{
|
||||
if ($customerId <= 0) return [];
|
||||
$allowed = ['bridge_url', 'bridge_token', 'sender_token', 'external_api_token'];
|
||||
$allowed = ['bridge_url', 'bridge_token', 'sender_token', 'external_api_token', 'bridge_tables'];
|
||||
$fields = array_intersect_key($data, array_flip($allowed));
|
||||
if (!$fields) return $this->getCustomerSettings($customerId);
|
||||
if (array_key_exists('bridge_tables', $fields)) {
|
||||
$normalized = $this->normalizeBridgeTables($fields['bridge_tables']);
|
||||
$fields['bridge_tables'] = $normalized ? $this->encodeBridgeTables($normalized) : null;
|
||||
}
|
||||
$fields['customer_id'] = $customerId;
|
||||
$columns = array_keys($fields);
|
||||
$insertCols = implode(',', array_map(fn($c) => "`$c`", $columns));
|
||||
@@ -1572,6 +1715,76 @@ class ApiKernel
|
||||
return $settings;
|
||||
}
|
||||
|
||||
private function formatCustomerSettingsRow(array $row): array
|
||||
{
|
||||
if (array_key_exists('bridge_tables', $row)) {
|
||||
$row['bridge_tables'] = $this->decodeBridgeTables($row['bridge_tables']);
|
||||
} else {
|
||||
$row['bridge_tables'] = [];
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
private function normalizeBridgeTables($input): array
|
||||
{
|
||||
$items = [];
|
||||
if (is_string($input)) {
|
||||
$items = preg_split('/[\s,]+/', $input) ?: [];
|
||||
} elseif (is_array($input)) {
|
||||
$items = $input;
|
||||
} elseif ($input === null) {
|
||||
return [];
|
||||
} else {
|
||||
$items = [$input];
|
||||
}
|
||||
$items = array_map(static function ($value) {
|
||||
return trim((string)$value);
|
||||
}, $items);
|
||||
$items = array_filter($items, fn($value) => $value !== '');
|
||||
return array_values(array_unique($items));
|
||||
}
|
||||
|
||||
private function encodeBridgeTables(?array $tables): ?string
|
||||
{
|
||||
if (empty($tables)) return null;
|
||||
return json_encode(array_values($tables), JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
private function decodeBridgeTables($stored): array
|
||||
{
|
||||
if (is_array($stored)) {
|
||||
return $this->normalizeBridgeTables($stored);
|
||||
}
|
||||
$str = (string)$stored;
|
||||
if ($str === '') return [];
|
||||
$decoded = json_decode($str, true);
|
||||
if (is_array($decoded)) {
|
||||
return $this->normalizeBridgeTables($decoded);
|
||||
}
|
||||
return $this->normalizeBridgeTables($str);
|
||||
}
|
||||
|
||||
private function filterSchemaTables(array $tables, array $allowed): array
|
||||
{
|
||||
if (empty($allowed)) return $tables;
|
||||
$allowedLower = array_map('strtolower', $allowed);
|
||||
$filtered = [];
|
||||
foreach ($tables as $entry) {
|
||||
if (is_array($entry)) {
|
||||
$name = strtolower((string)($entry['name'] ?? $entry['table'] ?? $entry['label'] ?? ''));
|
||||
if ($name !== '' && in_array($name, $allowedLower, true)) {
|
||||
$filtered[] = $entry;
|
||||
}
|
||||
} else {
|
||||
$name = strtolower((string)$entry);
|
||||
if ($name !== '' && in_array($name, $allowedLower, true)) {
|
||||
$filtered[] = $entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
private function customerSettingsTable(): string
|
||||
{
|
||||
return 'emailtemplate_customer_settings';
|
||||
@@ -1666,6 +1879,37 @@ class ApiKernel
|
||||
]);
|
||||
}
|
||||
|
||||
private function senderTable(): string
|
||||
{
|
||||
return 'emailtemplate_sender_identities';
|
||||
}
|
||||
|
||||
private function fetchSenderRow(int $customerId, int $senderId): array
|
||||
{
|
||||
if ($customerId <= 0 || $senderId <= 0) {
|
||||
$this->fail('Absender nicht gefunden', null, 404);
|
||||
}
|
||||
$table = $this->senderTable();
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM `$table` WHERE `id` = :id AND `customer_id` = :cid LIMIT 1");
|
||||
$stmt->execute([':id' => $senderId, ':cid' => $customerId]);
|
||||
$row = $stmt->fetch();
|
||||
if (!$row) $this->fail('Absender nicht gefunden', null, 404);
|
||||
return $this->formatSenderRow($row);
|
||||
}
|
||||
|
||||
private function formatSenderRow(array $row): array
|
||||
{
|
||||
return [
|
||||
'id' => (int)($row['id'] ?? 0),
|
||||
'label' => $row['label'] ?? '',
|
||||
'from_name' => $row['from_name'] ?? '',
|
||||
'from_email' => $row['from_email'] ?? '',
|
||||
'reply_to' => $row['reply_to'] ?? '',
|
||||
'created_at' => $row['created_at'] ?? null,
|
||||
'updated_at' => $row['updated_at'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
private function formatUserOutput(array $row): array
|
||||
{
|
||||
return [
|
||||
@@ -1720,8 +1964,15 @@ class ApiKernel
|
||||
|
||||
private function ensureOwner(array $user): void
|
||||
{
|
||||
if (($user['role'] ?? '') !== 'owner') {
|
||||
$this->fail('Nur Owner dürfen diese Aktion ausführen', null, 403);
|
||||
$this->ensureRole($user, ['owner']);
|
||||
}
|
||||
|
||||
private function ensureRole(array $user, array $roles): void
|
||||
{
|
||||
$role = strtolower((string)($user['role'] ?? ''));
|
||||
$allowed = array_values(array_unique(array_map('strtolower', $roles)));
|
||||
if (!in_array($role, $allowed, true)) {
|
||||
$this->fail('Unzureichende Berechtigungen', null, 403);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user