false,'error'=>$msg,'detail'=>$detail], $code); } // RESTLICHE HELFER (MÜSSEN KEINE GLOBALS VERWENDEN) function load_config(): array { $paths = [ __DIR__ . '/config.php', __DIR__ . '/../config.php', __DIR__ . '/../../config.php', ]; foreach ($paths as $p) { if (is_file($p)) { $conf = @include $p; if (is_array($conf)) return $conf; } } fail('Invalid config.php', 'config.php not found or not returning array', 500); } function cors(array $conf): void { $cors = $conf['cors'] ?? '*'; if ($cors) { header('Access-Control-Allow-Origin: ' . $cors); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization'); header('Access-Control-Allow-Credentials: true'); } if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'OPTIONS') respond(['ok'=>true]); } function get_input(): array { $data = []; $ct = $_SERVER['CONTENT_TYPE'] ?? ''; if (stripos($ct, 'application/json') !== false) { $raw = file_get_contents('php://input'); if ($raw !== false && $raw !== '') { $js = json_decode($raw, true); if (is_array($js)) $data = $js; } } foreach ($_POST as $k=>$v) $data[$k]=$v; foreach ($_GET as $k=>$v) if (!array_key_exists($k,$data)) $data[$k]=$v; return $data; } function val(array $in, $keys, $default=null) { if (!is_array($keys)) $keys = [$keys]; foreach ($keys as $k) if (array_key_exists($k,$in)) return $in[$k]; return $default; } function first_existing(array $columns, array $candidates): ?string { foreach ($candidates as $c) if (in_array($c, $columns, true)) return $c; return null; } function pdo_templates(array $conf): PDO { if (!isset($conf['templates']) || !is_array($conf['templates'])) { fail('Missing templates DB config', null, 500); } $c = $conf['templates']; $host = $c['db_host'] ?? 'localhost'; $db = $c['db_name'] ?? ($c['database'] ?? ''); $user = $c['db_user'] ?? ($c['username'] ?? ''); $pass = $c['db_pass'] ?? ($c['password'] ?? ''); $charset = $c['db_charset'] ?? 'utf8mb4'; $port = $c['db_port'] ?? 3306; $dsn = "mysql:host=$host;port=$port;dbname=$db;charset=$charset"; $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]; return new PDO($dsn, $user, $pass, $opt); } function verify_password(string $input, string $stored, array $authDbConf): bool { if (preg_match('~^\$2[aby]\$~', $stored) || strpos($stored, '$argon2') === 0) return password_verify($input, $stored); $legacy = strtolower($authDbConf['legacy'] ?? ''); if ($legacy === 'md5') return hash_equals($stored, md5($input)); if ($legacy === 'sha1') return hash_equals($stored, sha1($input)); if (password_get_info($stored)['algo'] !== 0) return password_verify($input, $stored); return hash_equals($stored, $input); } function table_columns(PDO $pdo, string $table): array { $cols = []; $stmt = $pdo->query("SHOW COLUMNS FROM `$table`"); foreach ($stmt->fetchAll() as $r) $cols[] = $r['Field']; return $cols; } function primary_key(PDO $pdo, string $table): ?string { $stmt = $pdo->prepare("SHOW KEYS FROM `$table` WHERE Key_name = 'PRIMARY'"); $stmt->execute(); $row = $stmt->fetch(); return $row['Column_name'] ?? null; } // --- Neue, reguläre Funktionen (ersetzen Closures) --- function requireAuth(): array { // Muss auf globale $_SESSION zugreifen if (empty($_SESSION['auth'])) fail('Not authenticated', null, 401); return $_SESSION['auth']; } function pullId(array $src) { $aliases = ['id','item_id','template_id','tpl_id','section_id','sec_id','block_id','blk_id','snippet_id','snip_id']; foreach ($aliases as $a) if (isset($src[$a]) && $src[$a] !== '') return $src[$a]; return null; } function tenantWhere(array $session): array { // Muss auf globale $conf zugreifen, um $tenantCol und $mapSess zu erhalten global $conf; $multi = $conf['multi'] ?? []; $tenantCol = $multi['tenant_col'] ?? null; $mapSess = $multi['map_session_to'] ?? 'id'; if (!$tenantCol) return ['', []]; if (!$session) return [' AND 1=0 ', []]; $val = $session[$mapSess] ?? null; if ($val===null || $val==='') return [' AND 1=0 ', []]; return [" AND `$tenantCol` = :__tenant", [':__tenant'=>$val]]; } function tenantAssign(array $session, array $columns): array { // Muss auf globale $conf zugreifen global $conf; $multi = $conf['multi'] ?? []; $tenantCol = $multi['tenant_col'] ?? null; $mapSess = $multi['map_session_to'] ?? 'id'; if (!$tenantCol || !in_array($tenantCol, $columns, true)) return []; $val = $session[$mapSess] ?? null; return ($val===null || $val==='') ? [] : [$tenantCol => $val]; } function resolveIdCol(string $kind): array { // Muss auf globale $conf, $pdo, und $tableMap zugreifen global $conf, $pdo, $tableMap; $t = $tableMap[$kind]; $cfg = $conf['columns'][$kind] ?? []; $cols = table_columns($pdo, $t); $idCol = $cfg['id'] ?? (in_array('id', $cols, true) ? 'id' : primary_key($pdo, $t)); if (!$idCol) $idCol = 'id'; return [$idCol, $cols]; } // --- Haupt-Setup-Logik (Setzt die globalen Variablen) --- try { // Deklariere alle Variablen, die im Router von api.php benötigt werden, als global global $conf, $pdo, $in, $action, $tableMap; // 1. Globale Konfiguration und CORS $conf = load_config(); cors($conf); // 2. Cookie-Parameter setzen if (!empty($conf['auth']['cookie'])) { $c = $conf['auth']['cookie']; $params = session_get_cookie_params(); $params['lifetime'] = $c['lifetime'] ?? $params['lifetime']; $params['path'] = $c['path'] ?? $params['path']; $params['domain'] = $c['domain'] ?? $params['domain']; $params['secure'] = $c['secure'] ?? $params['secure']; $params['httponly'] = $c['httponly'] ?? $params['httponly']; if (isset($c['samesite'])) $params['samesite'] = $c['samesite']; session_set_cookie_params($params); } // 3. Input-Daten abrufen $in = get_input(); // 4. Datenbankverbindung herstellen $pdo = pdo_templates($conf); // 5. Action / Resource auflösen $action = val($in, 'action', ''); $resource = val($in, 'resource', null); $allowedResources = ['templates','sections','blocks','snippets']; if ($resource && in_array($resource, $allowedResources, true) && strpos((string)$action, '.') === false) { $verb = strtolower((string)$action); if (in_array($verb, ['list','get','create','update','delete'], true)) $action = $resource.'.'.$verb; } // 6. Tabellenzuweisungen $tables = $conf['tables'] ?? []; $tableMap = [ 'templates' => $tables['templates'] ?? 'emailtemplate_templates', 'sections' => $tables['sections'] ?? 'emailtemplate_sections', 'blocks' => $tables['blocks'] ?? 'emailtemplate_blocks', 'snippets' => $tables['snippets'] ?? 'emailtemplate_snippets', ]; } catch (Throwable $e) { // Fehler während der Initialisierung abfangen fail('Initialization error', get_class($e).': '.$e->getMessage(), 500); }