summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui-quarkus
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-ui-quarkus')
-rw-r--r--opendc-web/opendc-web-ui-quarkus/build.gradle.kts19
-rw-r--r--opendc-web/opendc-web-ui-quarkus/deployment/build.gradle.kts39
-rw-r--r--opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/AuthConfiguration.java52
-rw-r--r--opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiConfig.java63
-rw-r--r--opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiProcessor.java314
-rw-r--r--opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiRoutingBuildItem.java119
-rw-r--r--opendc-web/opendc-web-ui-quarkus/runtime/build.gradle.kts36
-rw-r--r--opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java (renamed from opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java)0
-rw-r--r--opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java (renamed from opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java)0
-rw-r--r--opendc-web/opendc-web-ui-quarkus/src/main/resources/META-INF/quarkus-extension.yaml (renamed from opendc-web/opendc-web-ui-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml)0
10 files changed, 19 insertions, 623 deletions
diff --git a/opendc-web/opendc-web-ui-quarkus/build.gradle.kts b/opendc-web/opendc-web-ui-quarkus/build.gradle.kts
index cbec021c..7f2fad20 100644
--- a/opendc-web/opendc-web-ui-quarkus/build.gradle.kts
+++ b/opendc-web/opendc-web-ui-quarkus/build.gradle.kts
@@ -21,3 +21,22 @@
*/
description = "Quarkus extension for serving OpenDC web interface"
+
+plugins {
+ `java-library-conventions`
+ id("io.quarkus.extension")
+}
+
+quarkusExtension {
+ deploymentModule = "opendc-web-ui-quarkus-deployment"
+}
+
+dependencies {
+ implementation(platform(libs.quarkus.bom))
+
+ implementation(libs.quarkus.core.runtime)
+ implementation(libs.quarkus.vertx.http.runtime)
+ implementation(libs.quarkus.arc.runtime)
+}
+
+evaluationDependsOn(projects.opendcWeb.opendcWebUiQuarkusDeployment.dependencyProject.path)
diff --git a/opendc-web/opendc-web-ui-quarkus/deployment/build.gradle.kts b/opendc-web/opendc-web-ui-quarkus/deployment/build.gradle.kts
deleted file mode 100644
index 0f3ae8ce..00000000
--- a/opendc-web/opendc-web-ui-quarkus/deployment/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-description = "Quarkus extension for serving OpenDC web interface"
-
-/* Build configuration */
-plugins {
- `java-library-conventions`
-}
-
-dependencies {
- implementation(enforcedPlatform(libs.quarkus.bom))
-
- implementation(projects.opendcWeb.opendcWebUi)
- implementation(projects.opendcWeb.opendcWebUiQuarkus.runtime)
-
- implementation(libs.quarkus.core.deployment)
- implementation(libs.quarkus.vertx.http.deployment)
- implementation(libs.quarkus.arc.deployment)
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/AuthConfiguration.java b/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/AuthConfiguration.java
deleted file mode 100644
index 2e4d9198..00000000
--- a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/AuthConfiguration.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.ui.deployment;
-
-import io.quarkus.runtime.annotations.ConfigGroup;
-import io.quarkus.runtime.annotations.ConfigItem;
-
-import java.util.Optional;
-
-/**
- * Auth configuration for the OpenDC UI extension.
- */
-@ConfigGroup
-public class AuthConfiguration {
- /**
- * The authentication domain.
- */
- @ConfigItem
- Optional<String> domain;
-
- /**
- * The client identifier used by the OpenDC web ui.
- */
- @ConfigItem
- Optional<String> clientId;
-
- /**
- * The audience of the OpenDC API.
- */
- @ConfigItem
- Optional<String> audience;
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiConfig.java b/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiConfig.java
deleted file mode 100644
index 50c1fbe3..00000000
--- a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiConfig.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.ui.deployment;
-
-import io.quarkus.runtime.annotations.ConfigItem;
-import io.quarkus.runtime.annotations.ConfigRoot;
-
-import java.util.Optional;
-
-/**
- * Build-time configuration for the OpenDC UI extension.
- */
-@ConfigRoot(name = "opendc-ui")
-public class OpenDCUiConfig {
- /**
- * A flag to include the OpenDC UI extension into the build.
- */
- @ConfigItem(defaultValue = "true")
- boolean include;
-
- /**
- * The path where the OpenDC UI is available.
- */
- @ConfigItem(defaultValue = "/")
- String path;
-
- /**
- * The base URL of the OpenDC API.
- */
- @ConfigItem(defaultValue = "/api")
- String apiBaseUrl;
-
- /**
- * Configuration properties for web UI authentication.
- */
- AuthConfiguration auth;
-
- /**
- * Sentry DSN.
- */
- @ConfigItem
- Optional<String> sentryDsn;
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiProcessor.java b/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiProcessor.java
deleted file mode 100644
index 983bb852..00000000
--- a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiProcessor.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.ui.deployment;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.quarkus.deployment.annotations.BuildProducer;
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.annotations.ExecutionTime;
-import io.quarkus.deployment.annotations.Record;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
-import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
-import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
-import io.quarkus.maven.dependency.GACT;
-import io.quarkus.maven.dependency.ResolvedDependency;
-import io.quarkus.paths.PathVisit;
-import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
-import io.quarkus.vertx.http.deployment.RouteBuildItem;
-import io.quarkus.vertx.http.deployment.webjar.WebJarBuildItem;
-import io.quarkus.vertx.http.deployment.webjar.WebJarResourcesFilter;
-import io.quarkus.vertx.http.deployment.webjar.WebJarResultsBuildItem;
-import io.vertx.core.Handler;
-import io.vertx.ext.web.RoutingContext;
-import org.opendc.web.ui.runtime.OpenDCUiRecorder;
-import org.opendc.web.ui.runtime.OpenDCUiRuntimeConfig;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.function.BooleanSupplier;
-import java.util.function.Function;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Build processor for the OpenDC web UI Quarkus extension.
- */
-public class OpenDCUiProcessor {
-
- private static final String FEATURE = "opendc-ui";
- private static final GACT OPENDC_UI_WEBJAR_ARTIFACT_KEY = new GACT("org.opendc", "opendc-web-ui", null, "jar");
- private static final String OPENDC_UI_WEBJAR_STATIC_RESOURCES_PATH = "META-INF/resources/opendc-web-ui";
- private static final Pattern PATH_PARAM_PATTERN = Pattern.compile("\\[(\\w+)]");
-
- private final ObjectMapper objectMapper = new ObjectMapper();
-
- /**
- * Provide the {@link FeatureBuildItem} for this Quarkus extension.
- */
- @BuildStep(onlyIf = IsIncluded.class)
- public FeatureBuildItem feature() {
- return new FeatureBuildItem(FEATURE);
- }
-
- /**
- * Build the WebJar that is used to serve the Next.js resources.
- */
- @BuildStep(onlyIf = IsIncluded.class)
- public WebJarBuildItem buildWebJar(OpenDCUiConfig config,
- HttpRootPathBuildItem httpRootPathBuildItem) {
- return WebJarBuildItem.builder()
- .artifactKey(OPENDC_UI_WEBJAR_ARTIFACT_KEY)
- .root(OPENDC_UI_WEBJAR_STATIC_RESOURCES_PATH)
- .onlyCopyNonArtifactFiles(false)
- .useDefaultQuarkusBranding(false)
- .filter(new InsertVariablesResourcesFilter(config, httpRootPathBuildItem))
- .build();
- }
-
-
- /**
- * Build the Next.js routes based on the route manifest generated by it.
- */
- @BuildStep(onlyIf = IsIncluded.class)
- public OpenDCUiRoutingBuildItem buildRoutes(CurateOutcomeBuildItem curateOutcomeBuildItem) throws IOException {
- ResolvedDependency dependency = getAppArtifact(curateOutcomeBuildItem, OPENDC_UI_WEBJAR_ARTIFACT_KEY);
- PathVisit visit = dependency.getContentTree().apply(OPENDC_UI_WEBJAR_STATIC_RESOURCES_PATH + "/routes-manifest.json", v -> v);
-
- if (visit == null) {
- throw new FileNotFoundException("Cannot find routes-manifest.json");
- }
-
- JsonNode routeManifest = objectMapper.readTree(visit.getUrl());
-
- var pages = new ArrayList<OpenDCUiRoutingBuildItem.Page>();
- for (Iterator<JsonNode> it = routeManifest.get("staticRoutes").elements(); it.hasNext();) {
- JsonNode route = it.next();
-
- String page = route.get("page").asText();
-
- // Static routes do not have any path parameters
- pages.add(new OpenDCUiRoutingBuildItem.Page(page, page));
- }
-
- for (Iterator<JsonNode> it = routeManifest.get("dynamicRoutes").elements(); it.hasNext();) {
- JsonNode route = it.next();
-
- String page = route.get("page").asText();
- String path = PATH_PARAM_PATTERN.matcher(page).replaceAll(r -> ":" + r.group(1));
-
- pages.add(new OpenDCUiRoutingBuildItem.Page(path, page));
- }
-
- var redirects = new ArrayList<OpenDCUiRoutingBuildItem.Redirect>();
- for (Iterator<JsonNode> it = routeManifest.get("redirects").elements(); it.hasNext();) {
- JsonNode redirect = it.next();
- if (redirect.has("internal")) {
- continue;
- }
-
- int statusCode = redirect.get("statusCode").asInt();
- String path = redirect.get("source").asText().replaceAll("/%%NEXT_BASE_PATH%%", "");
- String destination = redirect.get("destination").asText().replaceAll("/%%NEXT_BASE_PATH%%", "");
-
- if (path.isEmpty()) {
- path = "/";
- }
-
- redirects.add(new OpenDCUiRoutingBuildItem.Redirect(path, destination, statusCode));
- }
-
- var custom404 = routeManifest.get("pages404").asBoolean();
- return new OpenDCUiRoutingBuildItem(pages, redirects, custom404);
- }
-
- /**
- * Register the HTTP handles for the OpenDC web UI.
- */
- @BuildStep(onlyIf = IsIncluded.class)
- @Record(ExecutionTime.RUNTIME_INIT)
- public void registerOpenDCUiHandler(OpenDCUiRecorder recorder,
- BuildProducer<RouteBuildItem> routes,
- HttpRootPathBuildItem httpRootPathBuildItem,
- WebJarResultsBuildItem webJarResultsBuildItem,
- OpenDCUiRoutingBuildItem openDCUiBuildItem,
- OpenDCUiRuntimeConfig runtimeConfig,
- OpenDCUiConfig buildConfig,
- ShutdownContextBuildItem shutdownContext) {
-
- WebJarResultsBuildItem.WebJarResult result = webJarResultsBuildItem.byArtifactKey(OPENDC_UI_WEBJAR_ARTIFACT_KEY);
- if (result == null) {
- return;
- }
-
- String basePath = httpRootPathBuildItem.resolvePath(buildConfig.path);
- String finalDestination = result.getFinalDestination();
-
- /* Construct dynamic routes */
- for (var redirect : openDCUiBuildItem.getRedirects()) {
- String destination = basePath.equals("/") ? redirect.getDestination() : basePath + redirect.getDestination();
-
- routes.produce(httpRootPathBuildItem.routeBuilder()
- .route(basePath + redirect.getPath())
- .handler(recorder.redirectHandler(destination, redirect.getStatusCode(), runtimeConfig))
- .build());
- }
-
- for (var page : openDCUiBuildItem.getPages()) {
- routes.produce(httpRootPathBuildItem.routeBuilder()
- .route(basePath + page.getPath())
- .handler(recorder.pageHandler(finalDestination, page.getName(), runtimeConfig))
- .build());
- }
-
- /* Construct static routes */
- Handler<RoutingContext> staticHandler = recorder.staticHandler(
- finalDestination,
- basePath,
- result.getWebRootConfigurations(),
- runtimeConfig,
- shutdownContext
- );
-
- routes.produce(httpRootPathBuildItem.routeBuilder()
- .route(buildConfig.path)
- .displayOnNotFoundPage("OpenDC UI")
- .routeConfigKey("quarkus.opendc-ui.path")
- .handler(staticHandler)
- .build());
-
- routes.produce(httpRootPathBuildItem.routeBuilder()
- .route(buildConfig.path + "*")
- .handler(staticHandler)
- .build());
- }
-
- /**
- * A {@link WebJarResourcesFilter} that instantiates the variables in the web jar resource.
- */
- private static class InsertVariablesResourcesFilter implements WebJarResourcesFilter {
-
- private static final String HTML = ".html";
- private static final String CSS = ".css";
- private static final String JS = ".js";
-
- private final OpenDCUiConfig config;
- private final HttpRootPathBuildItem httpRootPathBuildItem;
-
-
- public InsertVariablesResourcesFilter(OpenDCUiConfig config, HttpRootPathBuildItem httpRootPathBuildItem) {
- this.config = config;
- this.httpRootPathBuildItem = httpRootPathBuildItem;
- }
-
- @Override
- public FilterResult apply(String fileName, InputStream stream) throws IOException {
- if (stream == null) {
- return new FilterResult(null, false);
- }
-
- // Allow replacement of variables in HTML, CSS, and JavaScript files
- if (fileName.endsWith(HTML) || fileName.endsWith(CSS) || fileName.endsWith(JS)) {
- byte[] oldContentBytes = stream.readAllBytes();
- String oldContents = new String(oldContentBytes);
- String contents = substituteVariables(oldContents, this::substitute);
-
- boolean changed = contents.length() != oldContents.length() || !contents.equals(oldContents);
- if (changed) {
- return new FilterResult(new ByteArrayInputStream(contents.getBytes()), true);
- } else {
- return new FilterResult(new ByteArrayInputStream(oldContentBytes), false);
- }
- }
-
- return new FilterResult(stream, false);
- }
-
- private String substitute(String var) {
- switch (var) {
- case "NEXT_BASE_PATH":
- String basePath = httpRootPathBuildItem.resolvePath(config.path);
- return basePath.equals("/") ? "" : basePath; // Base path must not end with trailing slash
- case "NEXT_PUBLIC_API_BASE_URL":
- return config.apiBaseUrl;
- case "NEXT_PUBLIC_SENTRY_DSN":
- return config.sentryDsn.orElse("");
- case "NEXT_PUBLIC_AUTH0_DOMAIN":
- return config.auth.domain.orElse("");
- case "NEXT_PUBLIC_AUTH0_CLIENT_ID":
- return config.auth.clientId.orElse("");
- case "NEXT_PUBLIC_AUTH0_AUDIENCE":
- return config.auth.audience.orElse("");
- default:
- return null;
- }
- }
-
- /**
- * Pattern to match variables in the OpenDC web UI sources specified using the following format: "%%VAR_NAME%%".
- * <p>
- * Be aware that to properly handle Next.js base path, we need to also match a possible forward slash in front
- * of the variable.
- */
- private static final Pattern PATTERN = Pattern.compile("/?%%(\\w+)%%");
-
- /**
- * Helper method to substitute variables in the OpenDC web UI.
- */
- private static String substituteVariables(String contents, Function<String, String> substitute) {
- Matcher m = PATTERN.matcher(contents);
- StringBuilder sb = new StringBuilder();
-
- while (m.find()) {
- String group = m.group(1);
- String val = substitute.apply(group);
- m.appendReplacement(sb, val != null ? val : group);
- }
-
- m.appendTail(sb);
- return sb.toString();
- }
- }
-
- /**
- * A {@link BooleanSupplier} to determine if the OpenDC web UI extension should be included.
- */
- private static class IsIncluded implements BooleanSupplier {
- OpenDCUiConfig config;
-
- @Override
- public boolean getAsBoolean() {
- return config.include;
- }
- }
-
- private static ResolvedDependency getAppArtifact(CurateOutcomeBuildItem curateOutcomeBuildItem, GACT artifactKey) {
- for (ResolvedDependency dep : curateOutcomeBuildItem.getApplicationModel().getDependencies()) {
- if (dep.getKey().equals(artifactKey)) {
- return dep;
- }
- }
- throw new RuntimeException("Could not find artifact " + artifactKey + " among the application dependencies");
- }
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiRoutingBuildItem.java b/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiRoutingBuildItem.java
deleted file mode 100644
index 7e0f9408..00000000
--- a/opendc-web/opendc-web-ui-quarkus/deployment/src/main/java/org/opendc/web/ui/deployment/OpenDCUiRoutingBuildItem.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.web.ui.deployment;
-
-import io.quarkus.builder.item.SimpleBuildItem;
-
-import java.util.List;
-
-/**
- * Build item containing the routes for the OpenDC web UI.
- */
-public final class OpenDCUiRoutingBuildItem extends SimpleBuildItem {
-
- private final boolean custom404;
- private final List<Page> pages;
- private final List<Redirect> redirects;
-
- /**
- * Construct a {@link OpenDCUiRoutingBuildItem} instance.
- *
- * @param routes The routes defined by Next.js.
- * @param redirects The redirects that have been defined by Next.js.
- * @param custom404 A flag to indicate that custom 404 pages are enabled.
- */
- public OpenDCUiRoutingBuildItem(List<Page> routes, List<Redirect> redirects, boolean custom404) {
- this.custom404 = custom404;
- this.pages = routes;
- this.redirects = redirects;
- }
-
- public List<Page> getPages() {
- return pages;
- }
-
- public List<Redirect> getRedirects() {
- return redirects;
- }
-
- public boolean hasCustom404() {
- return this.custom404;
- }
-
- /**
- * A redirect defined by the Next.js routes manifest.
- */
- public static final class Redirect {
-
- private final String path;
- private final String destination;
- private final int statusCode;
-
- /**
- * Construct a {@link Redirect} route.
- *
- * @param path The path that should result in a redirect.
- * @param destination The destination of the redirect.
- * @param statusCode The status code of the redirect.
- */
- public Redirect(String path, String destination, int statusCode) {
- this.statusCode = statusCode;
- this.path = path;
- this.destination = destination;
- }
-
- public String getPath() {
- return path;
- }
-
- public String getDestination() {
- return destination;
- }
-
- public int getStatusCode() {
- return statusCode;
- }
- }
-
- /**
- * A page defined by the Next.js routes manifest.
- */
- public static final class Page {
-
- private final String path;
- private final String name;
-
- public Page(String path, String page) {
- this.path = path;
- this.name = page;
- }
-
- public String getPath() {
- return path;
- }
-
- public String getName() {
- return name;
- }
- }
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/runtime/build.gradle.kts b/opendc-web/opendc-web-ui-quarkus/runtime/build.gradle.kts
deleted file mode 100644
index f4131f0b..00000000
--- a/opendc-web/opendc-web-ui-quarkus/runtime/build.gradle.kts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2022 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-description = "Quarkus extension for serving OpenDC web interface"
-
-plugins {
- `java-library-conventions`
- id("io.quarkus.extension")
-}
-
-dependencies {
- implementation(enforcedPlatform(libs.quarkus.bom))
-
- implementation(libs.quarkus.core.runtime)
- implementation(libs.quarkus.vertx.http.runtime)
- implementation(libs.quarkus.arc.runtime)
-}
diff --git a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java b/opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java
index 026a9039..026a9039 100644
--- a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java
+++ b/opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRecorder.java
diff --git a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java b/opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java
index 8ae3b6a2..8ae3b6a2 100644
--- a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java
+++ b/opendc-web/opendc-web-ui-quarkus/src/main/java/org/opendc/web/ui/runtime/OpenDCUiRuntimeConfig.java
diff --git a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/opendc-web/opendc-web-ui-quarkus/src/main/resources/META-INF/quarkus-extension.yaml
index 581a1779..581a1779 100644
--- a/opendc-web/opendc-web-ui-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/opendc-web/opendc-web-ui-quarkus/src/main/resources/META-INF/quarkus-extension.yaml