diff --git a/README.md b/README.md index 8572786..d45101f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # iiko-connector +* `Числовые` → `Агрегация` +* `Категории` → `Группировка {ROW / COLUMN}` +* `Фильтры` → `Фильтрация` diff --git a/build.gradle.kts b/build.gradle.kts index 7a5b887..a07f5dc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,17 +48,26 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind") - // https://mvnrepository.com/artifact/org.mindrot/jbcrypt + // Source: https://mvnrepository.com/artifact/org.mindrot/jbcrypt implementation("org.mindrot:jbcrypt:0.4") - // https://mvnrepository.com/artifact/org.slf4j/slf4j-api + // Source: https://mvnrepository.com/artifact/org.slf4j/slf4j-api implementation("org.slf4j:slf4j-api:2.0.17") - // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl + // Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.25.4") - // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core + // Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core implementation("org.apache.logging.log4j:log4j-core:2.25.4") - // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api + // Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api implementation("org.apache.logging.log4j:log4j-api:2.25.4") + implementation("io.vertx:vertx-jdbc-client") + + // Source: https://mvnrepository.com/artifact/com.clickhouse/clickhouse-jdbc + implementation("com.clickhouse:clickhouse-jdbc:0.9.8") + // Source: https://mvnrepository.com/artifact/com.mysql/mysql-connector-j + implementation("com.mysql:mysql-connector-j:9.7.0") + // Source: https://mvnrepository.com/artifact/org.postgresql/postgresql + implementation("org.postgresql:postgresql:42.7.11") + } java { diff --git a/frontend/src/components/Layout/AppLayout.vue b/frontend/src/components/Layout/AppLayout.vue index e66c7e3..b7e8310 100644 --- a/frontend/src/components/Layout/AppLayout.vue +++ b/frontend/src/components/Layout/AppLayout.vue @@ -93,6 +93,22 @@ {{ t('app.olapColumns') }} + + + + + {{ t('dbConnections.pageName') }} + + {{ t('app.settings') }} - - - - - - - - - - {{ t('app.database') }} - @@ -193,6 +185,17 @@ + + + + + diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 80a40d0..329c8b5 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -234,5 +234,31 @@ "deleteError": "Error deleting field", "deleteField": "Delete Field", "deleteFieldConfirm": "Are you sure you want to delete this field? This action cannot be undone." + }, + "dbConnections": { + "pageName": "Databases", + "add": "Add Connection", + "edit": "Edit Connection", + "delete": "Delete Connection", + "deleteConfirmation": "Are you sure you want to delete this database connection? This action cannot be undone.", + "type": "Type", + "host": "Host", + "port": "Port", + "database": "Database", + "user": "User", + "test": "Test connection", + "noConnections": "No database connections found. Click 'Add Connection' to create one.", + "loadError": "Failed to load database connections.", + "testSuccess": "Connection successful! Latency: {latency} ms", + "testError": "Connection failed: {error}", + "testNetworkError": "Network error while testing connection: {error}", + "testUnknownError": "Unknown error", + "passwordRequired": "Password is required for new connection.", + "createSuccess": "Database connection created successfully.", + "updateSuccess": "Database connection updated successfully.", + "createError": "Failed to create database connection.", + "updateError": "Failed to update database connection.", + "deleteSuccess": "Database connection deleted successfully.", + "deleteError": "Failed to delete database connection." } } diff --git a/frontend/src/locales/ru.json b/frontend/src/locales/ru.json index 832d2fb..d877395 100644 --- a/frontend/src/locales/ru.json +++ b/frontend/src/locales/ru.json @@ -234,5 +234,31 @@ "deleteError": "Ошибка при удалении поля", "deleteField": "Удаление поля", "deleteFieldConfirm": "Вы уверены, что хотите удалить это поле? Это действие необратимо." + }, + "dbConnections": { + "pageName": "Базы данных", + "add": "Добавить подключение", + "edit": "Редактировать подключение", + "delete": "Удалить подключение", + "deleteConfirmation": "Вы уверены, что хотите удалить это подключение к базе данных? Действие необратимо.", + "type": "Тип", + "host": "Хост", + "port": "Порт", + "database": "База данных", + "user": "Пользователь", + "test": "Проверить подключение", + "noConnections": "Подключения к базам данных не найдены. Нажмите «Добавить подключение», чтобы создать.", + "loadError": "Не удалось загрузить список подключений.", + "testSuccess": "Подключение успешно! Задержка: {latency} мс", + "testError": "Ошибка подключения: {error}", + "testNetworkError": "Сетевая ошибка при проверке подключения: {error}", + "testUnknownError": "Неизвестная ошибка", + "passwordRequired": "Пароль обязателен для нового подключения.", + "createSuccess": "Подключение к БД успешно создано.", + "updateSuccess": "Подключение к БД успешно обновлено.", + "createError": "Не удалось создать подключение к БД.", + "updateError": "Не удалось обновить подключение к БД.", + "deleteSuccess": "Подключение к БД успешно удалено.", + "deleteError": "Не удалось удалить подключение к БД." } } diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 2c623db..751503f 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -8,6 +8,7 @@ import Dashboard from '@/views/Dashboard.vue' import Users from '@/views/Users.vue' import Restaurants from '@/views/Restaurants.vue' import OlapColumnsView from '@/views/OlapColumnsView.vue' +import DBConnections from '@/views/DBConnections.vue' import AdminSettings from '@/views/AdminSettings.vue' import Profile from '@/views/Profile.vue' import NotFound from '@/views/NotFound.vue' @@ -52,6 +53,11 @@ const routes = [ component: OlapColumnsView, meta: { requiresAuth: true, requiresAdmin: true, title: 'OlapColumns' } }, + { + path: '/database-connections', + component: DBConnections, + meta: { requiresAuth: true, requiresAdmin: true, title: 'Database Connections' } + }, { path: '/settings', component: AdminSettings, diff --git a/frontend/src/views/DBConnections.vue b/frontend/src/views/DBConnections.vue new file mode 100644 index 0000000..a21583c --- /dev/null +++ b/frontend/src/views/DBConnections.vue @@ -0,0 +1,384 @@ + + + + {{ t('dbConnections.pageName') }} + + + + + {{ t('dbConnections.add') }} + + + + + + + + + {{ t('common.id') }} + {{ t('common.name') }} + {{ t('dbConnections.type') }} + {{ t('dbConnections.host') }} + {{ t('dbConnections.port') }} + {{ t('dbConnections.database') }} + {{ t('dbConnections.user') }} + {{ t('common.created') }} + {{ t('common.actions') }} + + + + + {{ conn.id }} + {{ conn.name }} + + + {{ getTypeLabel(conn.type) }} + + + {{ conn.host }} + {{ conn.port }} + {{ conn.database }} + {{ conn.user }} + {{ formatDate(conn.created) }} + + + + + + + + + + + + + + + + + + + + + + + {{ conn.testResult }} + + + + + + {{ t('dbConnections.noConnections') }} + + + + + + + + + + + + + + {{ modalTitle }} + + + + + + + + + {{ t('common.name') }} * + + + + {{ t('dbConnections.type') }} * + + MySQL + PostgreSQL + ClickHouse + + + + {{ t('dbConnections.host') }} * + + + + {{ t('dbConnections.port') }} * + + + + {{ t('dbConnections.database') }} * + + + + {{ t('dbConnections.user') }} * + + + + {{ t('common.password') }} + + {{ t('common.leavePasswordBlank') }} + + + {{ t('app.cancel') }} + {{ t('app.save') }} + + + + + + + + + + + + + + + + + + + + {{ t('dbConnections.delete') }} + {{ t('dbConnections.deleteConfirmation') }} + + {{ t('app.cancel') }} + {{ t('app.delete') }} + + + + + + + + + + + + diff --git a/libs/RoaringBitmap-1.0.6.jar b/libs/RoaringBitmap-1.0.6.jar new file mode 100644 index 0000000..5c5f60c Binary files /dev/null and b/libs/RoaringBitmap-1.0.6.jar differ diff --git a/libs/antlr4-runtime-4.13.2.jar b/libs/antlr4-runtime-4.13.2.jar new file mode 100644 index 0000000..350c1d0 Binary files /dev/null and b/libs/antlr4-runtime-4.13.2.jar differ diff --git a/libs/asm-9.7.jar b/libs/asm-9.7.jar new file mode 100644 index 0000000..fee9b02 Binary files /dev/null and b/libs/asm-9.7.jar differ diff --git a/libs/clickhouse-client-0.9.8.jar b/libs/clickhouse-client-0.9.8.jar new file mode 100644 index 0000000..8d4fa82 Binary files /dev/null and b/libs/clickhouse-client-0.9.8.jar differ diff --git a/libs/clickhouse-data-0.9.8.jar b/libs/clickhouse-data-0.9.8.jar new file mode 100644 index 0000000..7eb3472 Binary files /dev/null and b/libs/clickhouse-data-0.9.8.jar differ diff --git a/libs/clickhouse-http-client-0.9.8.jar b/libs/clickhouse-http-client-0.9.8.jar new file mode 100644 index 0000000..d6a725d Binary files /dev/null and b/libs/clickhouse-http-client-0.9.8.jar differ diff --git a/libs/clickhouse-jdbc-0.9.8.jar b/libs/clickhouse-jdbc-0.9.8.jar new file mode 100644 index 0000000..8f3d6c4 Binary files /dev/null and b/libs/clickhouse-jdbc-0.9.8.jar differ diff --git a/libs/client-v2-0.9.8.jar b/libs/client-v2-0.9.8.jar new file mode 100644 index 0000000..d8bee18 Binary files /dev/null and b/libs/client-v2-0.9.8.jar differ diff --git a/libs/commons-codec-1.19.0.jar b/libs/commons-codec-1.19.0.jar new file mode 100644 index 0000000..ff6441a Binary files /dev/null and b/libs/commons-codec-1.19.0.jar differ diff --git a/libs/commons-compress-1.28.0.jar b/libs/commons-compress-1.28.0.jar new file mode 100644 index 0000000..ff7e6aa Binary files /dev/null and b/libs/commons-compress-1.28.0.jar differ diff --git a/libs/commons-io-2.20.0.jar b/libs/commons-io-2.20.0.jar new file mode 100644 index 0000000..5e06db2 Binary files /dev/null and b/libs/commons-io-2.20.0.jar differ diff --git a/libs/commons-lang3-3.20.0.jar b/libs/commons-lang3-3.20.0.jar new file mode 100644 index 0000000..8682b86 Binary files /dev/null and b/libs/commons-lang3-3.20.0.jar differ diff --git a/libs/error_prone_annotations-2.36.0.jar b/libs/error_prone_annotations-2.36.0.jar new file mode 100644 index 0000000..740268b Binary files /dev/null and b/libs/error_prone_annotations-2.36.0.jar differ diff --git a/libs/failureaccess-1.0.3.jar b/libs/failureaccess-1.0.3.jar new file mode 100644 index 0000000..2834ba1 Binary files /dev/null and b/libs/failureaccess-1.0.3.jar differ diff --git a/libs/guava-33.4.6-jre.jar b/libs/guava-33.4.6-jre.jar new file mode 100644 index 0000000..5e74385 Binary files /dev/null and b/libs/guava-33.4.6-jre.jar differ diff --git a/libs/httpclient5-5.4.4.jar b/libs/httpclient5-5.4.4.jar new file mode 100644 index 0000000..15fbd3d Binary files /dev/null and b/libs/httpclient5-5.4.4.jar differ diff --git a/libs/httpcore5-5.3.4.jar b/libs/httpcore5-5.3.4.jar new file mode 100644 index 0000000..44dff04 Binary files /dev/null and b/libs/httpcore5-5.3.4.jar differ diff --git a/libs/httpcore5-h2-5.3.4.jar b/libs/httpcore5-h2-5.3.4.jar new file mode 100644 index 0000000..03507de Binary files /dev/null and b/libs/httpcore5-h2-5.3.4.jar differ diff --git a/libs/j2objc-annotations-3.0.0.jar b/libs/j2objc-annotations-3.0.0.jar new file mode 100644 index 0000000..c293336 Binary files /dev/null and b/libs/j2objc-annotations-3.0.0.jar differ diff --git a/libs/jdbc-v2-0.9.8.jar b/libs/jdbc-v2-0.9.8.jar new file mode 100644 index 0000000..d763f86 Binary files /dev/null and b/libs/jdbc-v2-0.9.8.jar differ diff --git a/libs/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar b/libs/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar new file mode 100644 index 0000000..45832c0 Binary files /dev/null and b/libs/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar differ diff --git a/libs/lz4-java-1.10.4.jar b/libs/lz4-java-1.10.4.jar new file mode 100644 index 0000000..137537b Binary files /dev/null and b/libs/lz4-java-1.10.4.jar differ diff --git a/libs/mysql-connector-j-9.7.0.jar b/libs/mysql-connector-j-9.7.0.jar new file mode 100644 index 0000000..c2726da Binary files /dev/null and b/libs/mysql-connector-j-9.7.0.jar differ diff --git a/libs/postgresql-42.7.11.jar b/libs/postgresql-42.7.11.jar new file mode 100644 index 0000000..e42f175 Binary files /dev/null and b/libs/postgresql-42.7.11.jar differ diff --git a/libs/protobuf-java-4.31.1.jar b/libs/protobuf-java-4.31.1.jar new file mode 100644 index 0000000..f9c0c72 Binary files /dev/null and b/libs/protobuf-java-4.31.1.jar differ diff --git a/libs/vertx-jdbc-client-5.0.11.jar b/libs/vertx-jdbc-client-5.0.11.jar new file mode 100644 index 0000000..aa7461b Binary files /dev/null and b/libs/vertx-jdbc-client-5.0.11.jar differ diff --git a/src/main/java/su/xserver/iikocon/MainVerticle.java b/src/main/java/su/xserver/iikocon/MainVerticle.java index 3d2b7c2..fa59af5 100644 --- a/src/main/java/su/xserver/iikocon/MainVerticle.java +++ b/src/main/java/su/xserver/iikocon/MainVerticle.java @@ -38,6 +38,7 @@ public class MainVerticle extends AbstractVerticle { private UserService userService; private RestaurantService restaurantService; + private ExternalDataBaseService externalDataBaseService; private SettingsService settingsService; @Override @@ -64,6 +65,7 @@ public class MainVerticle extends AbstractVerticle { userService = new UserService(db.getPool()); restaurantService = new RestaurantService(db.getPool()); settingsService = new SettingsService(db.getPool()); + externalDataBaseService = new ExternalDataBaseService(db.getPool(), vertx); userService.initDatabase().onFailure(err -> { log.error("Failed to initialize database", err); @@ -77,6 +79,10 @@ public class MainVerticle extends AbstractVerticle { log.error("Failed to initialize database", err); startPromise.fail(err); }); + externalDataBaseService.initDatabase().onFailure(err -> { + log.error("Failed to initialize database", err); + startPromise.fail(err); + }); createRouterAndStartHttp(startPromise); @@ -418,6 +424,8 @@ public class MainVerticle extends AbstractVerticle { .onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage())); }); + externalDataBaseService.handleRoute(router); + new IikoHandler(vertx, router, db, restaurantService, authHandler); return router; diff --git a/src/main/java/su/xserver/iikocon/service/ExternalDataBaseService.java b/src/main/java/su/xserver/iikocon/service/ExternalDataBaseService.java new file mode 100644 index 0000000..0158a64 --- /dev/null +++ b/src/main/java/su/xserver/iikocon/service/ExternalDataBaseService.java @@ -0,0 +1,259 @@ +package su.xserver.iikocon.service; + +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.jdbcclient.JDBCConnectOptions; +import io.vertx.jdbcclient.JDBCPool; +import io.vertx.sqlclient.Pool; +import io.vertx.sqlclient.PoolOptions; +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.templates.SqlTemplate; +import su.xserver.iikocon.handler.AdminHandler; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ExternalDataBaseService { + private final Pool pool; + private final Vertx vertx; + + public ExternalDataBaseService(Pool pool, Vertx vertx) { + this.pool = pool; + this.vertx = vertx; + } + + public void handleRoute(Router router) { + + router.get("/api/admin/database-connections").handler(rc -> this.getAllDataBases().onComplete(ar -> { + if (ar.succeeded()) { + rc.response() + .putHeader("Content-Type", "application/json") + .end(ar.result().encode()); + } else { + rc.response().setStatusCode(500).end(ar.cause().getMessage()); + } + })); + + router.get("/api/admin/database-connections/:id/test").handler(AdminHandler::requireAdmin).handler(rc -> { + int id = Integer.parseInt(rc.pathParam("id")); + + this.testConnection(id) + .onSuccess(result -> rc.response() + .setStatusCode(200) + .putHeader("Content-Type", "application/json") + .end(result.encode())) + .onFailure(err -> rc.response() + .setStatusCode(500) + .putHeader("Content-Type", "application/json") + .end(new JsonObject() + .put("success", false) + .put("error", err.getMessage()) + .encode())); + }); + + router.post("/api/admin/database-connections").handler(AdminHandler::requireAdmin).handler(rc -> { + JsonObject body = rc.body().asJsonObject(); + String name = body.getString("name"); + String type = body.getString("type"); + String host = body.getString("host"); + int port = body.getInteger("port"); + String database = body.getString("database"); + String user = body.getString("user"); + String password = body.getString("password"); + if (name == null || type == null || host == null || port < 1 || database == null || user == null || password == null) { + rc.response().setStatusCode(400).end("Missing fields"); + return; + } + this.createDataBase(name, type, host, port, database, user, password) + .onSuccess(v -> rc.response().setStatusCode(201).end()) + .onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage())); + }); + + router.put("/api/admin/database-connections/:id").handler(AdminHandler::requireAdmin).handler(rc -> { + int id = Integer.parseInt(rc.pathParam("id")); + JsonObject body = rc.body().asJsonObject(); + String name = body.getString("name"); + String type = body.getString("type"); + String host = body.getString("host"); + int port = body.getInteger("port"); + String database = body.getString("database"); + String user = body.getString("user"); + String password = body.getString("password"); + if (name == null || type == null || host == null || port < 1 || database == null || user == null) { + rc.response().setStatusCode(400).end("Missing fields"); + return; + } + this.updateDataBase(id, name, type, host, port, database, user, password) + .onSuccess(v -> rc.response().end()) + .onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage())); + }); + + router.delete("/api/admin/database-connections/:id").handler(AdminHandler::requireAdmin).handler(rc -> { + int id = Integer.parseInt(rc.pathParam("id")); + this.deleteDataBase(id) + .onSuccess(v -> rc.response().end()) + .onFailure(err -> rc.response().setStatusCode(500).end(err.getMessage())); + }); + } + + public Future initDatabase() { + String createTable = """ + CREATE TABLE IF NOT EXISTS external_database ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) UNIQUE NOT NULL, + type VARCHAR(40) UNIQUE NOT NULL, + host VARCHAR(255) NOT NULL, + port INT NOT NULL, + database VARCHAR(255) NOT NULL, + user VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) + """; + return pool.query(createTable).execute().mapEmpty(); + } + + public Future createDataBase(String name, String type, String host, int port, String database, String user, String password) { + Map params = Map.of( + "name", name, + "type", type, + "host", host, + "port", port, + "database", database, + "user", user, + "password", password + ); + return SqlTemplate.forUpdate(pool, + "INSERT INTO external_database (name, type, host, port, database, user, password) VALUES (#{name}, #{type}, #{host}, #{port}, #{database}, #{user}, #{password})") + .execute(params) + .mapEmpty(); + } + + public Future getAllDataBases() { + return pool.query("SELECT id, name, type, host, port, database, user, password, created, updated FROM external_database ORDER BY id") + .execute() + .map(rows -> { + JsonArray array = new JsonArray(); + for (Row row : rows) { + array.add(new JsonObject() + .put("id", row.getInteger("id")) + .put("name", row.getString("name")) + .put("type", row.getString("type")) + .put("host", row.getString("host")) + .put("port", row.getInteger("port")) + .put("database", row.getString("database")) + .put("user", row.getString("user")) + .put("created", row.getLocalDateTime("created") != null ? + row.getLocalDateTime("created").toString() : null) + .put("updated", row.getLocalDateTime("updated") != null ? + row.getLocalDateTime("updated").toString() : null)); + } + return array; + }); + } + + public Future findById(int id) { + return SqlTemplate.forQuery(pool, + "SELECT id, name, type, host, port, database, user, password, created, updated FROM external_database WHERE id = #{id}") + .mapTo(row -> new JsonObject() + .put("id", row.getInteger("id")) + .put("name", row.getString("name")) + .put("type", row.getString("type")) + .put("host", row.getString("host")) + .put("port", row.getInteger("port")) + .put("database", row.getString("database")) + .put("user", row.getString("user")) + .put("password", row.getString("password")) + .put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) + .put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null)) + .execute(Collections.singletonMap("id", id)) + .map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); + } + + public Future updateDataBase(int id, String name, String type, String host, int port, String database, String user, String password) { + Map params = new HashMap<>(); + params.put("id", id); + params.put("name", name); + params.put("type", type); + params.put("host", host); + params.put("port", port); + params.put("database", database); + params.put("user", user); + String sql; + if (password != null && !password.isEmpty()) { + params.put("password", password); + sql = "UPDATE external_database SET name = #{name}, type = #{type}, host = #{host}, port = #{port}, database = #{database}, user = #{user}, password = #{password} WHERE id = #{id}"; + } else { + sql = "UPDATE external_database SET name = #{name}, type = #{type}, host = #{host}, port = #{port}, database = #{database}, user = #{user} WHERE id = #{id}"; + } + return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty(); + } + + public Future deleteDataBase(int id) { + return SqlTemplate.forUpdate(pool, "DELETE FROM external_database WHERE id = #{id}") + .execute(Collections.singletonMap("id", id)) + .mapEmpty(); + } + + public Future testConnection(int id) { + Promise promise = Promise.promise(); + + this.findById(id) + .onSuccess(conn -> { + String jdbcUrl = buildJdbcUrl(conn); + if (jdbcUrl == null) { + promise.fail("Unsupported database type: " + conn.getString("type")); + return; + } + + JDBCConnectOptions connectOptions = new JDBCConnectOptions() + .setJdbcUrl(jdbcUrl) + .setDatabase(conn.getString("database")) + .setUser(conn.getString("user")) + .setPassword(conn.getString("password")); + + PoolOptions poolOptions = new PoolOptions() + .setMaxSize(1); + + Pool pool = JDBCPool.pool(vertx, connectOptions, poolOptions); + + long startTime = System.currentTimeMillis(); + + pool + .query("SELECT 1") + .execute() + .onSuccess(rows -> { + long latency = System.currentTimeMillis() - startTime; + JsonObject result = new JsonObject() + .put("success", true) + .put("latency_ms", latency); + promise.complete(result); + pool.close(); + }) + .onFailure(err -> promise.fail("Connection failed: " + err.getMessage())); + }) + .onFailure(promise::fail); + + return promise.future(); + } + + private String buildJdbcUrl(JsonObject conn) { + return switch (conn.getString("type").toLowerCase()) { + case "mysql" -> String.format("jdbc:mysql://%s:%d", + conn.getString("host"), conn.getInteger("port")); + case "postgres" -> String.format("jdbc:postgresql://%s:%d", + conn.getString("host"), conn.getInteger("port")); + case "clickhouse" -> + String.format("jdbc:clickhouse://%s:%d", + conn.getString("host"), conn.getInteger("port")); + default -> null; + }; + } + +} diff --git a/src/main/java/su/xserver/iikocon/test/ClickHouseJDBCExample.java b/src/main/java/su/xserver/iikocon/test/ClickHouseJDBCExample.java new file mode 100644 index 0000000..4a6af6b --- /dev/null +++ b/src/main/java/su/xserver/iikocon/test/ClickHouseJDBCExample.java @@ -0,0 +1,41 @@ +package su.xserver.iikocon.test; + +import io.vertx.core.Vertx; +import io.vertx.jdbcclient.JDBCConnectOptions; +import io.vertx.jdbcclient.JDBCPool; +import io.vertx.sqlclient.Pool; +import io.vertx.sqlclient.PoolOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClickHouseJDBCExample { + private static final Logger log = LoggerFactory.getLogger(ClickHouseJDBCExample.class); + + public static void main(String[] args) { + Vertx vertx = Vertx.vertx(); + + JDBCConnectOptions connectOptions = new JDBCConnectOptions() + .setJdbcUrl("jdbc:clickhouse://dl-import.aramagedec.ru:8123") + .setDatabase("test") + .setUser("clickhouse_admin") + .setPassword("7002ITinsta11"); + + PoolOptions poolOptions = new PoolOptions() + .setMaxSize(16); + + Pool pool = JDBCPool.pool(vertx, connectOptions, poolOptions); + + pool + .query("SELECT 1") + .execute() + .onSuccess(rows -> { + rows.forEach(row -> log.info(row.toJson().encodePrettily())); + vertx.close(); + }) + .onFailure(err -> { + log.error(err.getMessage()); + vertx.close(); + }); + } + +}
{{ t('common.leavePasswordBlank') }}
{{ t('dbConnections.deleteConfirmation') }}