- Добавлен batch delete метод на бэкенде (удаление множества строк за один запрос) - Использование WHERE IN для удаления нескольких строк одним SQL запросом - Добавлен прогресс-бар при удалении большого количества строк - Удаление 1000 строк теперь занимает секунды вместо минут - Добавлена поддержка транзакций для атомарности операций - Оптимизирован размер батчей для баланса производительности и надежности
277 lines
10 KiB
PHP
277 lines
10 KiB
PHP
<?php
|
||
use Psr\Http\Message\ResponseInterface as Response;
|
||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||
use Slim\Factory\AppFactory;
|
||
use DI\Container;
|
||
|
||
require __DIR__ . '/../vendor/autoload.php';
|
||
|
||
session_start();
|
||
|
||
// DI‑контейнер
|
||
$container = new Container();
|
||
AppFactory::setContainer($container);
|
||
$app = AppFactory::create();
|
||
|
||
// В dev включаем подробные ошибки (можно выключить в проде)
|
||
$app->addErrorMiddleware(true, true, true);
|
||
|
||
$container->set('db', function () {
|
||
return \App\Db::connectFromSession();
|
||
});
|
||
|
||
// --- ROUTES ---
|
||
|
||
// Статическая страница с фронтендом
|
||
$app->get('/', function (Request $request, Response $response) {
|
||
// Очень простой view встроен прямо здесь
|
||
$html = file_get_contents(__DIR__ . '/index.html'); // вынесем в отдельный файл ниже
|
||
$response->getBody()->write($html);
|
||
return $response;
|
||
});
|
||
|
||
// Логин: сохраняет логин/пароль MariaDB в сессии после проверки
|
||
$app->post('/api/login', function (Request $request, Response $response) {
|
||
$data = json_decode((string)$request->getBody(), true);
|
||
$user = $data['user'] ?? '';
|
||
$pass = $data['pass'] ?? '';
|
||
|
||
try {
|
||
\App\Db::testConnection($user, $pass);
|
||
$_SESSION['db_user'] = $user;
|
||
$_SESSION['db_pass'] = $pass;
|
||
|
||
$payload = ['ok' => true];
|
||
$status = 200;
|
||
} catch (\RuntimeException $e) {
|
||
$payload = ['ok' => false, 'error' => $e->getMessage()];
|
||
$status = 401;
|
||
}
|
||
|
||
$response->getBody()->write(json_encode($payload));
|
||
return $response->withHeader('Content-Type', 'application/json')
|
||
->withStatus($status);
|
||
});
|
||
|
||
// Middleware на /api/*: проверяем, что аутентифицированы
|
||
$app->add(function (Request $request, $handler) {
|
||
$path = $request->getUri()->getPath();
|
||
if (str_starts_with($path, '/api/') && $path !== '/api/login') {
|
||
if (empty($_SESSION['db_user']) || empty($_SESSION['db_pass'])) {
|
||
$res = new \Slim\Psr7\Response();
|
||
$res->getBody()->write(json_encode(['error' => 'Not authenticated']));
|
||
return $res->withStatus(401)->withHeader('Content-Type', 'application/json');
|
||
}
|
||
}
|
||
return $handler->handle($request);
|
||
});
|
||
|
||
// API: дерево схем/таблиц
|
||
$app->get('/api/tree', function (Request $request, Response $response) use ($container) {
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$tree = $meta->getSchemaTree();
|
||
|
||
$response->getBody()->write(json_encode($tree));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
// API: метаданные таблицы
|
||
$app->get('/api/table/meta', function (Request $request, Response $response) use ($container) {
|
||
$params = $request->getQueryParams();
|
||
$schema = $params['schema'] ?? '';
|
||
$table = $params['table'] ?? '';
|
||
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$data = $meta->getTableMeta($schema, $table);
|
||
|
||
$response->getBody()->write(json_encode($data));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
// API: данные таблицы
|
||
$app->post('/api/table/data', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
|
||
$schema = $payload['schema'] ?? '';
|
||
$table = $payload['table'] ?? '';
|
||
$page = (int)($payload['page'] ?? 1);
|
||
$pageSize = (int)($payload['pageSize'] ?? 50);
|
||
$filters = $payload['filters'] ?? [];
|
||
$sort = $payload['sort'] ?? null;
|
||
$columns = $payload['columns'] ?? [];
|
||
|
||
// ✅ Логирование для отладки
|
||
error_log("Data request: page=$page, pageSize=$pageSize, filters=" . json_encode($filters));
|
||
|
||
$pdo = $container->get('db');
|
||
$ds = new \App\DataService($pdo);
|
||
$resData = $ds->fetchData($schema, $table, $columns, $page, $pageSize, $filters, $sort);
|
||
|
||
// ✅ Логирование ответа
|
||
error_log("Data response: " . json_encode([
|
||
'data_count' => count($resData['data']),
|
||
'last_page' => $resData['last_page'],
|
||
'current_page' => $resData['current_page'],
|
||
'total' => $resData['total']
|
||
]));
|
||
|
||
$response->getBody()->write(json_encode($resData));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
// API: insert / update / delete
|
||
$app->post('/api/table/insert', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'];
|
||
$table = $payload['table'];
|
||
$row = $payload['row'];
|
||
$metaArr = $meta->getTableMeta($schema, $table);
|
||
|
||
$result = $ds->insertRow($schema, $table, $row, $metaArr['columns']);
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
$app->post('/api/table/update', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'];
|
||
$table = $payload['table'];
|
||
$row = $payload['row'];
|
||
$metaArr = $meta->getTableMeta($schema, $table);
|
||
|
||
$result = $ds->updateRow($schema, $table, $row, $metaArr['columns'], $metaArr['primaryKey']);
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
$app->post('/api/table/delete', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'];
|
||
$table = $payload['table'];
|
||
$row = $payload['row'];
|
||
$metaArr = $meta->getTableMeta($schema, $table);
|
||
|
||
$result = $ds->deleteRow($schema, $table, $row, $metaArr['primaryKey']);
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
// API: импорт CSV (массовая вставка)
|
||
// API: импорт CSV (массовая вставка)
|
||
$app->post('/api/table/import-csv', function (Request $request, Response $response) use ($container) {
|
||
$body = (string)$request->getBody();
|
||
$payload = json_decode($body, true);
|
||
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
error_log("CSV Import: Invalid JSON - " . json_last_error_msg());
|
||
$response->getBody()->write(json_encode(['error' => 'Invalid JSON: ' . json_last_error_msg()]));
|
||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||
}
|
||
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'] ?? '';
|
||
$table = $payload['table'] ?? '';
|
||
$rows = $payload['rows'] ?? [];
|
||
|
||
if (empty($schema) || empty($table)) {
|
||
error_log("CSV Import: Missing schema or table");
|
||
$response->getBody()->write(json_encode(['error' => 'Schema and table are required']));
|
||
return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
|
||
}
|
||
|
||
try {
|
||
$metaArr = $meta->getTableMeta($schema, $table);
|
||
$result = $ds->insertMultipleRows($schema, $table, $rows, $metaArr['columns']);
|
||
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
} catch (\Exception $e) {
|
||
error_log("CSV Import: Critical error - " . $e->getMessage());
|
||
|
||
$response->getBody()->write(json_encode([
|
||
'error' => $e->getMessage(),
|
||
'inserted' => 0,
|
||
'errors' => 1
|
||
]));
|
||
return $response->withHeader('Content-Type', 'application/json')->withStatus(500);
|
||
}
|
||
});
|
||
|
||
|
||
// API: экспорт CSV
|
||
$app->post('/api/table/export-csv', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
$pdo = $container->get('db');
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'] ?? '';
|
||
$table = $payload['table'] ?? '';
|
||
$filters = $payload['filters'] ?? [];
|
||
$sort = $payload['sort'] ?? null;
|
||
$columns = $payload['columns'] ?? [];
|
||
|
||
$result = $ds->exportCSV($schema, $table, $columns, $filters, $sort);
|
||
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
// API: получить доступные значения для Foreign Key
|
||
$app->get('/api/fk-values', function (Request $request, Response $response) use ($container) {
|
||
$params = $request->getQueryParams();
|
||
$schema = $params['schema'] ?? '';
|
||
$table = $params['table'] ?? '';
|
||
$column = $params['column'] ?? '';
|
||
|
||
$pdo = $container->get('db');
|
||
|
||
// Получаем до 100 первых значений
|
||
$sql = "SELECT DISTINCT `{$column}` FROM `{$schema}`.`{$table}`
|
||
WHERE `{$column}` IS NOT NULL
|
||
ORDER BY `{$column}`
|
||
LIMIT 100";
|
||
|
||
$stmt = $pdo->prepare($sql);
|
||
$stmt->execute();
|
||
$values = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||
|
||
$response->getBody()->write(json_encode(['values' => $values]));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
// API: массовое удаление строк (batch delete)
|
||
$app->post('/api/table/delete-batch', function (Request $request, Response $response) use ($container) {
|
||
$payload = json_decode((string)$request->getBody(), true);
|
||
$pdo = $container->get('db');
|
||
$meta = new \App\MetaService($pdo);
|
||
$ds = new \App\DataService($pdo);
|
||
|
||
$schema = $payload['schema'];
|
||
$table = $payload['table'];
|
||
$rows = $payload['rows'] ?? []; // Массив строк для удаления
|
||
$metaArr = $meta->getTableMeta($schema, $table);
|
||
|
||
$result = $ds->deleteMultipleRows($schema, $table, $rows, $metaArr['primaryKey']);
|
||
$response->getBody()->write(json_encode($result));
|
||
return $response->withHeader('Content-Type', 'application/json');
|
||
});
|
||
|
||
|
||
$app->run();
|