up
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<Void> 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<Void> createDataBase(String name, String type, String host, int port, String database, String user, String password) {
|
||||
Map<String, Object> 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<JsonArray> 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<JsonObject> 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<Void> updateDataBase(int id, String name, String type, String host, int port, String database, String user, String password) {
|
||||
Map<String, Object> 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<Void> deleteDataBase(int id) {
|
||||
return SqlTemplate.forUpdate(pool, "DELETE FROM external_database WHERE id = #{id}")
|
||||
.execute(Collections.singletonMap("id", id))
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
public Future<JsonObject> testConnection(int id) {
|
||||
Promise<JsonObject> 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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user