diff --git a/api.php b/api.php new file mode 100644 index 0000000..bbfb28a --- /dev/null +++ b/api.php @@ -0,0 +1,238 @@ + '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); +}