'Não autorizado. Forneça o header X-API-Key.']); exit; } // ---- Conexão PDO ---- try { $pdo = new PDO( "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";port=" . DB_PORT . ";charset=utf8mb4", DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" ] ); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Falha na conexão com o banco de dados: ' . $e->getMessage()]); exit; } // ---- Helpers ---- function sanitizeIdentifier(string $name): string { return preg_replace('/[^a-zA-Z0-9_]/', '', $name); } function getPrimaryKey(PDO $pdo, string $table): string { $stmt = $pdo->query("SHOW KEYS FROM `{$table}` WHERE Key_name = 'PRIMARY'"); $row = $stmt->fetch(); return $row ? $row['Column_name'] : 'id'; } function jsonResponse(mixed $data, int $status = 200): void { http_response_code($status); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } // ---- Roteamento ---- $method = $_SERVER['REQUEST_METHOD']; $action = $_GET['action'] ?? null; $table = isset($_GET['table']) ? sanitizeIdentifier($_GET['table']) : null; $id = $_GET['id'] ?? null; // ACTION: Listar todas as tabelas if ($action === 'tables') { $stmt = $pdo->query("SHOW TABLES"); jsonResponse($stmt->fetchAll(PDO::FETCH_COLUMN)); } // ACTION: Schema de uma tabela (colunas + FKs) if ($action === 'schema' && $table) { $stmt = $pdo->query("DESCRIBE `{$table}`"); $columns = $stmt->fetchAll(); $fkStmt = $pdo->prepare(" SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL "); $fkStmt->execute([$table]); $fks = $fkStmt->fetchAll(); // Para cada FK, detecta a melhor coluna de exibição $fkDisplay = []; foreach ($fks as $fk) { $refTable = $fk['REFERENCED_TABLE_NAME']; $descStmt = $pdo->query("DESCRIBE `{$refTable}`"); $refCols = $descStmt->fetchAll(); $displayCol = null; foreach ($refCols as $col) { if ($col['Field'] !== $fk['REFERENCED_COLUMN_NAME'] && (strpos($col['Type'], 'varchar') !== false || strpos($col['Type'], 'text') !== false)) { $displayCol = $col['Field']; break; } } $fkDisplay[$fk['COLUMN_NAME']] = [ 'table' => $refTable, 'keyCol' => $fk['REFERENCED_COLUMN_NAME'], 'displayCol' => $displayCol ?? $fk['REFERENCED_COLUMN_NAME'], ]; } jsonResponse(['columns' => $columns, 'foreignKeys' => $fks, 'fkDisplay' => $fkDisplay]); } // ACTION: Opções para campo FK (select dropdown) if ($action === 'fk_options' && $table) { $pkCol = sanitizeIdentifier($_GET['pk'] ?? 'id'); $labelCol = sanitizeIdentifier($_GET['label'] ?? $pkCol); $stmt = $pdo->query( "SELECT `{$pkCol}` AS `value`, `{$labelCol}` AS `label` FROM `{$table}` ORDER BY `{$labelCol}` LIMIT 500" ); jsonResponse($stmt->fetchAll()); } // ---- CRUD ---- if (!$table) { jsonResponse(['error' => 'Parâmetro "table" obrigatório'], 400); } $pk = getPrimaryKey($pdo, $table); switch ($method) { // READ (listagem paginada ou registro único) case 'GET': if ($id !== null) { $stmt = $pdo->prepare("SELECT * FROM `{$table}` WHERE `{$pk}` = ?"); $stmt->execute([$id]); $row = $stmt->fetch(); jsonResponse($row !== false ? $row : null); } $page = max(1, intval($_GET['page'] ?? 1)); $limit = min(100, max(5, intval($_GET['limit'] ?? 10))); $offset = ($page - 1) * $limit; $search = trim($_GET['search'] ?? ''); // Busca simples em todas as colunas varchar $whereClause = ''; $searchParams = []; if ($search !== '') { $descStmt = $pdo->query("DESCRIBE `{$table}`"); $cols = $descStmt->fetchAll(PDO::FETCH_COLUMN, 0); $conditions = []; foreach ($cols as $col) { $conditions[] = "`{$col}` LIKE ?"; $searchParams[] = "%{$search}%"; } $whereClause = 'WHERE ' . implode(' OR ', $conditions); } $countStmt = $pdo->query("SELECT COUNT(*) FROM `{$table}` {$whereClause}"); if ($searchParams) { $countStmt = $pdo->prepare("SELECT COUNT(*) FROM `{$table}` {$whereClause}"); $countStmt->execute($searchParams); } $total = (int) $countStmt->fetchColumn(); $dataStmt = $pdo->prepare("SELECT * FROM `{$table}` {$whereClause} LIMIT ? OFFSET ?"); $params = array_merge($searchParams, [$limit, $offset]); $dataStmt->execute($params); $data = $dataStmt->fetchAll(); jsonResponse([ 'data' => $data, 'total' => $total, 'page' => $page, 'limit' => $limit, 'totalPages' => (int) ceil($total / $limit), ]); break; // CREATE case 'POST': $body = json_decode(file_get_contents('php://input'), true); if (!$body || !is_array($body)) { jsonResponse(['error' => 'Body JSON inválido'], 400); } $cols = array_keys($body); $placeholders = implode(', ', array_fill(0, count($cols), '?')); $colNames = '`' . implode('`, `', $cols) . '`'; $stmt = $pdo->prepare("INSERT INTO `{$table}` ({$colNames}) VALUES ({$placeholders})"); $stmt->execute(array_values($body)); jsonResponse(['id' => $pdo->lastInsertId(), 'message' => 'Registro criado com sucesso!']); break; // UPDATE case 'PUT': if ($id === null) jsonResponse(['error' => 'ID obrigatório para atualização'], 400); $body = json_decode(file_get_contents('php://input'), true); if (!$body || !is_array($body)) { jsonResponse(['error' => 'Body JSON inválido'], 400); } $sets = implode(', ', array_map(fn($col) => "`{$col}` = ?", array_keys($body))); $stmt = $pdo->prepare("UPDATE `{$table}` SET {$sets} WHERE `{$pk}` = ?"); $stmt->execute([...array_values($body), $id]); jsonResponse(['message' => 'Registro atualizado com sucesso!']); break; // DELETE case 'DELETE': if ($id === null) jsonResponse(['error' => 'ID obrigatório para exclusão'], 400); $stmt = $pdo->prepare("DELETE FROM `{$table}` WHERE `{$pk}` = ?"); $stmt->execute([$id]); jsonResponse(['message' => 'Registro excluído com sucesso!']); break; default: jsonResponse(['error' => 'Método não permitido'], 405); }