This commit is contained in:
2026-05-04 15:08:03 +03:00
parent a61c527ef9
commit 1ca4c90b88
7 changed files with 141 additions and 20 deletions

View File

@@ -6,9 +6,14 @@ import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.HttpRequest;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.handler.StaticHandler;
@@ -23,6 +28,7 @@ import su.xserver.iikocon.iiko.IikoHandler;
import su.xserver.iikocon.iiko.IikoOlapClient;
import su.xserver.iikocon.service.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -42,7 +48,10 @@ public class MainVerticle extends AbstractVerticle {
private SettingsService settingsService;
@Override
public void start(Promise<Void> startPromise) {
public void start(Promise<Void> 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<Buffer> 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<Buffer> 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"));

View File

@@ -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

View File

@@ -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)
);
}
}

View File

@@ -18,5 +18,10 @@
"password": null,
"maxPoolSize": 6,
"maxWaitingHandlers": 6
},
"pma": {
"enabled": false,
"basePath": "/pma",
"upstream": "http://localhost:80/"
}
}