Files
turborfq/public/index.php
Michael Chus 23c9c04a87 perf: оптимизировано массовое удаление строк
- Добавлен batch delete метод на бэкенде (удаление множества строк за один запрос)
- Использование WHERE IN для удаления нескольких строк одним SQL запросом
- Добавлен прогресс-бар при удалении большого количества строк
- Удаление 1000 строк теперь занимает секунды вместо минут
- Добавлена поддержка транзакций для атомарности операций
- Оптимизирован размер батчей для баланса производительности и надежности
2026-01-21 04:17:26 +03:00

277 lines
10 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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();