commit
This commit is contained in:
492
public/api.php.txt
Normal file
492
public/api.php.txt
Normal file
@@ -0,0 +1,492 @@
|
||||
<?php
|
||||
// api.php — Multi-User API mit Legacy-Kompatibilität
|
||||
// Stand: 2025-09-05
|
||||
|
||||
declare(strict_types=1);
|
||||
header('X-API-Version: 2025-09-05');
|
||||
|
||||
/* ------------------------- Fehler als JSON ------------------------- */
|
||||
set_error_handler(static function ($severity, $message, $file, $line) {
|
||||
throw new ErrorException($message, 0, $severity, $file, $line);
|
||||
});
|
||||
set_exception_handler(static function (Throwable $e) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => 'internal',
|
||||
'type' => get_class($e),
|
||||
'msg' => $e->getMessage(),
|
||||
'file' => basename($e->getFile()),
|
||||
'line' => $e->getLine(),
|
||||
], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
});
|
||||
|
||||
/* ------------------------- Helpers ------------------------- */
|
||||
function json_out(array $data, int $code = 200): void {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
function in_json(): array {
|
||||
$raw = file_get_contents('php://input') ?: '';
|
||||
if ($raw === '') return [];
|
||||
$j = json_decode($raw, true);
|
||||
return is_array($j) ? $j : [];
|
||||
}
|
||||
|
||||
/* ------------------------- Config laden ------------------------- */
|
||||
// Parent /inc/config.php (wie von dir beschrieben)
|
||||
$cfgFile = dirname(__DIR__) . '/inc/config.php';
|
||||
if (!is_file($cfgFile)) {
|
||||
json_out(['ok'=>false,'error'=>'config_missing','hint'=>'config.php nicht gefunden','path'=>$cfgFile], 500);
|
||||
}
|
||||
$CFG = include $cfgFile;
|
||||
if (!is_array($CFG)) {
|
||||
json_out(['ok'=>false,'error'=>'config_invalid','hint'=>'config.php muss ein Array zurückgeben'], 500);
|
||||
}
|
||||
$ENV = $CFG['env'] ?? 'prod';
|
||||
|
||||
/* ------------------------- PDO-Factories ------------------------- */
|
||||
function pdo_from_cfg(?array $dbc): ?PDO {
|
||||
if (!$dbc) return null;
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;dbname=%s;charset=%s',
|
||||
$dbc['db_host'] ?? 'localhost',
|
||||
$dbc['db_name'] ?? '',
|
||||
$dbc['db_charset']?? 'utf8mb4'
|
||||
);
|
||||
return new PDO($dsn, $dbc['db_user'] ?? '', $dbc['db_pass'] ?? '', [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
}
|
||||
$pdoTpl = pdo_from_cfg($CFG['templates'] ?? null); // Template-Daten
|
||||
$pdoCust = pdo_from_cfg(($CFG['customers'] ?? null) ?: ($CFG['templates'] ?? null)); // Kunden/Users
|
||||
|
||||
$TPL_DB = $CFG['templates']['db_name'] ?? null; // für information_schema
|
||||
|
||||
function has_column(PDO $pdo, ?string $db, string $table, string $col): bool {
|
||||
if (!$db) return false;
|
||||
$st = $pdo->prepare("SELECT 1 FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA=:db AND TABLE_NAME=:t AND COLUMN_NAME=:c
|
||||
LIMIT 1");
|
||||
$st->execute([':db'=>$db, ':t'=>$table, ':c'=>$col]);
|
||||
return (bool)$st->fetchColumn();
|
||||
}
|
||||
|
||||
/* ------------------------- Auth (Helper oder Fallback) ------------------------- */
|
||||
$authFile = dirname(__DIR__) . '/inc/auth_helpers.php';
|
||||
$useHelpers = is_file($authFile);
|
||||
if ($useHelpers) {
|
||||
require_once $authFile; // stellt auth_start_session(), auth_require(), auth_logout(), require_role() bereit
|
||||
} else {
|
||||
// interner Fallback – kompatibel zu deinen Erwartungen
|
||||
function auth_start_session(array $CFG): void {
|
||||
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||
session_set_cookie_params([
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
'secure' => $secure,
|
||||
'path' => rtrim(dirname($_SERVER['SCRIPT_NAME']),'/').'/'
|
||||
]);
|
||||
session_name('et_session');
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
|
||||
}
|
||||
function auth_require(array $CFG): void {
|
||||
auth_start_session($CFG);
|
||||
if (empty($_SESSION['user'])) {
|
||||
json_out(['ok'=>false,'error'=>'unauthorized'], 401);
|
||||
}
|
||||
}
|
||||
function auth_logout(array $CFG): void {
|
||||
auth_start_session($CFG);
|
||||
$_SESSION = [];
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$p = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time()-42000, $p['path'], $p['domain'] ?? '', $p['secure'], $p['httponly']);
|
||||
}
|
||||
session_destroy();
|
||||
}
|
||||
function require_role(array $CFG, array $roles): void {
|
||||
auth_start_session($CFG);
|
||||
$r = $_SESSION['user']['role'] ?? null;
|
||||
if (!$r || !in_array($r, $roles, true)) json_out(['ok'=>false,'error'=>'forbidden'], 403);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------- Routing + Legacy-Mapping ------------------------- */
|
||||
$action = $_GET['action'] ?? $_POST['action'] ?? null;
|
||||
|
||||
// Alt: ?resource=blocks&action=list|get|create|update|delete|sync
|
||||
if (!empty($_GET['resource'])) {
|
||||
$res = (string)$_GET['resource'];
|
||||
$act = (string)($_GET['action'] ?? '');
|
||||
$allowed = ['templates','sections','blocks','snippets','assets','template_items','section_items'];
|
||||
if (in_array($res, $allowed, true)) {
|
||||
if ($act === 'list') $action = $res.'.list';
|
||||
if ($act === 'get') $action = $res.'.get';
|
||||
if ($act === 'create') $action = $res.'.create';
|
||||
if ($act === 'update') $action = $res.'.update';
|
||||
if ($act === 'delete') $action = $res.'.delete';
|
||||
if ($act === 'sync') $action = $res.'.sync';
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------- Meta/Health ------------------------- */
|
||||
if ($action === 'health' || $action === 'ping') json_out(['ok'=>true,'env'=>$ENV,'time'=>date('c')]);
|
||||
if ($action === 'version') json_out(['ok'=>true,'version'=>'2025-09-05','env'=>$ENV]);
|
||||
|
||||
/* ------------------------- Diagnose (leichtgewichtig) ------------------------- */
|
||||
if ($action === 'debug.diag') {
|
||||
$diag = [
|
||||
'php' => PHP_VERSION,
|
||||
'pdo' => extension_loaded('pdo'),
|
||||
'pdo_mysql'=> extension_loaded('pdo_mysql'),
|
||||
'cfg' => ['templates'=>!!$pdoTpl, 'customers'=>!!$pdoCust],
|
||||
];
|
||||
json_out(['ok'=>true,'diag'=>$diag]);
|
||||
}
|
||||
|
||||
/* ------------------------- STAGING: User-Debug ------------------------- */
|
||||
if (in_array($action, ['debug.users','debug.users.check','debug.users.setpass','debug.users.peek'], true)) {
|
||||
if ($ENV !== 'staging') json_out(['ok'=>false,'error'=>'forbidden'], 403);
|
||||
if (!$pdoCust) json_out(['ok'=>false,'error'=>'customers_db_not_configured'], 500);
|
||||
|
||||
if ($action === 'debug.users') {
|
||||
$email = isset($_GET['email']) ? trim((string)$_GET['email']) : '';
|
||||
if ($email !== '') {
|
||||
$st = $pdoCust->prepare("SELECT id, customer_id, email, role, is_active, created_at, updated_at
|
||||
FROM customer_users WHERE email=:email");
|
||||
$st->execute([':email'=>$email]);
|
||||
$rows = $st->fetchAll();
|
||||
} else {
|
||||
$st = $pdoCust->query("SELECT id, customer_id, email, role, is_active, created_at, updated_at
|
||||
FROM customer_users ORDER BY id DESC LIMIT 50");
|
||||
$rows = $st->fetchAll();
|
||||
}
|
||||
json_out(['ok'=>true,'items'=>$rows]);
|
||||
}
|
||||
|
||||
if ($action === 'debug.users.check') {
|
||||
$in = in_json();
|
||||
$email = trim((string)($in['email'] ?? ''));
|
||||
$pass = (string)($in['password'] ?? '');
|
||||
if ($email==='' || $pass==='') json_out(['ok'=>false,'error'=>'missing_params'], 400);
|
||||
$st = $pdoCust->prepare("SELECT id, customer_id, email, password_hash, role, is_active FROM customer_users
|
||||
WHERE email=:email LIMIT 1");
|
||||
$st->execute([':email'=>$email]);
|
||||
$u = $st->fetch();
|
||||
if (!$u) json_out(['ok'=>true,'exists'=>false,'password_match'=>false]);
|
||||
json_out(['ok'=>true,'exists'=>true,'password_match'=>password_verify($pass,$u['password_hash'])]);
|
||||
}
|
||||
|
||||
if ($action === 'debug.users.setpass') {
|
||||
$in = in_json();
|
||||
$email = trim((string)($in['email'] ?? ''));
|
||||
$pass = (string)($in['password'] ?? '');
|
||||
if ($email==='' || $pass==='') json_out(['ok'=>false,'error'=>'missing_params'], 400);
|
||||
$st = $pdoCust->prepare("SELECT id FROM customer_users WHERE email=:email LIMIT 1");
|
||||
$st->execute([':email'=>$email]);
|
||||
$u = $st->fetch();
|
||||
if (!$u) json_out(['ok'=>false,'error'=>'user_not_found'], 404);
|
||||
$hash = password_hash($pass, PASSWORD_DEFAULT);
|
||||
$upd = $pdoCust->prepare("UPDATE customer_users SET password_hash=:h, is_active=1 WHERE id=:id");
|
||||
$upd->execute([':h'=>$hash, ':id'=>$u['id']]);
|
||||
json_out(['ok'=>true,'set'=>true]);
|
||||
}
|
||||
|
||||
if ($action === 'debug.users.peek') {
|
||||
$email = isset($_GET['email']) ? trim((string)$_GET['email']) : '';
|
||||
if ($email==='') json_out(['ok'=>false,'error'=>'missing_email'], 400);
|
||||
$st = $pdoCust->prepare("SELECT id, customer_id, email, LENGTH(password_hash) len
|
||||
FROM customer_users WHERE email=:email");
|
||||
$st->execute([':email'=>$email]);
|
||||
json_out(['ok'=>true,'user'=>$st->fetch()]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------- AUTH: Login / Logout / Me ------------------------- */
|
||||
if ($action === 'auth.login') {
|
||||
$in = in_json();
|
||||
$email = trim(strtolower((string)($in['email'] ?? '')));
|
||||
$pass = (string)($in['password'] ?? '');
|
||||
if ($email==='' || $pass==='') json_out(['ok'=>false,'error'=>'missing_credentials'], 400);
|
||||
if (!$pdoCust) json_out(['ok'=>false,'error'=>'customers_db_not_configured'], 500);
|
||||
|
||||
// Mehrfachkunden mit gleicher Mail erlauben → best match über password_verify
|
||||
$st = $pdoCust->prepare("SELECT cu.id, cu.customer_id, cu.email, cu.password_hash, cu.role, cu.is_active,
|
||||
c.slug AS customer_slug, c.plan, c.status
|
||||
FROM customer_users cu
|
||||
JOIN customers c ON c.id = cu.customer_id
|
||||
WHERE cu.email=:email");
|
||||
$st->execute([':email'=>$email]);
|
||||
$rows = $st->fetchAll();
|
||||
|
||||
$match = null;
|
||||
foreach ($rows as $r) {
|
||||
if ((int)$r['is_active'] === 1 && !empty($r['password_hash']) && password_verify($pass, $r['password_hash'])) {
|
||||
$match = $r; break;
|
||||
}
|
||||
}
|
||||
if (!$match) json_out(['ok'=>false,'error'=>'invalid_credentials'], 401);
|
||||
if (($match['status'] ?? 'active') !== 'active') json_out(['ok'=>false,'error'=>'customer_inactive'], 403);
|
||||
|
||||
auth_start_session($CFG);
|
||||
$_SESSION['user'] = [
|
||||
'id' => (int)$match['id'],
|
||||
'email' => $match['email'],
|
||||
'role' => $match['role'],
|
||||
'customer_id' => (int)$match['customer_id'],
|
||||
'customer_slug' => $match['customer_slug'],
|
||||
'plan' => $match['plan'] ?? null,
|
||||
];
|
||||
json_out(['ok'=>true,'user'=>$_SESSION['user']]);
|
||||
}
|
||||
|
||||
if ($action === 'auth.logout') { auth_logout($CFG); json_out(['ok'=>true]); }
|
||||
|
||||
if ($action === 'auth.me') {
|
||||
auth_start_session($CFG);
|
||||
json_out(['ok'=>!empty($_SESSION['user']), 'user'=>$_SESSION['user'] ?? null]);
|
||||
}
|
||||
|
||||
/* ------------------------- ab hier: geschützt ------------------------- */
|
||||
$public = ['auth.login','auth.logout','auth.me','health','ping','version','debug.diag','debug.users','debug.users.check','debug.users.setpass','debug.users.peek'];
|
||||
if (!in_array($action, $public, true)) auth_require($CFG);
|
||||
|
||||
$customerId = (int)($_SESSION['user']['customer_id'] ?? 0);
|
||||
|
||||
/* ------------------------- Templates ------------------------- */
|
||||
if ($action === 'templates.list') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$st = $pdoTpl->prepare("SELECT id, name, updated_at
|
||||
FROM emailtemplate_templates
|
||||
WHERE customer_id = :cid
|
||||
ORDER BY updated_at DESC, id DESC
|
||||
LIMIT 1000");
|
||||
$st->execute([':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'items'=>$st->fetchAll()]);
|
||||
}
|
||||
|
||||
if ($action === 'templates.get') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : (int)($_POST['id'] ?? 0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$hasHtml = has_column($pdoTpl, $TPL_DB, 'emailtemplate_templates', 'html');
|
||||
$cols = $hasHtml ? "id, customer_id, name, html, updated_at" : "id, customer_id, name, NULL AS html, updated_at";
|
||||
$st = $pdoTpl->prepare("SELECT $cols FROM emailtemplate_templates WHERE id=:id AND customer_id=:cid LIMIT 1");
|
||||
$st->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$row = $st->fetch();
|
||||
if (!$row) json_out(['ok'=>false,'error'=>'not_found'], 404);
|
||||
json_out(['ok'=>true,'item'=>$row]);
|
||||
}
|
||||
|
||||
if ($action === 'templates.create') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in = in_json();
|
||||
$name = trim((string)($in['name'] ?? ''));
|
||||
$html = (string)($in['html'] ?? '');
|
||||
if ($name==='') json_out(['ok'=>false,'error'=>'name_required'], 400);
|
||||
$hasHtml = has_column($pdoTpl, $TPL_DB, 'emailtemplate_templates', 'html');
|
||||
|
||||
if ($hasHtml) {
|
||||
$st = $pdoTpl->prepare("INSERT INTO emailtemplate_templates (customer_id,name,html,created_at,updated_at)
|
||||
VALUES (:cid,:name,:html,NOW(),NOW())");
|
||||
$st->execute([':cid'=>$customerId, ':name'=>$name, ':html'=>$html]);
|
||||
} else {
|
||||
$st = $pdoTpl->prepare("INSERT INTO emailtemplate_templates (customer_id,name,created_at,updated_at)
|
||||
VALUES (:cid,:name,NOW(),NOW())");
|
||||
$st->execute([':cid'=>$customerId, ':name'=>$name]);
|
||||
}
|
||||
json_out(['ok'=>true,'id'=>(int)$pdoTpl->lastInsertId()]);
|
||||
}
|
||||
|
||||
if ($action === 'templates.update') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in = in_json();
|
||||
$id = (int)($in['id'] ?? 0);
|
||||
$name = array_key_exists('name',$in) ? trim((string)$in['name']) : null;
|
||||
$html = array_key_exists('html',$in) ? (string)$in['html'] : null;
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
|
||||
$hasHtml = has_column($pdoTpl, $TPL_DB, 'emailtemplate_templates', 'html');
|
||||
$sets=[]; $p=[':id'=>$id, ':cid'=>$customerId];
|
||||
if ($name!==null) { $sets[]="name=:name"; $p[':name']=$name; }
|
||||
if ($hasHtml && $html!==null) { $sets[]="html=:html"; $p[':html']=$html; }
|
||||
if (!$sets) json_out(['ok'=>false,'error'=>'nothing_to_update'], 400);
|
||||
|
||||
$sql = "UPDATE emailtemplate_templates SET ".implode(',',$sets).", updated_at=NOW() WHERE id=:id AND customer_id=:cid";
|
||||
$st = $pdoTpl->prepare($sql);
|
||||
$st->execute($p);
|
||||
json_out(['ok'=>true,'updated'=>$st->rowCount()]);
|
||||
}
|
||||
|
||||
if ($action === 'templates.delete') {
|
||||
require_role($CFG, ['owner','admin']);
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in = in_json(); $id=(int)($in['id'] ?? 0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$pdoTpl->beginTransaction();
|
||||
try {
|
||||
$pdoTpl->prepare("DELETE FROM emailtemplate_template_items WHERE template_id=:id AND customer_id=:cid")->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$pdoTpl->prepare("DELETE FROM emailtemplate_templates WHERE id=:id AND customer_id=:cid")->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$pdoTpl->commit();
|
||||
json_out(['ok'=>true]);
|
||||
} catch (Throwable $e) { $pdoTpl->rollBack(); throw $e; }
|
||||
}
|
||||
|
||||
/* ------------------------- Sections ------------------------- */
|
||||
if ($action === 'sections.list') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$templateId = isset($_GET['template_id']) ? (int)$_GET['template_id'] : 0;
|
||||
if ($templateId>0) {
|
||||
$st = $pdoTpl->prepare("SELECT id, template_id, name, z_index, type, updated_at
|
||||
FROM emailtemplate_sections
|
||||
WHERE customer_id=:cid AND template_id=:tid
|
||||
ORDER BY z_index ASC, id ASC
|
||||
LIMIT 1000");
|
||||
$st->execute([':cid'=>$customerId, ':tid'=>$templateId]);
|
||||
} else {
|
||||
$st = $pdoTpl->prepare("SELECT id, template_id, name, z_index, type, updated_at
|
||||
FROM emailtemplate_sections
|
||||
WHERE customer_id=:cid
|
||||
ORDER BY template_id ASC, z_index ASC, id ASC
|
||||
LIMIT 1000");
|
||||
$st->execute([':cid'=>$customerId]);
|
||||
}
|
||||
json_out(['ok'=>true,'items'=>$st->fetchAll()]);
|
||||
}
|
||||
|
||||
if ($action === 'sections.get') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : (int)($_POST['id'] ?? 0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$st = $pdoTpl->prepare("SELECT id, customer_id, template_id, name, z_index, type, updated_at
|
||||
FROM emailtemplate_sections
|
||||
WHERE id=:id AND customer_id=:cid
|
||||
LIMIT 1");
|
||||
$st->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$row = $st->fetch();
|
||||
if (!$row) json_out(['ok'=>false,'error'=>'not_found'], 404);
|
||||
json_out(['ok'=>true,'item'=>$row]);
|
||||
}
|
||||
|
||||
/* ------------------------- Blocks ------------------------- */
|
||||
if ($action === 'blocks.list') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$st = $pdoTpl->prepare("SELECT id, name, category, updated_at
|
||||
FROM emailtemplate_blocks
|
||||
WHERE customer_id=:cid
|
||||
ORDER BY name ASC
|
||||
LIMIT 1000");
|
||||
$st->execute([':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'items'=>$st->fetchAll()]);
|
||||
}
|
||||
|
||||
if ($action === 'blocks.get') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : (int)($_POST['id'] ?? 0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$st = $pdoTpl->prepare("SELECT id, customer_id, name, category, updated_at
|
||||
FROM emailtemplate_blocks
|
||||
WHERE id=:id AND customer_id=:cid
|
||||
LIMIT 1");
|
||||
$st->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$row = $st->fetch();
|
||||
if (!$row) json_out(['ok'=>false,'error'=>'not_found'], 404);
|
||||
json_out(['ok'=>true,'item'=>$row]);
|
||||
}
|
||||
|
||||
if ($action === 'blocks.create') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in=in_json(); $name=trim((string)($in['name']??'')); $cat=trim((string)($in['category']??''));
|
||||
if ($name==='') json_out(['ok'=>false,'error'=>'name_required'], 400);
|
||||
$st=$pdoTpl->prepare("INSERT INTO emailtemplate_blocks (customer_id,name,category,created_at,updated_at)
|
||||
VALUES (:cid,:name,COALESCE(NULLIF(:cat,''),'Default'),NOW(),NOW())");
|
||||
$st->execute([':cid'=>$customerId, ':name'=>$name, ':cat'=>$cat]);
|
||||
json_out(['ok'=>true,'id'=>(int)$pdoTpl->lastInsertId()]);
|
||||
}
|
||||
|
||||
if ($action === 'blocks.update') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in=in_json(); $id=(int)($in['id']??0); $name=trim((string)($in['name']??'')); $cat=trim((string)($in['category']??''));
|
||||
if ($id<=0 || $name==='') json_out(['ok'=>false,'error'=>'invalid_params'], 400);
|
||||
$st=$pdoTpl->prepare("UPDATE emailtemplate_blocks
|
||||
SET name=:name, category=COALESCE(NULLIF(:cat,''),category), updated_at=NOW()
|
||||
WHERE id=:id AND customer_id=:cid");
|
||||
$st->execute([':name'=>$name, ':cat'=>$cat, ':id'=>$id, ':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'updated'=>$st->rowCount()]);
|
||||
}
|
||||
|
||||
if ($action === 'blocks.delete') {
|
||||
require_role($CFG, ['owner','admin']);
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in=in_json(); $id=(int)($in['id']??0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$st=$pdoTpl->prepare("DELETE FROM emailtemplate_blocks WHERE id=:id AND customer_id=:cid");
|
||||
$st->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'deleted'=>$st->rowCount()]);
|
||||
}
|
||||
|
||||
/* ------------------------- Snippets ------------------------- */
|
||||
if ($action === 'snippets.list') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$st=$pdoTpl->prepare("SELECT id, name, category, updated_at
|
||||
FROM emailtemplate_snippets
|
||||
WHERE customer_id=:cid
|
||||
ORDER BY name ASC
|
||||
LIMIT 1000");
|
||||
$st->execute([':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'items'=>$st->fetchAll()]);
|
||||
}
|
||||
|
||||
if ($action === 'snippets.get') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$id = isset($_GET['id']) ? (int)$_GET['id'] : (int)($_POST['id'] ?? 0);
|
||||
if ($id<=0) json_out(['ok'=>false,'error'=>'missing_id'], 400);
|
||||
$st=$pdoTpl->prepare("SELECT id, customer_id, name, category, updated_at
|
||||
FROM emailtemplate_snippets
|
||||
WHERE id=:id AND customer_id=:cid
|
||||
LIMIT 1");
|
||||
$st->execute([':id'=>$id, ':cid'=>$customerId]);
|
||||
$row=$st->fetch();
|
||||
if (!$row) json_out(['ok'=>false,'error'=>'not_found'], 404);
|
||||
json_out(['ok'=>true,'item'=>$row]);
|
||||
}
|
||||
|
||||
/* ------------------------- Assets (READ) ------------------------- */
|
||||
if ($action === 'assets.list') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$st=$pdoTpl->prepare("SELECT id, name, type, mime_type, size_bytes, public_url, updated_at
|
||||
FROM emailtemplate_assets
|
||||
WHERE customer_id=:cid
|
||||
ORDER BY updated_at DESC, id DESC
|
||||
LIMIT 200");
|
||||
$st->execute([':cid'=>$customerId]);
|
||||
json_out(['ok'=>true,'items'=>$st->fetchAll()]);
|
||||
}
|
||||
|
||||
/* ------------------------- Editor-Referenzen (Placeholders) ------------------------- */
|
||||
if ($action === 'template_items.sync') { json_out(['ok'=>true]); }
|
||||
if ($action === 'section_items.sync') { json_out(['ok'=>true]); }
|
||||
|
||||
/* ------------------------- Render (Fallback) ------------------------- */
|
||||
if ($action === 'render.preview') {
|
||||
if (!$pdoTpl) json_out(['ok'=>false,'error'=>'templates_db_not_configured'], 500);
|
||||
$in = in_json();
|
||||
$templateId = (int)($in['template_id'] ?? 0);
|
||||
if ($templateId<=0) json_out(['ok'=>false,'error'=>'template_id_required'], 400);
|
||||
$st = $pdoTpl->prepare("SELECT id, name FROM emailtemplate_templates WHERE id=:id AND customer_id=:cid LIMIT 1");
|
||||
$st->execute([':id'=>$templateId, ':cid'=>$customerId]);
|
||||
$tpl = $st->fetch();
|
||||
if (!$tpl) json_out(['ok'=>false,'error'=>'not_found'], 404);
|
||||
$html = "<!-- preview {$tpl['name']} (#{$tpl['id']}) -->\n<div style=\"padding:16px;font:14px/1.4 system-ui\">Preview okay.</div>";
|
||||
json_out(['ok'=>true, 'template'=>$tpl, 'html'=>$html]);
|
||||
}
|
||||
|
||||
/* ------------------------- Fallback ------------------------- */
|
||||
json_out(['ok'=>false,'error'=>'unknown_action','action'=>$action], 404);
|
||||
|
||||
Reference in New Issue
Block a user