From 1ca4c90b882345d4bcc95ce3856720731f44bb9f Mon Sep 17 00:00:00 2001 From: Danil-Bodry Date: Mon, 4 May 2026 15:08:03 +0300 Subject: [PATCH] up --- build.gradle.kts | 1 + docker-compose.yml | 8 +- frontend/src/components/Layout/AppLayout.vue | 2 +- .../java/su/xserver/iikocon/MainVerticle.java | 123 +++++++++++++++--- .../su/xserver/iikocon/config/AppConfig.java | 4 +- .../iikocon/config/PhpMyAdminConfig.java | 18 +++ src/main/resources/config.json | 5 + 7 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 src/main/java/su/xserver/iikocon/config/PhpMyAdminConfig.java diff --git a/build.gradle.kts b/build.gradle.kts index a07f5dc..f127ffb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(platform("io.vertx:vertx-stack-depchain:$vertxVersion")) implementation("io.vertx:vertx-launcher-application") implementation("io.vertx:vertx-web-client") + implementation("io.vertx:vertx-web-proxy") implementation("io.vertx:vertx-config") implementation("io.vertx:vertx-sql-client-templates") implementation("io.vertx:vertx-health-check") diff --git a/docker-compose.yml b/docker-compose.yml index e2d9f51..68b2ac0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,11 +34,13 @@ services: environment: PMA_HOST: iiko-db PMA_PORT: 3306 + PMA_USER: root + PMA_PASSWORD: DVjXT_kew508 UPLOAD_LIMIT: 10M PMA_ABSOLUTE_URI: https://iiko-app.dev.xserver.su/phpmyadmin/ TZ: Europe/Moscow ports: - - "7102:80" +# - "7102:80" iiko-redis: image: redis:latest @@ -75,5 +77,9 @@ services: REDIS__HOST: iiko-redis REDIS__PORT: 6379 SERVER__PORT: 7104 + PMA__ENABLED: true + PMA__BASE_PATH: /phpmyadmin + PMA__UPSTREAM: http://iiko-pma:80/ + volumes: - $PWD/app/logs:/app/logs diff --git a/frontend/src/components/Layout/AppLayout.vue b/frontend/src/components/Layout/AppLayout.vue index b7e8310..aa825c5 100644 --- a/frontend/src/components/Layout/AppLayout.vue +++ b/frontend/src/components/Layout/AppLayout.vue @@ -186,7 +186,7 @@ startPromise) { + public void start(Promise startPromise) throws ClassNotFoundException { + + Class.forName("com.mysql.cj.jdbc.Driver"); + Class.forName("org.postgresql.Driver"); ConfigStoreOptions classpathStore = new ConfigStoreOptions() .setType("file") @@ -119,12 +128,107 @@ public class MainVerticle extends AbstractVerticle { }); } + private void setupPhpmyadminProxy(Router router) { + if (config.pma == null || !config.pma.enabled) return; + + String upstream = config.pma.upstream; + String basePath = config.pma.basePath; + + final URI upstreamUri = URI.create(upstream); + final String host = upstreamUri.getHost(); + int portTmp = upstreamUri.getPort(); + if (portTmp == -1) { + portTmp = "https".equals(upstreamUri.getScheme()) ? 443 : 80; + } + final int port = portTmp; + + final WebClient webClient = WebClient.create(vertx); + + router.route(basePath + "/*").handler(ctx -> { + if (ctx.session() != null && "admin".equals(ctx.session().get("role"))) { + ctx.next(); + } else { + ctx.response().putHeader("Location", "/").setStatusCode(302).end(); + } + }); + + router.route(basePath + "/*").handler(ctx -> { + String targetPathBase = ctx.request().path().substring(basePath.length()); + if (targetPathBase.isEmpty()) targetPathBase = "/"; + String targetPath = targetPathBase; + String query = ctx.request().query(); + if (query != null && !query.isEmpty()) { + targetPath += "?" + query; + } + final String targetPathFinal = targetPath; + + final HttpRequest proxyReq = webClient.request( + ctx.request().method(), port, host, targetPathFinal + ); + + ctx.request().headers().forEach(header -> { + if (!"host".equalsIgnoreCase(header.getKey())) { + proxyReq.putHeader(header.getKey(), header.getValue()); + } + }); + proxyReq.putHeader("Host", host + ":" + port); + + ctx.request().bodyHandler(body -> { + if (body != null && body.length() > 0) { + proxyReq.sendBuffer(body) + .onSuccess(resp -> sendResponse(ctx, resp)) + .onFailure(err -> sendError(ctx, err)); + } else { + proxyReq.send() + .onSuccess(resp -> sendResponse(ctx, resp)) + .onFailure(err -> sendError(ctx, err)); + } + }); + }); + } + + private void sendResponse(RoutingContext ctx, HttpResponse resp) { + ctx.response().setStatusCode(resp.statusCode()); + resp.headers().forEach(h -> ctx.response().putHeader(h.getKey(), h.getValue())); + ctx.response().end(resp.body()); + } + + private void sendError(RoutingContext ctx, Throwable err) { + log.error("Proxy error: {}", err.getMessage()); + ctx.response().setStatusCode(502).end("Bad Gateway: " + err.getMessage()); + } + private Router initRouter(SessionHandler sessionHandler) { Router router = Router.router(vertx); - router.route().handler(BodyHandler.create()); + + router.route().handler(ctx -> { + long start = System.currentTimeMillis(); + String method = ctx.request().method().name(); + String path = ctx.request().path(); + final String remoteIp = ctx.get("realClientIp") != null ? + ctx.get("realClientIp") : + ctx.request().remoteAddress().host(); + ctx.addBodyEndHandler(v -> { + long duration = System.currentTimeMillis() - start; + log.info("{} {} - {} ms - {} - {}", + method, path, duration, ctx.response().getStatusCode(), remoteIp); + }); + ctx.next(); + }); + + router.route().handler(ctx -> { + String path = ctx.request().path(); + if (path != null && path.startsWith(config.pma.basePath + "/")) { + ctx.next(); // пропускаем BodyHandler для прокси + } else { + BodyHandler.create().handle(ctx); + } + }); router.route().handler(sessionHandler); + setupPhpmyadminProxy(router); + SecurityHandler securityHandlers = new SecurityHandler(settingsService); // Обработчики безопасности @@ -147,21 +251,6 @@ public class MainVerticle extends AbstractVerticle { } }); - router.route().handler(ctx -> { - long start = System.currentTimeMillis(); - String method = ctx.request().method().name(); - String path = ctx.request().path(); - final String remoteIp = ctx.get("realClientIp") != null ? - ctx.get("realClientIp") : - ctx.request().remoteAddress().host(); - ctx.addBodyEndHandler(v -> { - long duration = System.currentTimeMillis() - start; - log.info("{} {} - {} ms - {} - {}", - method, path, duration, ctx.response().getStatusCode(), remoteIp); - }); - ctx.next(); - }); - // ------ Раздаём Vue статику ------ router.route("/assets/*").handler(StaticHandler.create("webroot/assets")); diff --git a/src/main/java/su/xserver/iikocon/config/AppConfig.java b/src/main/java/su/xserver/iikocon/config/AppConfig.java index 385d171..2d49b7c 100644 --- a/src/main/java/su/xserver/iikocon/config/AppConfig.java +++ b/src/main/java/su/xserver/iikocon/config/AppConfig.java @@ -8,6 +8,7 @@ public class AppConfig { public ServerConfig server; public DatabaseConfig database; public RedisConfig redis; + public PhpMyAdminConfig pma; public static AppConfig from(JsonObject json) { JsonObject resolved = json.copy(); @@ -94,7 +95,8 @@ public class AppConfig { return new JsonObject() .put("server", server.json().getJsonObject("server")) .put("database", database.json().getJsonObject("database")) - .put("redis", redis.json().getJsonObject("redis")); + .put("redis", redis.json().getJsonObject("redis")) + .put("pma", pma.json().getJsonObject("pma")); } @Override diff --git a/src/main/java/su/xserver/iikocon/config/PhpMyAdminConfig.java b/src/main/java/su/xserver/iikocon/config/PhpMyAdminConfig.java new file mode 100644 index 0000000..800721f --- /dev/null +++ b/src/main/java/su/xserver/iikocon/config/PhpMyAdminConfig.java @@ -0,0 +1,18 @@ +package su.xserver.iikocon.config; + +import io.vertx.core.json.JsonObject; + +public class PhpMyAdminConfig { + public boolean enabled; + public String upstream; + public String basePath; + + public JsonObject json() { + return new JsonObject() + .put("pma", new JsonObject() + .put("enabled", enabled) + .put("upstream", upstream) + .put("basePath", basePath) + ); + } +} diff --git a/src/main/resources/config.json b/src/main/resources/config.json index 05c3fa4..2012ced 100644 --- a/src/main/resources/config.json +++ b/src/main/resources/config.json @@ -18,5 +18,10 @@ "password": null, "maxPoolSize": 6, "maxWaitingHandlers": 6 + }, + "pma": { + "enabled": false, + "basePath": "/pma", + "upstream": "http://localhost:80/" } }