up
This commit is contained in:
@@ -37,6 +37,7 @@ dependencies {
|
|||||||
implementation(platform("io.vertx:vertx-stack-depchain:$vertxVersion"))
|
implementation(platform("io.vertx:vertx-stack-depchain:$vertxVersion"))
|
||||||
implementation("io.vertx:vertx-launcher-application")
|
implementation("io.vertx:vertx-launcher-application")
|
||||||
implementation("io.vertx:vertx-web-client")
|
implementation("io.vertx:vertx-web-client")
|
||||||
|
implementation("io.vertx:vertx-web-proxy")
|
||||||
implementation("io.vertx:vertx-config")
|
implementation("io.vertx:vertx-config")
|
||||||
implementation("io.vertx:vertx-sql-client-templates")
|
implementation("io.vertx:vertx-sql-client-templates")
|
||||||
implementation("io.vertx:vertx-health-check")
|
implementation("io.vertx:vertx-health-check")
|
||||||
|
|||||||
@@ -34,11 +34,13 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PMA_HOST: iiko-db
|
PMA_HOST: iiko-db
|
||||||
PMA_PORT: 3306
|
PMA_PORT: 3306
|
||||||
|
PMA_USER: root
|
||||||
|
PMA_PASSWORD: DVjXT_kew508
|
||||||
UPLOAD_LIMIT: 10M
|
UPLOAD_LIMIT: 10M
|
||||||
PMA_ABSOLUTE_URI: https://iiko-app.dev.xserver.su/phpmyadmin/
|
PMA_ABSOLUTE_URI: https://iiko-app.dev.xserver.su/phpmyadmin/
|
||||||
TZ: Europe/Moscow
|
TZ: Europe/Moscow
|
||||||
ports:
|
ports:
|
||||||
- "7102:80"
|
# - "7102:80"
|
||||||
|
|
||||||
iiko-redis:
|
iiko-redis:
|
||||||
image: redis:latest
|
image: redis:latest
|
||||||
@@ -75,5 +77,9 @@ services:
|
|||||||
REDIS__HOST: iiko-redis
|
REDIS__HOST: iiko-redis
|
||||||
REDIS__PORT: 6379
|
REDIS__PORT: 6379
|
||||||
SERVER__PORT: 7104
|
SERVER__PORT: 7104
|
||||||
|
PMA__ENABLED: true
|
||||||
|
PMA__BASE_PATH: /phpmyadmin
|
||||||
|
PMA__UPSTREAM: http://iiko-pma:80/
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- $PWD/app/logs:/app/logs
|
- $PWD/app/logs:/app/logs
|
||||||
|
|||||||
@@ -186,7 +186,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</router-link>
|
</router-link>
|
||||||
<a
|
<a
|
||||||
href="/phpmyadmin"
|
href="/phpmyadmin/"
|
||||||
v-if="userStore.role === 'admin'"
|
v-if="userStore.role === 'admin'"
|
||||||
target="_self"
|
target="_self"
|
||||||
class="p-2 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors"
|
class="p-2 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors"
|
||||||
|
|||||||
@@ -6,9 +6,14 @@ import io.vertx.config.ConfigStoreOptions;
|
|||||||
import io.vertx.core.AbstractVerticle;
|
import io.vertx.core.AbstractVerticle;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.Promise;
|
import io.vertx.core.Promise;
|
||||||
|
import io.vertx.core.buffer.Buffer;
|
||||||
import io.vertx.core.http.HttpServer;
|
import io.vertx.core.http.HttpServer;
|
||||||
import io.vertx.core.json.JsonObject;
|
import io.vertx.core.json.JsonObject;
|
||||||
import io.vertx.ext.web.Router;
|
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.BodyHandler;
|
||||||
import io.vertx.ext.web.handler.SessionHandler;
|
import io.vertx.ext.web.handler.SessionHandler;
|
||||||
import io.vertx.ext.web.handler.StaticHandler;
|
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.iiko.IikoOlapClient;
|
||||||
import su.xserver.iikocon.service.*;
|
import su.xserver.iikocon.service.*;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -42,7 +48,10 @@ public class MainVerticle extends AbstractVerticle {
|
|||||||
private SettingsService settingsService;
|
private SettingsService settingsService;
|
||||||
|
|
||||||
@Override
|
@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()
|
ConfigStoreOptions classpathStore = new ConfigStoreOptions()
|
||||||
.setType("file")
|
.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) {
|
private Router initRouter(SessionHandler sessionHandler) {
|
||||||
|
|
||||||
Router router = Router.router(vertx);
|
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);
|
router.route().handler(sessionHandler);
|
||||||
|
|
||||||
|
setupPhpmyadminProxy(router);
|
||||||
|
|
||||||
SecurityHandler securityHandlers = new SecurityHandler(settingsService);
|
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 статику ------
|
// ------ Раздаём Vue статику ------
|
||||||
router.route("/assets/*").handler(StaticHandler.create("webroot/assets"));
|
router.route("/assets/*").handler(StaticHandler.create("webroot/assets"));
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class AppConfig {
|
|||||||
public ServerConfig server;
|
public ServerConfig server;
|
||||||
public DatabaseConfig database;
|
public DatabaseConfig database;
|
||||||
public RedisConfig redis;
|
public RedisConfig redis;
|
||||||
|
public PhpMyAdminConfig pma;
|
||||||
|
|
||||||
public static AppConfig from(JsonObject json) {
|
public static AppConfig from(JsonObject json) {
|
||||||
JsonObject resolved = json.copy();
|
JsonObject resolved = json.copy();
|
||||||
@@ -94,7 +95,8 @@ public class AppConfig {
|
|||||||
return new JsonObject()
|
return new JsonObject()
|
||||||
.put("server", server.json().getJsonObject("server"))
|
.put("server", server.json().getJsonObject("server"))
|
||||||
.put("database", database.json().getJsonObject("database"))
|
.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
|
@Override
|
||||||
|
|||||||
@@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,5 +18,10 @@
|
|||||||
"password": null,
|
"password": null,
|
||||||
"maxPoolSize": 6,
|
"maxPoolSize": 6,
|
||||||
"maxWaitingHandlers": 6
|
"maxWaitingHandlers": 6
|
||||||
|
},
|
||||||
|
"pma": {
|
||||||
|
"enabled": false,
|
||||||
|
"basePath": "/pma",
|
||||||
|
"upstream": "http://localhost:80/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user