From c54e3f47859c2d7c27eeec7f8d348d21e4defd81 Mon Sep 17 00:00:00 2001 From: Melloware Date: Sat, 15 Jun 2024 09:09:59 -0400 Subject: [PATCH 01/13] Refactor TLS and network handling (#693) --- .../deployment/ForwardedDevProcessor.java | 50 +++---- .../items/ForwardedDevServerBuildItem.java | 27 ++-- .../packagemanager/PackageManagerRunner.java | 22 +-- .../ROOT/pages/includes/quarkus-quinoa.adoc | 55 ++++---- .../quinoa/QuinoaDevProxyHandler.java | 26 ++-- .../QuinoaDevWebSocketProxyHandler.java | 20 +-- .../quinoa/QuinoaNetworkConfiguration.java | 131 ++++++++++++++++++ .../io/quarkiverse/quinoa/QuinoaRecorder.java | 6 +- 8 files changed, 230 insertions(+), 107 deletions(-) create mode 100644 runtime/src/main/java/io/quarkiverse/quinoa/QuinoaNetworkConfiguration.java diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java index 8815d498..c52168e2 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java @@ -29,6 +29,7 @@ import org.jboss.logging.Logger; import io.quarkiverse.quinoa.QuinoaDevProxyHandlerConfig; +import io.quarkiverse.quinoa.QuinoaNetworkConfiguration; import io.quarkiverse.quinoa.QuinoaRecorder; import io.quarkiverse.quinoa.deployment.config.DevServerConfig; import io.quarkiverse.quinoa.deployment.config.QuinoaConfig; @@ -42,7 +43,6 @@ import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.CuratedApplicationShutdownBuildItem; import io.quarkus.deployment.builditem.DevServicesResultBuildItem; -import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.LiveReloadBuildItem; import io.quarkus.deployment.console.ConsoleInstalledBuildItem; import io.quarkus.deployment.logging.LoggingSetupBuildItem; @@ -62,7 +62,6 @@ public class ForwardedDevProcessor { @BuildStep(onlyIf = IsDevelopment.class) public ForwardedDevServerBuildItem prepareDevService( - LaunchModeBuildItem launchMode, ConfiguredQuinoaBuildItem configuredQuinoa, InstalledPackageManagerBuildItem installedPackageManager, QuinoaConfig userConfig, @@ -78,9 +77,8 @@ public ForwardedDevServerBuildItem prepareDevService( final QuinoaConfig resolvedConfig = configuredQuinoa.resolvedConfig(); final DevServerConfig devServerConfig = resolvedConfig.devServer(); liveReload.setContextObject(QuinoaConfig.class, resolvedConfig); - final String configuredDevServerHost = devServerConfig.host(); - final boolean configuredTls = devServerConfig.tls(); - final boolean configuredTlsAllowInsecure = devServerConfig.tlsAllowInsecure(); + final QuinoaNetworkConfiguration networkConfiguration = new QuinoaNetworkConfiguration(devServerConfig.tls(), + devServerConfig.tlsAllowInsecure(), devServerConfig.host(), devServerConfig.port().orElse(0), false); final PackageManagerRunner packageManagerRunner = installedPackageManager.getPackageManager(); final String checkPath = resolvedConfig.devServer().checkPath().orElse(null); if (devService != null) { @@ -94,13 +92,11 @@ public ForwardedDevServerBuildItem prepareDevService( } LOG.debug("Quinoa config did not change; no need to restart."); devServices.produce(devService.toBuildItem()); - final String resolvedDevServerHost = PackageManagerRunner.isDevServerUp(devServerConfig.tls(), - devServerConfig.tlsAllowInsecure(), - devServerConfig.host(), - devServerConfig.port().get(), - checkPath); - return new ForwardedDevServerBuildItem(devServerConfig.tls(), devServerConfig.tlsAllowInsecure(), - resolvedDevServerHost, devServerConfig.port().get()); + networkConfiguration.setHost(devServerConfig.host()); + networkConfiguration.setPort(devServerConfig.port().get()); + final String resolvedDevServerHost = PackageManagerRunner.isDevServerUp(networkConfiguration, checkPath); + networkConfiguration.setHost(resolvedDevServerHost); + return new ForwardedDevServerBuildItem(networkConfiguration); } shutdownDevService(); } @@ -119,14 +115,15 @@ public ForwardedDevServerBuildItem prepareDevService( return null; } final Integer port = devServerConfig.port().get(); + networkConfiguration.setPort(port); if (!devServerConfig.managed()) { // No need to start the dev-service it is not managed by Quinoa // We just check that it is up - final String resolvedHostIPAddress = PackageManagerRunner.isDevServerUp(configuredTls, configuredTlsAllowInsecure, - configuredDevServerHost, port, checkPath); + final String resolvedHostIPAddress = PackageManagerRunner.isDevServerUp(networkConfiguration, checkPath); if (resolvedHostIPAddress != null) { - return new ForwardedDevServerBuildItem(configuredTls, configuredTlsAllowInsecure, resolvedHostIPAddress, port); + networkConfiguration.setHost(resolvedHostIPAddress); + return new ForwardedDevServerBuildItem(networkConfiguration); } else { throw new IllegalStateException( "The Web UI dev server (configured as not managed by Quinoa) is not started on port: " + port); @@ -141,11 +138,8 @@ public ForwardedDevServerBuildItem prepareDevService( final AtomicReference dev = new AtomicReference<>(); PackageManagerRunner.DevServer devServer = null; try { - devServer = packageManagerRunner.dev(consoleInstalled, loggingSetup, configuredTls, configuredTlsAllowInsecure, - configuredDevServerHost, - port, - checkPath, - checkTimeout); + devServer = packageManagerRunner.dev(consoleInstalled, loggingSetup, networkConfiguration, + checkPath, checkTimeout); dev.set(devServer.process()); devServer.logCompressor().close(); final LiveCodingLogOutputFilter logOutputFilter = new LiveCodingLogOutputFilter( @@ -162,7 +156,8 @@ public ForwardedDevServerBuildItem prepareDevService( devService = new DevServicesResultBuildItem.RunningDevService( DEV_SERVICE_NAME, null, onClose, devServerConfigMap); devServices.produce(devService.toBuildItem()); - return new ForwardedDevServerBuildItem(configuredTls, configuredTlsAllowInsecure, devServer.hostIPAddress(), port); + networkConfiguration.setHost(devServer.hostIPAddress()); + return new ForwardedDevServerBuildItem(networkConfiguration); } catch (Throwable t) { packageManagerRunner.stopDev(dev.get()); if (devServer != null) { @@ -176,7 +171,7 @@ private static Map createDevServiceMapForDevUI(QuinoaConfig quin Map devServerConfigMap = new LinkedHashMap<>(); devServerConfigMap.put("quarkus.quinoa.dev-server.host", quinoaConfig.devServer().host()); devServerConfigMap.put("quarkus.quinoa.dev-server.port", - quinoaConfig.devServer().port().map(p -> p.toString()).orElse("")); + quinoaConfig.devServer().port().map(Object::toString).orElse("")); devServerConfigMap.put("quarkus.quinoa.dev-server.check-timeout", Integer.toString(quinoaConfig.devServer().checkTimeout())); devServerConfigMap.put("quarkus.quinoa.dev-server.check-path", quinoaConfig.devServer().checkPath().orElse("")); @@ -206,11 +201,12 @@ public void runtimeInit( } LOG.infof("Quinoa is forwarding unhandled requests to port: %d", devProxy.get().getPort()); final QuinoaDevProxyHandlerConfig handlerConfig = toDevProxyHandlerConfig(quinoaConfig, httpBuildTimeConfig); + final QuinoaNetworkConfiguration networkConfig = new QuinoaNetworkConfiguration(devProxy.get().isTls(), + devProxy.get().isTlsAllowInsecure(), devProxy.get().getHost(), + devProxy.get().getPort(), + quinoaConfig.devServer().websocket()); routes.produce(RouteBuildItem.builder().orderedRoute("/*", QUINOA_ROUTE_ORDER) - .handler(recorder.quinoaProxyDevHandler(handlerConfig, vertx.getVertx(), devProxy.get().isTls(), - devProxy.get().isTlsAllowInsecure(), devProxy.get().getHost(), - devProxy.get().getPort(), - quinoaConfig.devServer().websocket())) + .handler(recorder.quinoaProxyDevHandler(handlerConfig, vertx.getVertx(), networkConfig)) .build()); if (quinoaConfig.devServer().websocket()) { websocketSubProtocols.produce(new WebsocketSubProtocolsBuildItem("*")); @@ -300,4 +296,4 @@ public Thread newThread(Runnable r) { } -} +} \ No newline at end of file diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/items/ForwardedDevServerBuildItem.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/items/ForwardedDevServerBuildItem.java index a202cb3d..096f047a 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/items/ForwardedDevServerBuildItem.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/items/ForwardedDevServerBuildItem.java @@ -1,34 +1,33 @@ package io.quarkiverse.quinoa.deployment.items; +import io.quarkiverse.quinoa.QuinoaNetworkConfiguration; import io.quarkus.builder.item.SimpleBuildItem; public final class ForwardedDevServerBuildItem extends SimpleBuildItem { - private final boolean tls; - private final boolean tlsAllowInsecure; - private final String host; - private final Integer port; + private final QuinoaNetworkConfiguration networkConfiguration; - public ForwardedDevServerBuildItem(boolean tls, boolean tlsAllowInsecure, String host, Integer port) { - this.tls = tls; - this.tlsAllowInsecure = tlsAllowInsecure; - this.host = host; - this.port = port; + public ForwardedDevServerBuildItem(QuinoaNetworkConfiguration networkConfiguration) { + this.networkConfiguration = networkConfiguration; + } + + public QuinoaNetworkConfiguration getNetworkConfiguration() { + return networkConfiguration; } public boolean isTls() { - return tls; + return networkConfiguration.isTls(); } public boolean isTlsAllowInsecure() { - return tlsAllowInsecure; + return networkConfiguration.isTlsAllowInsecure(); } public String getHost() { - return host; + return networkConfiguration.getHost(); } public Integer getPort() { - return port; + return networkConfiguration.getPort(); } -} +} \ No newline at end of file diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/packagemanager/PackageManagerRunner.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/packagemanager/PackageManagerRunner.java index 152d1b8a..e8fd3d57 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/packagemanager/PackageManagerRunner.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/packagemanager/PackageManagerRunner.java @@ -27,6 +27,7 @@ import org.jboss.logging.Logger; +import io.quarkiverse.quinoa.QuinoaNetworkConfiguration; import io.quarkiverse.quinoa.deployment.SslUtil; import io.quarkiverse.quinoa.deployment.config.PackageManagerCommandConfig; import io.quarkiverse.quinoa.deployment.packagemanager.types.PackageManager; @@ -139,7 +140,7 @@ private static void killDescendants(ProcessHandle process, boolean force) { } public DevServer dev(Optional consoleInstalled, LoggingSetupBuildItem loggingSetup, - boolean tls, boolean tlsAllowInsecure, String devServerHost, int devServerPort, String checkPath, + QuinoaNetworkConfiguration network, String checkPath, int checkTimeout) { final PackageManager.Command dev = packageManager.dev(); LOG.infof("Running Quinoa package manager live coding as a dev service: %s", dev.commandWithArguments); @@ -156,16 +157,16 @@ public void run() { }); if (checkPath == null) { LOG.infof("Quinoa is configured to continue without check if the live coding server is up"); - return new DevServer(p, devServerHost, logCompressor); + return new DevServer(p, network.getHost(), logCompressor); } String ipAddress = null; try { int i = 0; - while ((ipAddress = isDevServerUp(tls, tlsAllowInsecure, devServerHost, devServerPort, checkPath)) == null) { + while ((ipAddress = isDevServerUp(network, checkPath)) == null) { if (++i >= checkTimeout / 500) { stopDev(p); throw new RuntimeException( - "Quinoa package manager live coding port " + devServerPort + "Quinoa package manager live coding port " + network.getPort() + " is still not listening after the checkTimeout."); } Thread.sleep(500); @@ -275,22 +276,23 @@ public void run() { } } - public static String isDevServerUp(boolean tls, boolean tlsAllowInsecure, String host, int port, String path) { + public static String isDevServerUp(QuinoaNetworkConfiguration network, String path) { if (path == null) { - return host; + return network.getHost(); } final String normalizedPath = path.indexOf("/") == 0 ? path : "/" + path; try { - InetAddress[] addresses = InetAddress.getAllByName(host); + InetAddress[] addresses = InetAddress.getAllByName(network.getHost()); for (InetAddress address : addresses) { try { final String hostAddress = address.getHostAddress(); final String ipAddress = address instanceof Inet6Address ? "[" + hostAddress + "]" : hostAddress; - URL url = new URL(String.format("%s://%s:%d%s", tls ? "https" : "http", ipAddress, port, normalizedPath)); + URL url = new URL(String.format("%s://%s:%d%s", network.isTls() ? "https" : "http", ipAddress, + network.getPort(), normalizedPath)); HttpURLConnection connection; - if (tls) { + if (network.isTls()) { HttpsURLConnection httpsConnection = (HttpsURLConnection) url.openConnection(); - if (tlsAllowInsecure) { + if (network.isTlsAllowInsecure()) { httpsConnection.setSSLSocketFactory(SslUtil.createNonValidatingSslContext().getSocketFactory()); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override diff --git a/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc b/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc index 2c81bbcf..96c8f2e2 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc @@ -519,70 +519,73 @@ endif::add-copy-button-to-env-var[] --|boolean |`true` -a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-tls]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-tls[quarkus.quinoa.dev-server.tls]` + +a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-port]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-port[quarkus.quinoa.dev-server.port]` [.description] -- -When set to true, Quinoa requests will be forwarded with tls enabled. +Port of the server to forward requests to. The dev server process (i.e npm start) is managed like a dev service by Quarkus. If the external server responds with a 404, it is ignored by Quinoa and processed like any other backend request. ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_TLS+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_PORT+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_TLS+++` +Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_PORT+++` endif::add-copy-button-to-env-var[] ---|boolean -|`false` +--|int +|`framework detection or fallback to empty` -a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-tls-allow-insecure]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-tls-allow-insecure[quarkus.quinoa.dev-server.tls.allow-insecure]` + +a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-host]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-host[quarkus.quinoa.dev-server.host]` [.description] -- -When set to true, Quinoa will accept any certificate with any hostname. +Host of the server to forward requests to. ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_TLS_ALLOW_INSECURE+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_HOST+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_TLS_ALLOW_INSECURE+++` +Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_HOST+++` endif::add-copy-button-to-env-var[] ---|boolean -|`false` +--|string +|`localhost` -a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-port]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-port[quarkus.quinoa.dev-server.port]` + +a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-tls]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-tls[quarkus.quinoa.dev-server.tls]` [.description] -- -Port of the server to forward requests to. The dev server process (i.e npm start) is managed like a dev service by Quarkus. If the external server responds with a 404, it is ignored by Quinoa and processed like any other backend request. +When set to true, Quinoa requests will be forwarded with tls enabled. ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_PORT+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_TLS+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_PORT+++` +Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_TLS+++` endif::add-copy-button-to-env-var[] ---|int -|`framework detection or fallback to empty` +--|boolean +|`false` -a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-host]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-host[quarkus.quinoa.dev-server.host]` +a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-tls-allow-insecure]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-tls-allow-insecure[quarkus.quinoa.dev-server.tls-allow-insecure]` [.description] -- -Host of the server to forward requests to. +When set to true, Quinoa will accept any certificate with any hostname. ifdef::add-copy-button-to-env-var[] -Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_HOST+++[] +Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_DEV_SERVER_TLS_ALLOW_INSECURE+++[] endif::add-copy-button-to-env-var[] ifndef::add-copy-button-to-env-var[] -Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_HOST+++` +Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_TLS_ALLOW_INSECURE+++` endif::add-copy-button-to-env-var[] ---|string -|`localhost` +--|boolean +|`false` a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-check-path]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-check-path[quarkus.quinoa.dev-server.check-path]` @@ -667,7 +670,7 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_QUINOA_DEV_SERVER_INDEX_PAGE+++` endif::add-copy-button-to-env-var[] --|string -|`auto-detected falling back to the quinoa.index-page` +|`auto-detected falling back to index.html` a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server-direct-forwarding]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server-direct-forwarding[quarkus.quinoa.dev-server.direct-forwarding]` @@ -776,4 +779,4 @@ endif::add-copy-button-to-env-var[] | -|=== +|=== \ No newline at end of file diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevProxyHandler.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevProxyHandler.java index 31065b1d..0cd1fba1 100644 --- a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevProxyHandler.java +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevProxyHandler.java @@ -7,7 +7,6 @@ import java.util.List; -import io.vertx.ext.web.client.WebClientOptions; import org.jboss.logging.Logger; import io.vertx.core.AsyncResult; @@ -21,6 +20,7 @@ import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; +import io.vertx.ext.web.client.WebClientOptions; class QuinoaDevProxyHandler implements Handler { private static final Logger LOG = Logger.getLogger(QuinoaDevProxyHandler.class); @@ -30,29 +30,21 @@ class QuinoaDevProxyHandler implements Handler { HttpHeaders.CONTENT_LENGTH.toString(), HttpHeaders.CONTENT_TYPE.toString()); - private final String host; - private final int port; + private final QuinoaNetworkConfiguration networkConfiguration; private final WebClient client; private final QuinoaDevWebSocketProxyHandler wsUpgradeHandler; private final ClassLoader currentClassLoader; private final QuinoaDevProxyHandlerConfig config; - QuinoaDevProxyHandler(final QuinoaDevProxyHandlerConfig config, final Vertx vertx, boolean tls, boolean tlsAllowInsecure, - String host, int port, - boolean websocket) { - this.host = host; - this.port = port; + QuinoaDevProxyHandler(final QuinoaDevProxyHandlerConfig config, final Vertx vertx, QuinoaNetworkConfiguration network) { WebClientOptions options = new WebClientOptions(); - if (tls) { - options.setSsl(true); - if (tlsAllowInsecure) { - options.setTrustAll(true); - options.setVerifyHost(false); - } - } + options.setSsl(network.isTls()); + options.setTrustAll(network.isTlsAllowInsecure()); + options.setVerifyHost(!network.isTlsAllowInsecure()); this.client = WebClient.create(vertx, options); - this.wsUpgradeHandler = websocket ? new QuinoaDevWebSocketProxyHandler(vertx, host, port) : null; + this.wsUpgradeHandler = network.isWebsocket() ? new QuinoaDevWebSocketProxyHandler(vertx, network) : null; this.config = config; + this.networkConfiguration = network; currentClassLoader = Thread.currentThread().getContextClassLoader(); } @@ -97,7 +89,7 @@ private void handleHttpRequest(final RoutingContext ctx, final String resourcePa // Disable compression in the forwarded request headers.remove("Accept-Encoding"); - client.request(request.method(), port, host, uri) + client.request(request.method(), networkConfiguration.getPort(), networkConfiguration.getHost(), uri) .putHeaders(headers) .send(event -> { if (event.succeeded()) { diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevWebSocketProxyHandler.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevWebSocketProxyHandler.java index b7a5c2e7..054970ed 100644 --- a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevWebSocketProxyHandler.java +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaDevWebSocketProxyHandler.java @@ -18,13 +18,11 @@ class QuinoaDevWebSocketProxyHandler { private static final Logger LOG = Logger.getLogger(QuinoaDevWebSocketProxyHandler.class); private final HttpClient httpClient; - private String host; - private final int port; + private final QuinoaNetworkConfiguration networkConfiguration; - QuinoaDevWebSocketProxyHandler(Vertx vertx, String host, int port) { + QuinoaDevWebSocketProxyHandler(Vertx vertx, QuinoaNetworkConfiguration network) { this.httpClient = vertx.createHttpClient(); - this.host = host; - this.port = port; + this.networkConfiguration = network; } public void handle(final RoutingContext ctx) { @@ -33,7 +31,8 @@ public void handle(final RoutingContext ctx) { request.toWebSocket(r -> { if (r.succeeded()) { final String forwardUri = request.uri(); - LOG.debugf("Quinoa Dev WebSocket Server Connected: %s:%s%s", host, port, forwardUri); + LOG.debugf("Quinoa Dev WebSocket Server Connected: %s:%s%s", networkConfiguration.getHost(), + networkConfiguration.getPort(), forwardUri); final ServerWebSocket serverWs = r.result(); final AtomicReference clientWs = new AtomicReference<>(); serverWs @@ -58,8 +57,8 @@ public void handle(final RoutingContext ctx) { } final WebSocketConnectOptions options = new WebSocketConnectOptions() - .setHost(host) - .setPort(port) + .setHost(networkConfiguration.getHost()) + .setPort(networkConfiguration.getPort()) .setURI(forwardUri) .setHeaders(serverWs.headers()) .setSubProtocols(subProtocols) @@ -68,7 +67,8 @@ public void handle(final RoutingContext ctx) { httpClient.webSocket(options, clientContext -> { if (clientContext.succeeded()) { - LOG.infof("Quinoa Dev WebSocket Client Connected: %s:%s%s", host, port, forwardUri); + LOG.infof("Quinoa Dev WebSocket Client Connected: %s:%s%s", networkConfiguration.getHost(), + networkConfiguration.getPort(), forwardUri); clientWs.set(clientContext.result()); // messages from NodeJS forwarded back to browser clientWs.get().exceptionHandler( @@ -99,4 +99,4 @@ public void handle(final RoutingContext ctx) { }); } -} +} \ No newline at end of file diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaNetworkConfiguration.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaNetworkConfiguration.java new file mode 100644 index 00000000..89ddc8f0 --- /dev/null +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaNetworkConfiguration.java @@ -0,0 +1,131 @@ +package io.quarkiverse.quinoa; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Represents the network configuration settings for Quinoa, + * including TLS options, host, port, and websocket settings. + */ +public class QuinoaNetworkConfiguration { + + private final boolean tls; + private final boolean tlsAllowInsecure; + private String host; + private Integer port; + private final boolean websocket; + + /** + * Constructs a new {@code QuinoaNetworkConfiguration} with the specified settings. + * + * @param tls whether TLS is enabled + * @param tlsAllowInsecure whether insecure TLS connections are allowed + * @param host the hostname or IP address of the server + * @param port the port number on which the server is listening + * @param websocket whether websocket is enabled + */ + public QuinoaNetworkConfiguration(boolean tls, boolean tlsAllowInsecure, String host, Integer port, boolean websocket) { + this.tls = tls; + this.tlsAllowInsecure = tlsAllowInsecure; + this.host = host; + this.port = port; + this.websocket = websocket; + } + + /** + * Returns whether TLS is enabled. + * + * @return {@code true} if TLS is enabled, {@code false} otherwise + */ + public boolean isTls() { + return tls; + } + + /** + * Returns whether insecure TLS connections are allowed. + * + * @return {@code true} if insecure TLS connections are allowed, {@code false} otherwise + */ + public boolean isTlsAllowInsecure() { + return tlsAllowInsecure; + } + + /** + * Returns the hostname or IP address of the server. + * + * @return the host + */ + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + /** + * Returns the port number on which the server is listening. + * + * @return the port number + */ + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + /** + * Returns whether websocket is enabled. + * + * @return {@code true} if websocket is enabled, {@code false} otherwise + */ + public boolean isWebsocket() { + return websocket; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param o the reference object with which to compare + * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + QuinoaNetworkConfiguration that = (QuinoaNetworkConfiguration) o; + return isTls() == that.isTls() && isTlsAllowInsecure() == that.isTlsAllowInsecure() + && isWebsocket() == that.isWebsocket() && Objects.equals(getHost(), that.getHost()) + && Objects.equals(getPort(), that.getPort()); + } + + /** + * Returns a hash code value for the object. + * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + return Objects.hash(isTls(), isTlsAllowInsecure(), getHost(), getPort(), isWebsocket()); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object + */ + @Override + public String toString() { + return new StringJoiner(", ", QuinoaNetworkConfiguration.class.getSimpleName() + "[", "]") + .add("tls=" + tls) + .add("tlsAllowInsecure=" + tlsAllowInsecure) + .add("host='" + host + "'") + .add("port=" + port) + .add("websocket=" + websocket) + .toString(); + } +} \ No newline at end of file diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java index 07cb3c9b..c11a2e11 100644 --- a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java @@ -26,12 +26,12 @@ public class QuinoaRecorder { public static final Set HANDLED_METHODS = Set.of(HttpMethod.HEAD, HttpMethod.OPTIONS, HttpMethod.GET); public Handler quinoaProxyDevHandler(final QuinoaDevProxyHandlerConfig handlerConfig, Supplier vertx, - boolean tls, boolean tlsAllowInsecure, String host, int port, boolean websocket) { + QuinoaNetworkConfiguration network) { if (LOG.isDebugEnabled()) { LOG.debugf("Quinoa dev proxy-handler is ignoring paths starting with: " + String.join(", ", handlerConfig.ignoredPathPrefixes)); } - return new QuinoaDevProxyHandler(handlerConfig, vertx.get(), tls, tlsAllowInsecure, host, port, websocket); + return new QuinoaDevProxyHandler(handlerConfig, vertx.get(), network); } public Handler quinoaSPARoutingHandler(List ignoredPathPrefixes) throws IOException { @@ -85,4 +85,4 @@ static void next(ClassLoader cl, RoutingContext ctx) { ctx.next(); } -} +} \ No newline at end of file From 8d93b4fc452b99ba31651a3bf65b126bdca7c274 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 15 Jun 2024 09:18:12 -0400 Subject: [PATCH 02/13] docs: add liquidnya as a contributor for code, and ideas (#695) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 10 ++++++++++ README.md | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 5cb8b7de..11672643 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -206,6 +206,16 @@ "contributions": [ "code" ] + }, + { + "login": "liquidnya", + "name": "Alice", + "avatar_url": "https://avatars.githubusercontent.com/u/7364785?v=4", + "profile": "http://meow.liquidnya.art", + "contributions": [ + "code", + "ideas" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index e2c9f0e9..98e6c1d5 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Project Map](https://sourcespy.com/shield.svg)](https://sourcespy.com/github/quarkiversequarkusquinoa/) -[![All Contributors](https://img.shields.io/badge/all_contributors-22-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-23-orange.svg?style=flat-square)](#contributors-) Quinoa is a Quarkus extension which eases the development, the build and serving of single page apps or web components (built with [npm](https://docs.npmjs.com/) : [React](https://react.dev/learn), [Angular](https://angular.io/guide/what-is-angular), [Vue](https://vuejs.org/guide/introduction.html), [Lit](https://lit.dev/), [Svelte](https://svelte.dev/docs/introduction), [Astro](https://docs.astro.build/en/getting-started/), [SolidJS](https://www.solidjs.com/guides/getting-started) …) alongside [Quarkus](https://quarkus.io/). It is possible to use it with a Quarkus backend in a single project. @@ -117,6 +117,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Yann Le Moigne
Yann Le Moigne

💻 + Alice
Alice

💻 🤔 From e0fc4bd33d85972605ba9c410badfc7cbde3fd03 Mon Sep 17 00:00:00 2001 From: Melloware Date: Mon, 17 Jun 2024 10:12:55 -0400 Subject: [PATCH 03/13] Fix static imports (#697) --- .../quarkiverse/quinoa/deployment/QuinoaProcessor.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java index a933b63d..f38c15ee 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java @@ -1,7 +1,9 @@ package io.quarkiverse.quinoa.deployment; import static io.quarkiverse.quinoa.QuinoaRecorder.QUINOA_SPA_ROUTE_ORDER; -import static io.quarkiverse.quinoa.deployment.config.QuinoaConfig.*; +import static io.quarkiverse.quinoa.deployment.config.QuinoaConfig.getNormalizedIgnoredPathPrefixes; +import static io.quarkiverse.quinoa.deployment.config.QuinoaConfig.isDevServerMode; +import static io.quarkiverse.quinoa.deployment.config.QuinoaConfig.isEnabled; import static io.quarkiverse.quinoa.deployment.framework.FrameworkType.overrideConfig; import static io.quarkiverse.quinoa.deployment.packagemanager.PackageManagerRunner.autoDetectPackageManager; import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; @@ -219,7 +221,7 @@ void watchChanges( public void produceGeneratedStaticResources( ConfiguredQuinoaBuildItem configuredQuinoa, BuildProducer generatedStaticResourceProducer, - Optional uiResources) throws IOException { + Optional uiResources) { if (configuredQuinoa != null && configuredQuinoa.resolvedConfig().justBuild()) { LOG.info("Quinoa is in build only mode"); return; @@ -243,7 +245,7 @@ public void runtimeInit( return; } if (uiResources.isPresent() && !uiResources.get().resources().isEmpty()) { - if (configuredQuinoa.resolvedConfig().enableSPARouting()) { + if (Objects.requireNonNull(configuredQuinoa).resolvedConfig().enableSPARouting()) { routes.produce(RouteBuildItem.builder().orderedRoute("/*", QUINOA_SPA_ROUTE_ORDER) .handler(recorder .quinoaSPARoutingHandler(getNormalizedIgnoredPathPrefixes(configuredQuinoa.resolvedConfig()))) @@ -425,4 +427,4 @@ public Path getUIDir() { } } -} +} \ No newline at end of file From 4eb6dc7dd1f674d00aad579101a8d9334ded0a36 Mon Sep 17 00:00:00 2001 From: Alice <7364785+liquidnya@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:53:40 +0200 Subject: [PATCH 04/13] Add root path for Quinoa Web UI (#691) * Add quarkus.quinoa.ui-root-path This adds the quarkus.quinoa.ui-root-path property which is the path for hosting the Web UI. The quarkus.quinoa.ignored-path-prefixes property is always relative to quarkus.quinoa.ui-root-path. The quarkus.http.non-application-root-path is not added to the default ignores if it is not relative to quarkus.http.root-path. * Add tests for ignored paths * Add integration test for quarkus.quinoa.ui-root-path * Add trailing slash to ui root path log * Fix ignored paths --------- Co-authored-by: Melloware --- .../deployment/ForwardedDevProcessor.java | 14 ++- .../quinoa/deployment/QuinoaProcessor.java | 19 +++- .../deployment/config/QuinoaConfig.java | 101 ++++++++++++++--- .../config/delegate/QuinoaConfigDelegate.java | 5 + ...refixesRESTConfigRelativeRootPathTest.java | 42 +++++++ ...noaPathPrefixesRESTConfigRootPathTest.java | 42 +++++++ .../QuinoaPathPrefixesRESTConfigTest.java | 4 +- ...aPathPrefixesRESTConfigUiRootPathTest.java | 41 +++++++ .../ROOT/pages/includes/quarkus-quinoa.adoc | 21 +++- .../src/main/resources/application.properties | 23 ++-- .../src/main/ui-lit/src/simple-greeting.js | 4 +- .../src/main/ui-lit/webpack.config.js | 2 +- .../quinoa/it/QuinoaUiRootPathTest.java | 106 ++++++++++++++++++ .../quarkiverse/quinoa/it/TestProfiles.java | 7 ++ .../io/quarkiverse/quinoa/QuinoaRecorder.java | 35 +++++- .../quinoa/QuinoaSPARoutingHandler.java | 12 +- 16 files changed, 434 insertions(+), 44 deletions(-) create mode 100644 deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRelativeRootPathTest.java create mode 100644 deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRootPathTest.java create mode 100644 deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigUiRootPathTest.java create mode 100644 integration-tests/src/test/java/io/quarkiverse/quinoa/it/QuinoaUiRootPathTest.java diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java index c52168e2..f6f07986 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/ForwardedDevProcessor.java @@ -50,6 +50,8 @@ import io.quarkus.resteasy.reactive.server.spi.ResumeOn404BuildItem; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; +import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.deployment.WebsocketSubProtocolsBuildItem; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; @@ -189,6 +191,8 @@ public void runtimeInit( Optional devProxy, Optional configuredQuinoa, CoreVertxBuildItem vertx, + HttpRootPathBuildItem httpRootPath, + NonApplicationRootPathBuildItem nonApplicationRootPath, BuildProducer routes, BuildProducer websocketSubProtocols, BuildProducer resumeOn404) throws IOException { @@ -200,12 +204,16 @@ public void runtimeInit( return; } LOG.infof("Quinoa is forwarding unhandled requests to port: %d", devProxy.get().getPort()); - final QuinoaDevProxyHandlerConfig handlerConfig = toDevProxyHandlerConfig(quinoaConfig, httpBuildTimeConfig); + final QuinoaDevProxyHandlerConfig handlerConfig = toDevProxyHandlerConfig(quinoaConfig, httpBuildTimeConfig, + nonApplicationRootPath); + String uiRootPath = QuinoaConfig.getNormalizedUiRootPath(quinoaConfig); + recorder.logUiRootPath(httpRootPath.relativePath(uiRootPath)); final QuinoaNetworkConfiguration networkConfig = new QuinoaNetworkConfiguration(devProxy.get().isTls(), devProxy.get().isTlsAllowInsecure(), devProxy.get().getHost(), devProxy.get().getPort(), quinoaConfig.devServer().websocket()); - routes.produce(RouteBuildItem.builder().orderedRoute("/*", QUINOA_ROUTE_ORDER) + // note that the uiRootPath is resolved relative to 'quarkus.http.root-path' by the RouteBuildItem + routes.produce(RouteBuildItem.builder().orderedRoute(uiRootPath + "*", QUINOA_ROUTE_ORDER) .handler(recorder.quinoaProxyDevHandler(handlerConfig, vertx.getVertx(), networkConfig)) .build()); if (quinoaConfig.devServer().websocket()) { @@ -213,7 +221,7 @@ public void runtimeInit( } if (quinoaConfig.enableSPARouting()) { resumeOn404.produce(new ResumeOn404BuildItem()); - routes.produce(RouteBuildItem.builder().orderedRoute("/*", QUINOA_SPA_ROUTE_ORDER) + routes.produce(RouteBuildItem.builder().orderedRoute(uiRootPath + "*", QUINOA_SPA_ROUTE_ORDER) .handler(recorder.quinoaSPARoutingHandler(handlerConfig.ignoredPathPrefixes)) .build()); } diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java index f38c15ee..49c4b968 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/QuinoaProcessor.java @@ -50,6 +50,8 @@ import io.quarkus.deployment.util.FileUtil; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.deployment.spi.GeneratedStaticResourceBuildItem; @@ -227,9 +229,13 @@ public void produceGeneratedStaticResources( return; } if (uiResources.isPresent() && !uiResources.get().resources().isEmpty()) { + String uiRootPath = QuinoaConfig.getNormalizedUiRootPath(configuredQuinoa.resolvedConfig()); for (BuiltResourcesBuildItem.BuiltResource resource : uiResources.get().resources()) { + // note how uiRootPath always starts and ends in a slash + // and resource.name() always starts in a slash, therfore resource.name().substring(1) never starts in a slash generatedStaticResourceProducer - .produce(new GeneratedStaticResourceBuildItem(resource.name(), resource.content())); + .produce(new GeneratedStaticResourceBuildItem(uiRootPath + resource.name().substring(1), + resource.content())); } } } @@ -238,6 +244,8 @@ public void produceGeneratedStaticResources( @Record(RUNTIME_INIT) public void runtimeInit( ConfiguredQuinoaBuildItem configuredQuinoa, + HttpRootPathBuildItem httpRootPath, + NonApplicationRootPathBuildItem nonApplicationRootPath, QuinoaRecorder recorder, BuildProducer routes, Optional uiResources) throws IOException { @@ -245,10 +253,15 @@ public void runtimeInit( return; } if (uiResources.isPresent() && !uiResources.get().resources().isEmpty()) { + String uiRootPath = QuinoaConfig.getNormalizedUiRootPath(configuredQuinoa.resolvedConfig()); + // the resolvedUiRootPath is only used for logging + String resolvedUiRootPath = httpRootPath.relativePath(uiRootPath); + recorder.logUiRootPath(resolvedUiRootPath.endsWith("/") ? resolvedUiRootPath : resolvedUiRootPath + "/"); if (Objects.requireNonNull(configuredQuinoa).resolvedConfig().enableSPARouting()) { - routes.produce(RouteBuildItem.builder().orderedRoute("/*", QUINOA_SPA_ROUTE_ORDER) + routes.produce(RouteBuildItem.builder().orderedRoute(uiRootPath + "*", QUINOA_SPA_ROUTE_ORDER) .handler(recorder - .quinoaSPARoutingHandler(getNormalizedIgnoredPathPrefixes(configuredQuinoa.resolvedConfig()))) + .quinoaSPARoutingHandler(getNormalizedIgnoredPathPrefixes(configuredQuinoa.resolvedConfig(), + nonApplicationRootPath))) .build()); } } diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/QuinoaConfig.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/QuinoaConfig.java index 0d0c0431..be466d23 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/QuinoaConfig.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/QuinoaConfig.java @@ -1,20 +1,21 @@ package io.quarkiverse.quinoa.deployment.config; -import static java.util.stream.Collectors.toList; - import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import io.quarkiverse.quinoa.QuinoaDevProxyHandlerConfig; +import io.quarkus.deployment.util.UriNormalizationUtil; import io.quarkus.runtime.annotations.ConfigDocDefault; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; @@ -26,6 +27,7 @@ public interface QuinoaConfig { String DEFAULT_BUILD_DIR = "build/"; + String DEFAULT_WEB_UI_ROOT_PATH = "/"; String DEFAULT_WEB_UI_DIR = "src/main/webui"; String DEFAULT_INDEX_PAGE = "index.html"; @@ -46,6 +48,13 @@ public interface QuinoaConfig { @WithDefault("false") boolean justBuild(); + /** + * Root path for hosting the Web UI. + * This path is normalized and always resolved relative to 'quarkus.http.root-path'. + */ + @WithDefault(DEFAULT_WEB_UI_ROOT_PATH) + String uiRootPath(); + /** * Path to the Web UI (NodeJS) root directory (relative to the project root). */ @@ -115,8 +124,9 @@ public interface QuinoaConfig { /** * List of path prefixes to be ignored by Quinoa (SPA Handler and Dev-Proxy). + * The paths are normalized and always resolved relative to 'quarkus.quinoa.ui-root-path'. */ - @ConfigDocDefault("ignore values configured by 'quarkus.resteasy-reactive.path', 'quarkus.resteasy.path' and 'quarkus.http.non-application-root-path'") + @ConfigDocDefault("ignore values configured by 'quarkus.resteasy-reactive.path', 'quarkus.rest.path', 'quarkus.resteasy.path' and 'quarkus.http.non-application-root-path'") Optional> ignoredPathPrefixes(); /** @@ -124,30 +134,84 @@ public interface QuinoaConfig { */ DevServerConfig devServer(); - static List getNormalizedIgnoredPathPrefixes(QuinoaConfig config) { - return config.ignoredPathPrefixes().orElseGet(() -> { - Config allConfig = ConfigProvider.getConfig(); - List defaultIgnore = new ArrayList<>(); - readExternalConfigPath(allConfig, "quarkus.resteasy.path").ifPresent(defaultIgnore::add); - readExternalConfigPath(allConfig, "quarkus.rest.path").ifPresent(defaultIgnore::add); - readExternalConfigPath(allConfig, "quarkus.resteasy-reactive.path").ifPresent(defaultIgnore::add); - readExternalConfigPath(allConfig, "quarkus.http.non-application-root-path").ifPresent(defaultIgnore::add); - return defaultIgnore; - }).stream().map(s -> s.startsWith("/") ? s : "/" + s).collect(toList()); + static List getNormalizedIgnoredPathPrefixes(QuinoaConfig config, + NonApplicationRootPathBuildItem nonApplicationRootPath) { + return config.ignoredPathPrefixes() + .map(list -> list.stream() + .map(s -> normalizePath(s, false)) + .collect(Collectors.toList())) + .orElseGet(() -> { + Config allConfig = ConfigProvider.getConfig(); + List defaultIgnore = new ArrayList<>(); + String uiRootPath = getNormalizedUiRootPath(config); + // note that quarkus.resteasy.path and quarkus.resteasy-reactive.path are always relative to the http root path + readExternalConfigPath(uiRootPath, allConfig, "quarkus.resteasy.path").ifPresent(defaultIgnore::add); + readExternalConfigPath(uiRootPath, allConfig, "quarkus.rest.path").ifPresent(defaultIgnore::add); + readExternalConfigPath(uiRootPath, allConfig, "quarkus.resteasy-reactive.path") + .ifPresent(defaultIgnore::add); + // the non-application root path is not always relative to the http root path + convertNonApplicationRootPath(uiRootPath, nonApplicationRootPath).ifPresent(defaultIgnore::add); + return defaultIgnore; + }); } static QuinoaDevProxyHandlerConfig toDevProxyHandlerConfig(final QuinoaConfig config, - final HttpBuildTimeConfig httpBuildTimeConfig) { + final HttpBuildTimeConfig httpBuildTimeConfig, final NonApplicationRootPathBuildItem nonApplicationRootPath) { final Set compressMediaTypes = httpBuildTimeConfig.compressMediaTypes.map(Set::copyOf).orElse(Set.of()); - return new QuinoaDevProxyHandlerConfig(getNormalizedIgnoredPathPrefixes(config), + return new QuinoaDevProxyHandlerConfig(getNormalizedIgnoredPathPrefixes(config, nonApplicationRootPath), config.devServer().indexPage().orElse(DEFAULT_INDEX_PAGE), httpBuildTimeConfig.enableCompression, compressMediaTypes, config.devServer().directForwarding()); } - private static Optional readExternalConfigPath(Config config, String key) { + /** + *

+ * Normalizes the {@link QuinoaConfig#uiRootPath()} and the returned path always starts with {@code "/"} and ends with + * {@code "/"}. + *

+ * Note that this will not resolve the path relative to 'quarkus.http.root-path'. + */ + static String getNormalizedUiRootPath(QuinoaConfig config) { + return normalizePath(config.uiRootPath(), true); + } + + /** + * Normalizes the path and the returned path starts with a slash and if {@code trailingSlash} is set to {@code true} then it + * will also end in a slash. + */ + private static String normalizePath(String path, boolean trailingSlash) { + String normalizedPath = UriNormalizationUtil.toURI(path, trailingSlash).getPath(); + return normalizedPath.startsWith("/") ? normalizedPath : "/" + normalizedPath; + } + + /** + * Note that {@code rootPath} and {@code leafPath} are required to start and end in a slash. + * The returned path also fulfills this requirement. + */ + private static Optional relativizePath(String rootPath, String leafPath) { + return Optional.ofNullable(UriNormalizationUtil.relativize(rootPath, leafPath)) + // note that relativize always removes the leading slash + .map(s -> "/" + s); + } + + private static Optional readExternalConfigPath(String uiRootPath, Config config, String key) { return config.getOptionalValue(key, String.class) + .map(s -> normalizePath(s, true)) + // only add this path if it is relative to the ui-root-path + .flatMap(s -> relativizePath(uiRootPath, s)) .filter(s -> !Objects.equals(s, "/")) - .map(s -> s.endsWith("/") ? s : s + "/"); + .map(s -> s.endsWith("/") ? s.substring(0, s.length() - 1) : s); + } + + private static Optional convertNonApplicationRootPath(String uiRootPath, + NonApplicationRootPathBuildItem nonApplicationRootPath) { + // only add the non-application root path if it is relative to the http root path + // note that both paths start and end in a slash already + return relativizePath(nonApplicationRootPath.getNormalizedHttpRootPath(), + nonApplicationRootPath.getNonApplicationRootPath()) + // and also only add this path if it is relative to the ui-root-path + .flatMap(s -> relativizePath(uiRootPath, s)) + .filter(s -> !Objects.equals(s, "/")) + .map(s -> s.endsWith("/") ? s.substring(0, s.length() - 1) : s); } static boolean isDevServerMode(QuinoaConfig config) { @@ -165,6 +229,9 @@ static boolean isEqual(QuinoaConfig q1, QuinoaConfig q2) { if (!Objects.equals(q1.justBuild(), q2.justBuild())) { return false; } + if (!Objects.equals(q1.uiRootPath(), q2.uiRootPath())) { + return false; + } if (!Objects.equals(q1.uiDir(), q2.uiDir())) { return false; } diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/delegate/QuinoaConfigDelegate.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/delegate/QuinoaConfigDelegate.java index b7b3dfbf..1b52393a 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/delegate/QuinoaConfigDelegate.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/config/delegate/QuinoaConfigDelegate.java @@ -26,6 +26,11 @@ public boolean justBuild() { return delegate.justBuild(); } + @Override + public String uiRootPath() { + return delegate.uiRootPath(); + } + @Override public String uiDir() { return delegate.uiDir(); diff --git a/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRelativeRootPathTest.java b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRelativeRootPathTest.java new file mode 100644 index 00000000..f1d0efc8 --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRelativeRootPathTest.java @@ -0,0 +1,42 @@ +package io.quarkiverse.quinoa.test; + +import static io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest.getWebUITestDirPath; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest; +import io.quarkus.test.QuarkusUnitTest; + +public class QuinoaPathPrefixesRESTConfigRelativeRootPathTest { + + private static final String NAME = "resteasy-reactive-path-config-relative-root-path"; + + @RegisterExtension + static final QuarkusUnitTest config = QuinoaQuarkusUnitTest.create(NAME) + .toQuarkusUnitTest() + .overrideConfigKey("quarkus.http.root-path", "root/path") + .overrideConfigKey("quarkus.rest.path", "foo/reactive") + .overrideConfigKey("quarkus.resteasy.path", "foo/classic") + .overrideConfigKey("quarkus.http.non-application-root-path", "bar/non") + .overrideConfigKey("quarkus.quinoa.enable-spa-routing", "true") + .assertLogRecords(l -> assertThat(l) + .anyMatch(s -> s.getMessage() + // note how /bar/non is part of the ignored paths + // this is because bar/non is relative to the root path when it does not start with a slash + // also note that quarkus.rest.path, and quarkus.resteasy.path are always relative to the root path even if they start with a slash + .equals("Quinoa SPA routing handler is ignoring paths starting with: /foo/classic, /foo/reactive, /bar/non")) + .anyMatch(s -> s.getMessage() + .equals("Quinoa is available at: /root/path/"))); + + @Test + public void testQuinoa() { + assertThat(Path.of("target/quinoa/build/index.html")).isRegularFile() + .hasContent("test"); + assertThat(getWebUITestDirPath(NAME).resolve("node_modules/installed")).isRegularFile() + .hasContent("hello"); + } +} diff --git a/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRootPathTest.java b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRootPathTest.java new file mode 100644 index 00000000..8b5ed2e2 --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigRootPathTest.java @@ -0,0 +1,42 @@ +package io.quarkiverse.quinoa.test; + +import static io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest.getWebUITestDirPath; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest; +import io.quarkus.test.QuarkusUnitTest; + +public class QuinoaPathPrefixesRESTConfigRootPathTest { + + private static final String NAME = "resteasy-reactive-path-config-root-path"; + + @RegisterExtension + static final QuarkusUnitTest config = QuinoaQuarkusUnitTest.create(NAME) + .toQuarkusUnitTest() + .overrideConfigKey("quarkus.http.root-path", "/root/path") + .overrideConfigKey("quarkus.rest.path", "/foo/reactive") + .overrideConfigKey("quarkus.resteasy.path", "/foo/classic") + .overrideConfigKey("quarkus.http.non-application-root-path", "/bar/non") + .overrideConfigKey("quarkus.quinoa.enable-spa-routing", "true") + .assertLogRecords(l -> assertThat(l) + .anyMatch(s -> s.getMessage() + // note how /bar/non is not part of the ignored paths + // this is because /bar/non is not relative to /root/path + // also note that quarkus.rest.path, and quarkus.resteasy.path are always relative to the root path even if they start with a slash + .equals("Quinoa SPA routing handler is ignoring paths starting with: /foo/classic, /foo/reactive")) + .anyMatch(s -> s.getMessage() + .equals("Quinoa is available at: /root/path/"))); + + @Test + public void testQuinoa() { + assertThat(Path.of("target/quinoa/build/index.html")).isRegularFile() + .hasContent("test"); + assertThat(getWebUITestDirPath(NAME).resolve("node_modules/installed")).isRegularFile() + .hasContent("hello"); + } +} diff --git a/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigTest.java b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigTest.java index 77bee2b0..f679a595 100644 --- a/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigTest.java +++ b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigTest.java @@ -24,7 +24,9 @@ public class QuinoaPathPrefixesRESTConfigTest { .overrideConfigKey("quarkus.quinoa.enable-spa-routing", "true") .assertLogRecords(l -> assertThat(l) .anyMatch(s -> s.getMessage() - .equals("Quinoa SPA routing handler is ignoring paths starting with: /foo/classic/, /foo/reactive/, /bar/non/"))); + .equals("Quinoa SPA routing handler is ignoring paths starting with: /foo/classic, /foo/reactive, /bar/non")) + .anyMatch(s -> s.getMessage() + .equals("Quinoa is available at: /"))); @Test public void testQuinoa() { diff --git a/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigUiRootPathTest.java b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigUiRootPathTest.java new file mode 100644 index 00000000..8a6b3f9b --- /dev/null +++ b/deployment/src/test/java/io/quarkiverse/quinoa/test/QuinoaPathPrefixesRESTConfigUiRootPathTest.java @@ -0,0 +1,41 @@ +package io.quarkiverse.quinoa.test; + +import static io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest.getWebUITestDirPath; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.quinoa.deployment.testing.QuinoaQuarkusUnitTest; +import io.quarkus.test.QuarkusUnitTest; + +public class QuinoaPathPrefixesRESTConfigUiRootPathTest { + + private static final String NAME = "resteasy-reactive-path-config-ui-root-path"; + + @RegisterExtension + static final QuarkusUnitTest config = QuinoaQuarkusUnitTest.create(NAME) + .toQuarkusUnitTest() + .overrideConfigKey("quarkus.http.root-path", "/root/path") + .overrideConfigKey("quarkus.quinoa.ui-root-path", "/foo") + .overrideConfigKey("quarkus.rest.path", "/foo/reactive") + .overrideConfigKey("quarkus.resteasy.path", "/foo/classic") + .overrideConfigKey("quarkus.http.non-application-root-path", "/bar/non") + .overrideConfigKey("quarkus.quinoa.enable-spa-routing", "true") + .assertLogRecords(l -> assertThat(l) + .anyMatch(s -> s.getMessage() + // ignored paths are always relative to the ui root path + .equals("Quinoa SPA routing handler is ignoring paths starting with: /classic, /reactive")) + .anyMatch(s -> s.getMessage() + .equals("Quinoa is available at: /root/path/foo/"))); + + @Test + public void testQuinoa() { + assertThat(Path.of("target/quinoa/build/index.html")).isRegularFile() + .hasContent("test"); + assertThat(getWebUITestDirPath(NAME).resolve("node_modules/installed")).isRegularFile() + .hasContent("hello"); + } +} diff --git a/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc b/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc index 96c8f2e2..8fa9b470 100644 --- a/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc +++ b/docs/modules/ROOT/pages/includes/quarkus-quinoa.adoc @@ -44,6 +44,23 @@ endif::add-copy-button-to-env-var[] |`false` +a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-ui-root-path]]`link:#quarkus-quinoa_quarkus-quinoa-ui-root-path[quarkus.quinoa.ui-root-path]` + + +[.description] +-- +Root path for hosting the Web UI. This path is normalized and always resolved relative to 'quarkus.http.root-path'. + +ifdef::add-copy-button-to-env-var[] +Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_UI_ROOT_PATH+++[] +endif::add-copy-button-to-env-var[] +ifndef::add-copy-button-to-env-var[] +Environment variable: `+++QUARKUS_QUINOA_UI_ROOT_PATH+++` +endif::add-copy-button-to-env-var[] +--|string +|`/` + + a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-ui-dir]]`link:#quarkus-quinoa_quarkus-quinoa-ui-dir[quarkus.quinoa.ui-dir]` @@ -474,7 +491,7 @@ a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-ignored-p [.description] -- -List of path prefixes to be ignored by Quinoa (SPA Handler and Dev-Proxy). +List of path prefixes to be ignored by Quinoa (SPA Handler and Dev-Proxy). The paths are normalized and always resolved relative to 'quarkus.quinoa.ui-root-path'. ifdef::add-copy-button-to-env-var[] Environment variable: env_var_with_copy_button:+++QUARKUS_QUINOA_IGNORED_PATH_PREFIXES+++[] @@ -483,7 +500,7 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_QUINOA_IGNORED_PATH_PREFIXES+++` endif::add-copy-button-to-env-var[] --|list of string -|`ignore values configured by 'quarkus.resteasy-reactive.path', 'quarkus.resteasy.path' and 'quarkus.http.non-application-root-path'` +|`ignore values configured by 'quarkus.resteasy-reactive.path', 'quarkus.rest.path', 'quarkus.resteasy.path' and 'quarkus.http.non-application-root-path'` a|icon:lock[title=Fixed at build time] [[quarkus-quinoa_quarkus-quinoa-dev-server]]`link:#quarkus-quinoa_quarkus-quinoa-dev-server[quarkus.quinoa.dev-server]` diff --git a/integration-tests/src/main/resources/application.properties b/integration-tests/src/main/resources/application.properties index 326f1106..0ff9007c 100644 --- a/integration-tests/src/main/resources/application.properties +++ b/integration-tests/src/main/resources/application.properties @@ -1,6 +1,12 @@ -%lit-root-path.quarkus.http.root-path=/foo/bar -%lit-root-path.quarkus.quinoa.enable-spa-routing=true -%lit-root-path.quarkus.quinoa.package-manager-command.build-env.ROOT_PATH=/foo/bar/ +%lit-root-path,lit-ui-root-path.quarkus.http.root-path=/foo/bar +%lit-root-path,lit-ui-root-path.quarkus.quinoa.enable-spa-routing=true +%lit-root-path,lit-ui-root-path.quarkus.quinoa.package-manager-command.build-env.API_PATH=/foo/bar/api/ + +%lit-root-path.quarkus.quinoa.package-manager-command.build-env.ROOT_PATH=/foo/bar + +%lit-ui-root-path.quarkus.quinoa.ui-root-path=/ui +%lit-ui-root-path.quarkus.quinoa.package-manager-command.build-env.ROOT_PATH=/foo/bar/ui +%lit-ui-root-path.quarkus.quinoa.ignored-path-prefixes=/ignored quarkus.quinoa.ui-dir=src/main/ui-react %react.quarkus.quinoa.ui-dir=src/main/ui-react @@ -13,13 +19,14 @@ quarkus.quinoa.ui-dir=src/main/ui-react %angular.quarkus.quinoa.package-manager-install.install-dir=target/ %angular.quarkus.quinoa.package-manager-install.node-version=20.9.0 %angular.quarkus.quinoa.package-manager-install.yarn-version=1.22.19 -%lit,lit-root-path.quarkus.quinoa.ui-dir=src/main/ui-lit -%lit,lit-root-path.quarkus.quinoa.build-dir=dist -%lit,lit-root-path.quarkus.http.static-resources.index-page=app.html -%lit,lit-root-path.quarkus.quinoa.package-manager-command.build=run build-per-env -%lit-root-path.quarkus.quinoa.package-manager-command.build-env.FOO=bar +%lit,lit-root-path,lit-ui-root-path.quarkus.quinoa.ui-dir=src/main/ui-lit +%lit,lit-root-path,lit-ui-root-path.quarkus.quinoa.build-dir=dist +%lit,lit-root-path,lit-ui-root-path.quarkus.http.static-resources.index-page=app.html +%lit,lit-root-path,lit-ui-root-path.quarkus.quinoa.package-manager-command.build=run build-per-env +%lit-root-path,lit-ui-root-path.quarkus.quinoa.package-manager-command.build-env.FOO=bar %lit.quarkus.quinoa.package-manager-command.build-env.FOO=bar %lit.quarkus.quinoa.package-manager-command.build-env.ROOT_PATH=/ +%lit.quarkus.quinoa.package-manager-command.build-env.API_PATH=/api/ %yarn.quarkus.quinoa.package-manager=yarn %just-build.quarkus.quinoa.just-build=true diff --git a/integration-tests/src/main/ui-lit/src/simple-greeting.js b/integration-tests/src/main/ui-lit/src/simple-greeting.js index c75d91c1..79adab00 100644 --- a/integration-tests/src/main/ui-lit/src/simple-greeting.js +++ b/integration-tests/src/main/ui-lit/src/simple-greeting.js @@ -1,6 +1,6 @@ import {html, css, LitElement} from 'lit'; -const ROOT_PATH = process.env.ROOT_PATH; +const API_PATH = process.env.API_PATH; export class SimpleGreeting extends LitElement { static styles = css`p { color: blue }`; @@ -17,7 +17,7 @@ export class SimpleGreeting extends LitElement { render() { const xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", `${ROOT_PATH}api/quinoa`, false ); + xmlHttp.open( "GET", `${API_PATH}quinoa`, false ); xmlHttp.send( null ); const response = xmlHttp.responseText; return html`

${response} and ${this.name} and ${process.env.FOO}

`; diff --git a/integration-tests/src/main/ui-lit/webpack.config.js b/integration-tests/src/main/ui-lit/webpack.config.js index 48cc13f6..57806f33 100644 --- a/integration-tests/src/main/ui-lit/webpack.config.js +++ b/integration-tests/src/main/ui-lit/webpack.config.js @@ -12,6 +12,6 @@ module.exports = { { from: 'public' } ] }), - new EnvironmentPlugin(['FOO', 'ROOT_PATH']) + new EnvironmentPlugin(['FOO', 'ROOT_PATH', 'API_PATH']) ] }; \ No newline at end of file diff --git a/integration-tests/src/test/java/io/quarkiverse/quinoa/it/QuinoaUiRootPathTest.java b/integration-tests/src/test/java/io/quarkiverse/quinoa/it/QuinoaUiRootPathTest.java new file mode 100644 index 00000000..2f7b251b --- /dev/null +++ b/integration-tests/src/test/java/io/quarkiverse/quinoa/it/QuinoaUiRootPathTest.java @@ -0,0 +1,106 @@ +package io.quarkiverse.quinoa.it; + +import static io.restassured.RestAssured.given; + +import java.net.URL; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.microsoft.playwright.BrowserContext; +import com.microsoft.playwright.ElementHandle; +import com.microsoft.playwright.Page; +import com.microsoft.playwright.Response; + +import io.quarkiverse.playwright.InjectPlaywright; +import io.quarkiverse.playwright.WithPlaywright; +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(TestProfiles.UiRootPathTests.class) +@WithPlaywright +public class QuinoaUiRootPathTest { + + @InjectPlaywright + BrowserContext context; + + @TestHTTPResource("/") + URL url404Root; + + @TestHTTPResource("/ui/ignored") + URL url404Ignored; + + @TestHTTPResource("/ui/ignored/sub-path") + URL url404IgnoredSubPath; + + // this path starts with /ignored, but is not a sub path of /ignored + @TestHTTPResource("/ui/ignored-not-ignored") + URL urlNotIgnored; + + // note how the path "/ui" does not work in a production build + // note that "/ui" works in dev-mode as long as the nodejs server also hosts "/ui" + @TestHTTPResource("/ui/") + URL url; + + @TestHTTPResource("/ui/some-route") + URL urlRoute; + + @Test + public void test404Endpoints() { + given() + .when().get(url404Root) + .then() + .statusCode(404); + given() + .when().get(url404Ignored) + .then() + .statusCode(404); + given() + .when().get(url404IgnoredSubPath) + .then() + .statusCode(404); + } + + @Test + public void testUIIndex() { + final Page page = context.newPage(); + Response response = page.navigate(url.toString()); + Assertions.assertEquals("OK", response.statusText()); + + page.waitForLoadState(); + + String title = page.title(); + Assertions.assertEquals("Quinoa Lit App", title); + + // Make sure the component loaded and hits the backend + final ElementHandle quinoaEl = page.waitForSelector(".greeting"); + String greeting = quinoaEl.innerText(); + Assertions.assertEquals("Hello Quinoa and World and bar", greeting); + } + + @Test + public void testRoute() { + final Page page = context.newPage(); + Response response = page.navigate(urlRoute.toString()); + Assertions.assertEquals("OK", response.statusText()); + + page.waitForLoadState(); + + String title = page.title(); + Assertions.assertEquals("Quinoa Lit App", title); + } + + @Test + public void testNotIgnored() { + final Page page = context.newPage(); + Response response = page.navigate(urlNotIgnored.toString()); + Assertions.assertEquals("OK", response.statusText()); + + page.waitForLoadState(); + + String title = page.title(); + Assertions.assertEquals("Quinoa Lit App", title); + } +} diff --git a/integration-tests/src/test/java/io/quarkiverse/quinoa/it/TestProfiles.java b/integration-tests/src/test/java/io/quarkiverse/quinoa/it/TestProfiles.java index 0f512cd4..6a65d0e9 100644 --- a/integration-tests/src/test/java/io/quarkiverse/quinoa/it/TestProfiles.java +++ b/integration-tests/src/test/java/io/quarkiverse/quinoa/it/TestProfiles.java @@ -46,6 +46,13 @@ public String getConfigProfile() { } } + public static class UiRootPathTests extends QuinoaTestProfiles.Enable { + @Override + public String getConfigProfile() { + return "lit-ui-root-path"; + } + } + public static class VueTests extends QuinoaTestProfiles.Enable { @Override public String getConfigProfile() { diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java index c11a2e11..3027053b 100644 --- a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaRecorder.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Supplier; @@ -28,7 +29,7 @@ public class QuinoaRecorder { public Handler quinoaProxyDevHandler(final QuinoaDevProxyHandlerConfig handlerConfig, Supplier vertx, QuinoaNetworkConfiguration network) { if (LOG.isDebugEnabled()) { - LOG.debugf("Quinoa dev proxy-handler is ignoring paths starting with: " + LOG.debug("Quinoa dev proxy-handler is ignoring paths starting with: " + String.join(", ", handlerConfig.ignoredPathPrefixes)); } return new QuinoaDevProxyHandler(handlerConfig, vertx.get(), network); @@ -36,22 +37,46 @@ public Handler quinoaProxyDevHandler(final QuinoaDevProxyHandler public Handler quinoaSPARoutingHandler(List ignoredPathPrefixes) throws IOException { if (LOG.isDebugEnabled()) { - LOG.debugf("Quinoa SPA routing handler is ignoring paths starting with: " + String.join(", ", ignoredPathPrefixes)); + LOG.debug("Quinoa SPA routing handler is ignoring paths starting with: " + String.join(", ", ignoredPathPrefixes)); } return new QuinoaSPARoutingHandler(ignoredPathPrefixes); } + public void logUiRootPath(final String resolvedUiRootPath) { + if (LOG.isDebugEnabled()) { + LOG.debug("Quinoa is available at: " + resolvedUiRootPath); + } + } + static String resolvePath(RoutingContext ctx) { - return (ctx.mountPoint() == null) ? ctx.normalizedPath() + // quarkus.http.root-path + String path = (ctx.mountPoint() == null) ? ctx.normalizedPath() : ctx.normalizedPath().substring( // let's be extra careful here in case Vert.x normalizes the mount points at // some point ctx.mountPoint().endsWith("/") ? ctx.mountPoint().length() - 1 : ctx.mountPoint().length()); + // quarkus.quinoa.ui-root-path + String routePath = ctx.currentRoute().getPath(); + String resolvedPath = (routePath == null) ? path + : path.substring(routePath.endsWith("/") ? routePath.length() - 1 : routePath.length()); + // use "/" when the path is empty + // e.g. this happens when the request path is "/example" and the root path is "/example" + return resolvedPath.isEmpty() ? "/" : resolvedPath; + } + + static boolean matchesPathSeparatedPrefix(String path, String pathSeparatedPrefix) { + if (path.startsWith(pathSeparatedPrefix)) { + String restPath = path.substring(pathSeparatedPrefix.length()); + // the path matches the path separated prefix if the rest path is empty or starts with "/" + // note that the pathSeparatedPrefix never ends in "/" except if it equals "/" exactly + return restPath.isEmpty() || restPath.startsWith("/") || Objects.equals(pathSeparatedPrefix, "/"); + } + return false; } static boolean isIgnored(final String path, final List ignoredPathPrefixes) { - if (ignoredPathPrefixes.stream().anyMatch(path::startsWith)) { - LOG.debugf("Quinoa is ignoring path (quarkus.quinoa.ignored-path-prefixes): " + path); + if (ignoredPathPrefixes.stream().anyMatch(prefix -> matchesPathSeparatedPrefix(path, prefix))) { + LOG.debug("Quinoa is ignoring path (quarkus.quinoa.ignored-path-prefixes): " + path); return true; } return false; diff --git a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaSPARoutingHandler.java b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaSPARoutingHandler.java index ec42a562..d32f3720 100644 --- a/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaSPARoutingHandler.java +++ b/runtime/src/main/java/io/quarkiverse/quinoa/QuinoaSPARoutingHandler.java @@ -31,8 +31,16 @@ public void handle(RoutingContext ctx) { } String path = resolvePath(ctx); if (!Objects.equals(path, "/") && !isIgnored(path, ignoredPathPrefixes)) { - LOG.debugf("Quinoa is re-routing SPA request '%s' to '/'", ctx.normalizedPath()); - ctx.reroute(ctx.mountPoint() != null ? ctx.mountPoint() : "/"); + String mountPoint = ctx.mountPoint() != null ? ctx.mountPoint() : "/"; + String routePath = ctx.currentRoute().getPath() != null ? ctx.currentRoute().getPath() : "/"; + String target; + if (mountPoint.endsWith("/")) { + target = mountPoint.substring(0, mountPoint.length() - 1) + routePath; + } else { + target = mountPoint + routePath; + } + LOG.debugf("Quinoa is re-routing SPA request '%s' to '%s'", ctx.normalizedPath(), target); + ctx.reroute(target); } else { next(currentClassLoader, ctx); } From a6e0a72dc14b4439d3b951c192943227bce9f559 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 07:53:58 -0400 Subject: [PATCH 05/13] Bump ws from 7.5.9 to 7.5.10 in /integration-tests/src/main/ui-react (#700) Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../src/main/ui-react/package-lock.json | 26 +++++++++---------- integration-tests/src/main/ui-react/yarn.lock | 12 ++++----- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/integration-tests/src/main/ui-react/package-lock.json b/integration-tests/src/main/ui-react/package-lock.json index 4ba83937..381cb959 100644 --- a/integration-tests/src/main/ui-react/package-lock.json +++ b/integration-tests/src/main/ui-react/package-lock.json @@ -16043,15 +16043,15 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -16615,9 +16615,9 @@ } }, "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "engines": { "node": ">=8.3.0" }, @@ -28160,9 +28160,9 @@ } }, "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "requires": {} } } @@ -28619,9 +28619,9 @@ } }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "requires": {} }, "xml-name-validator": { diff --git a/integration-tests/src/main/ui-react/yarn.lock b/integration-tests/src/main/ui-react/yarn.lock index fa686640..816ba011 100644 --- a/integration-tests/src/main/ui-react/yarn.lock +++ b/integration-tests/src/main/ui-react/yarn.lock @@ -9599,14 +9599,14 @@ write-file-atomic@^3.0.0: typedarray-to-buffer "^3.1.5" ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.13.0: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xml-name-validator@^3.0.0: version "3.0.0" From d300c9aafff3dc88f328962358d5c84e303de0a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 08:06:15 -0400 Subject: [PATCH 06/13] Bump braces in /integration-tests/src/main/ui-angular (#701) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- integration-tests/src/main/ui-angular/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/integration-tests/src/main/ui-angular/yarn.lock b/integration-tests/src/main/ui-angular/yarn.lock index 24c161af..fa6579c7 100644 --- a/integration-tests/src/main/ui-angular/yarn.lock +++ b/integration-tests/src/main/ui-angular/yarn.lock @@ -2911,11 +2911,11 @@ __metadata: linkType: hard "braces@npm:^3.0.2, braces@npm:~3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: ^7.0.1 - checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459 + fill-range: ^7.1.1 + checksum: b95aa0b3bd909f6cd1720ffcf031aeaf46154dd88b4da01f9a1d3f7ea866a79eba76a6d01cbc3c422b2ee5cdc39a4f02491058d5df0d7bf6e6a162a832df1f69 languageName: node linkType: hard @@ -4455,12 +4455,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: ^5.0.1 - checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917 + checksum: b4abfbca3839a3d55e4ae5ec62e131e2e356bf4859ce8480c64c4876100f4df292a63e5bb1618e1d7460282ca2b305653064f01654474aa35c68000980f17798 languageName: node linkType: hard From 6d6a98ea9fdba3d4e56d5ba20b84ffb737aa7ba9 Mon Sep 17 00:00:00 2001 From: Melloware Date: Fri, 21 Jun 2024 12:47:40 -0400 Subject: [PATCH 07/13] Quarkus 3.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1072487..56276e33 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ 17 UTF-8 UTF-8 - 3.12.0.CR1 + 3.12.0 1.0.0 3.26.0 1.15.0 From 6c51569100c85c62b8d13e2c3f157fb950eea9b6 Mon Sep 17 00:00:00 2001 From: Melloware Date: Fri, 21 Jun 2024 12:48:24 -0400 Subject: [PATCH 08/13] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 98e6c1d5..e281d6f1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ There are multiple versions available please check which one for your Quarkus re | --- | --- | | ![1.x](https://img.shields.io/maven-central/v/io.quarkiverse.quinoa/quarkus-quinoa?versionPrefix=1.&color=cyan) | Quarkus 2 (legacy) | | ![2.2.x](https://img.shields.io/maven-central/v/io.quarkiverse.quinoa/quarkus-quinoa?versionPrefix=2.2&color=cyan) | Quarkus 3.2 LTS | -| ![latest](https://img.shields.io/maven-central/v/io.quarkiverse.quinoa/quarkus-quinoa?&color=cyan) | Quarkus 3.8 LTS | +| ![2.3.x](https://img.shields.io/maven-central/v/io.quarkiverse.quinoa/quarkus-quinoa?versionPrefix=2.3&color=cyan) | Quarkus 3.8 LTS | +| ![latest](https://img.shields.io/maven-central/v/io.quarkiverse.quinoa/quarkus-quinoa?&color=cyan) | Quarkus 3.12 | ## Getting started From c07691bb6bf37cec394c1826fe90a8ac27572dac Mon Sep 17 00:00:00 2001 From: Melloware Date: Fri, 21 Jun 2024 12:59:04 -0400 Subject: [PATCH 09/13] 2.4.0 (#703) --- .github/project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/project.yml b/.github/project.yml index ba5ac8f0..b2572e13 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -1,4 +1,4 @@ release: - current-version: 2.3.8 + current-version: 2.4.0 next-version: 999-SNAPSHOT From 5cda33154e4601bb934b6e9d5cd64118dd004994 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 21 Jun 2024 16:59:34 +0000 Subject: [PATCH 10/13] Update the latest release version 2.4.0 in documentation --- docs/modules/ROOT/pages/includes/attributes.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index ef1d3d1c..8af7864e 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,5 +1,5 @@ -:quarkus-version: 3.12.0.CR1 -:quarkus-quinoa-version: 2.3.8 +:quarkus-version: 3.12.0 +:quarkus-quinoa-version: 2.4.0 :maven-version: 3.8.1+ :extension-status: stable From 48b56ee65b02238ff04eced99ced73ce3f8d5252 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 21 Jun 2024 17:05:12 +0000 Subject: [PATCH 11/13] [maven-release-plugin] prepare release 2.4.0 --- deployment-testing/pom.xml | 2 +- deployment/pom.xml | 2 +- docs/pom.xml | 2 +- integration-tests/pom.xml | 2 +- pom.xml | 4 ++-- runtime/pom.xml | 2 +- testing/pom.xml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deployment-testing/pom.xml b/deployment-testing/pom.xml index 385e154c..060a0129 100644 --- a/deployment-testing/pom.xml +++ b/deployment-testing/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa-deployment-testing Quarkus - Quinoa - Deployment - Testing diff --git a/deployment/pom.xml b/deployment/pom.xml index 1ee1d962..960c72b4 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa-deployment Quarkus - Quinoa - Deployment diff --git a/docs/pom.xml b/docs/pom.xml index 0386c136..c291f6a6 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa-docs diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 14f79270..05019d1a 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa-integration-tests Quarkus - Quinoa - Integration Tests diff --git a/pom.xml b/pom.xml index 56276e33..1222e5dd 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 pom Quarkus - Quinoa - Parent Develop, build, and serve your npm-compatible web applications such as React, Angular, Vue, Lit, Svelte, Astro, SolidJS, and others alongside Quarkus. @@ -24,7 +24,7 @@ scm:git:git@github.com:quarkiverse/quarkus-quinoa.git scm:git:git@github.com:quarkiverse/quarkus-quinoa.git https://github.com/quarkiverse/quarkus-quinoa - HEAD + 2.4.0 diff --git a/runtime/pom.xml b/runtime/pom.xml index d3d0b09c..039866f8 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa Quarkus - Quinoa - Runtime diff --git a/testing/pom.xml b/testing/pom.xml index 757ed135..6878859d 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 999-SNAPSHOT + 2.4.0 quarkus-quinoa-testing Quarkus - Quinoa - Testing From 2a0ddc647358226481fa6aed22d99e01a13507e4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 21 Jun 2024 17:05:12 +0000 Subject: [PATCH 12/13] [maven-release-plugin] prepare for next development iteration --- deployment-testing/pom.xml | 2 +- deployment/pom.xml | 2 +- docs/pom.xml | 2 +- integration-tests/pom.xml | 2 +- pom.xml | 4 ++-- runtime/pom.xml | 2 +- testing/pom.xml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deployment-testing/pom.xml b/deployment-testing/pom.xml index 060a0129..385e154c 100644 --- a/deployment-testing/pom.xml +++ b/deployment-testing/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa-deployment-testing Quarkus - Quinoa - Deployment - Testing diff --git a/deployment/pom.xml b/deployment/pom.xml index 960c72b4..1ee1d962 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa-deployment Quarkus - Quinoa - Deployment diff --git a/docs/pom.xml b/docs/pom.xml index c291f6a6..0386c136 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa-docs diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 05019d1a..14f79270 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa-integration-tests Quarkus - Quinoa - Integration Tests diff --git a/pom.xml b/pom.xml index 1222e5dd..56276e33 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT pom Quarkus - Quinoa - Parent Develop, build, and serve your npm-compatible web applications such as React, Angular, Vue, Lit, Svelte, Astro, SolidJS, and others alongside Quarkus. @@ -24,7 +24,7 @@ scm:git:git@github.com:quarkiverse/quarkus-quinoa.git scm:git:git@github.com:quarkiverse/quarkus-quinoa.git https://github.com/quarkiverse/quarkus-quinoa - 2.4.0 + HEAD diff --git a/runtime/pom.xml b/runtime/pom.xml index 039866f8..d3d0b09c 100644 --- a/runtime/pom.xml +++ b/runtime/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa Quarkus - Quinoa - Runtime diff --git a/testing/pom.xml b/testing/pom.xml index 6878859d..757ed135 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -4,7 +4,7 @@ io.quarkiverse.quinoa quarkus-quinoa-parent - 2.4.0 + 999-SNAPSHOT quarkus-quinoa-testing Quarkus - Quinoa - Testing From 41ddbe28c785822facda643575dc138a24146c6a Mon Sep 17 00:00:00 2001 From: Melloware Date: Tue, 25 Jun 2024 09:37:06 -0400 Subject: [PATCH 13/13] Fix #705: Solid Start use vinxi dev also (#706) --- .../quarkiverse/quinoa/deployment/framework/FrameworkType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/framework/FrameworkType.java b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/framework/FrameworkType.java index 6ee20c83..8a48144d 100644 --- a/deployment/src/main/java/io/quarkiverse/quinoa/deployment/framework/FrameworkType.java +++ b/deployment/src/main/java/io/quarkiverse/quinoa/deployment/framework/FrameworkType.java @@ -40,7 +40,7 @@ public enum FrameworkType { REACT(Set.of("react-scripts start", "react-app-rewired start", "craco start"), new ReactFramework()), VUE_LEGACY(Set.of("vue-cli-service serve"), generic("dist", "serve", 3000)), VITE(Set.of("vite"), generic("dist", "dev", 5173)), - SOLID_START(Set.of("solid-start dev"), generic("dist", "dev", 3000)), + SOLID_START(Set.of("solid-start dev", "vinxi dev"), generic("dist", "dev", 3000)), ASTRO(Set.of("astro dev"), generic("dist", "dev", 3000)), NEXT(Set.of("next dev"), new NextFramework()), NUXT(Set.of("nuxt dev"), generic("dist", "dev", 3000)),