From 870569161c0a5c302511f7910e8dd2e0056137e9 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Mon, 3 Feb 2025 20:03:12 +0100 Subject: [PATCH] Add basic redirect support Related to #559 Signed-off-by: stianst Signed-off-by: Alexander Schwartz Co-authored-by: Alexander Schwartz --- README.md | 3 +- package.json | 6 +++ pages/404.ftl | 30 ++++++++---- resources/js/redirect.js | 46 ++++++++++++++++++ resources/redirects | 9 ++++ .../webbuilder/builders/AppBuilder.java | 48 ++++++++++--------- 6 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 resources/js/redirect.js create mode 100644 resources/redirects diff --git a/README.md b/README.md index 94872024..e810bb70 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ There's also an auto-builder utility that watches the filesystem and continuousl To serve the website locally with a web browser specify the base URL with `KC_URL`: - export KC_URL=http://localhost:8000 + export KC_URL=http://localhost:5000 mvn install + npm run serve Finally, when building the website to be published you need to include `-Dpublish`. This should usually not be done manually though as there is a GitHub Action that takes care of building and publishing to `https://github.com/keycloak/keycloak.github.io`. diff --git a/package.json b/package.json index 69e3c62f..d260db8e 100644 --- a/package.json +++ b/package.json @@ -3,5 +3,11 @@ "bootstrap": "5.1.x", "@fortawesome/fontawesome-free": "5.15.4", "tocbot": "4.18.0" + }, + "scripts": { + "serve": "http-server target/web -c-1 -p 5000 -a 127.0.0.1 --cors" + }, + "devDependencies": { + "http-server": "^14.1.1" } } \ No newline at end of file diff --git a/pages/404.ftl b/pages/404.ftl index d2f84bd2..00761afb 100644 --- a/pages/404.ftl +++ b/pages/404.ftl @@ -3,18 +3,26 @@ <@tmpl.page current="keys" title="Page not found" noindex=true>
-

Page not found

+

Page not found

-

- The page you’re looking for does not exist. It may have been moved. You can return to the start page, or follow one of the links in the navigation on the top. -

-

- If you arrived at this page by clicking on a link, please notify the owner of the site that the link is broken. If you typed the URL of this page manually, please double-check that you entered the address correctly. -

+
+

+ The page you’re looking for does not exist. It may have been moved. You can return to the start page, or follow one of the links in the navigation on the top. +

+

+ If you arrived at this page by clicking on a link, please notify the owner of the site that the link is broken. If you typed the URL of this page manually, please double-check that you entered the address correctly. +

-

- If you think this is a bug that the Keycloak team should fix, create a bug issue on the GitHub issue tracker. -

+

+ If you think this is a bug that the Keycloak team should fix, create a bug issue on the GitHub issue tracker. +

+
+ + + diff --git a/resources/js/redirect.js b/resources/js/redirect.js new file mode 100644 index 00000000..5e9db0af --- /dev/null +++ b/resources/js/redirect.js @@ -0,0 +1,46 @@ +var pageWithHash = window.location.pathname + window.location.hash; +var page = window.location.pathname; +var openRedirects = new XMLHttpRequest(); +openRedirects.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + var redirects = this.responseText.split(/\r?\n/); + for (var i = 0; i < redirects.length; i++) { + var redirect = redirects[i].split("="); + if (redirects[i].trim().startsWith("#")) { + // allow for line comments + continue; + } + var pattern = new RegExp(redirect[0]) + if (pattern.test(page) || pattern.test(pageWithHash)) { + document.title = "Redirecting..." + document.getElementById("heading").innerText = "Redirecting..."; + document.getElementById("redirecting").style.display = "block"; + document.getElementById("notfound").style.display = "none"; + document.getElementById("redirectlink").href = redirect[1]; + document.getElementById("redirectlink").innerText = redirect[1]; + var anchor = ""; + if (redirect[1].indexOf("#") !== -1) { + anchor = "#" + redirect[1].split("#")[1]; + } + var checkRedirect = new XMLHttpRequest(); + checkRedirect.onreadystatechange = function() { + if (this.readyState === 4) { + if (this.status === 200) { + // The response URL will no longer contain the anchor, therefore, add it again + window.location = checkRedirect.responseURL + anchor; + } else { + document.getElementById("heading").innerText = "Redirect failed"; + document.getElementById("notfound").style.display = "block"; + document.getElementById("redirecting").style.display = "none"; + } + } + } + checkRedirect.open("GET", redirect[1], true); + checkRedirect.send(); + break; + } + } + } + }; +openRedirects.open("GET", "/resources/redirects", true); +openRedirects.send(); \ No newline at end of file diff --git a/resources/redirects b/resources/redirects new file mode 100644 index 00000000..be6920a4 --- /dev/null +++ b/resources/redirects @@ -0,0 +1,9 @@ +# Regex with with or without a anchor -> target path +^/docs/(latest|[0-9.]+)/securing_apps=/guides.html#securing-apps +^/docs/(latest|[0-9.]+)/server_installation=/guides.html#server +^/docs/(latest|[0-9.]+)/getting_started=/guides.html#getting-started +^/docs/([0-9.]+)/server-admin=/docs-api/latest/server-admin/index.html +^/docs/([0-9.]+)/upgrading=/docs-api/latest/upgrading/index.html +^/docs-api/[0-9.]+/rest-api=/docs-api/latest/rest-api/index.html +^/[0-9]+/[0-9]+/keycloak-[0-9]+-released=/blog.html +^/404=/ \ No newline at end of file diff --git a/src/main/java/org/keycloak/webbuilder/builders/AppBuilder.java b/src/main/java/org/keycloak/webbuilder/builders/AppBuilder.java index 3386e5f4..00cee224 100644 --- a/src/main/java/org/keycloak/webbuilder/builders/AppBuilder.java +++ b/src/main/java/org/keycloak/webbuilder/builders/AppBuilder.java @@ -11,6 +11,7 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -36,30 +37,31 @@ private void installPackage(String name, String version) throws Exception { // Get package contents as tarball stream. Package packageInfo = Registry.getPackage(name); Version latestVersion = packageInfo.getVersionByTag(version); - ArchiveInputStream tarball = latestVersion.getDist().getTarballStream(); - - // Copy package contents to installation path. - ArchiveEntry entry; - while ((entry = tarball.getNextEntry()) != null) { - // Skip any files not part of the package contents. - String packagePrefix = "package"; - if (!entry.getName().startsWith(packagePrefix)) { - continue; + try (ArchiveInputStream tarball = latestVersion.getDist().getTarballStream()) { + + // Copy package contents to installation path. + ArchiveEntry entry; + while ((entry = tarball.getNextEntry()) != null) { + // Skip any files not part of the package contents. + String packagePrefix = "package"; + if (!entry.getName().startsWith(packagePrefix)) { + continue; + } + + // Resolve path without 'package' prefix. + Path entryPath = Path.of(packagePrefix).relativize(Path.of(entry.getName())); + + // Skip file if it's extension is not permitted. + String extension = getFileExtension(entryPath.getFileName().toString()); + if (!ALLOWED_EXTENSIONS.contains(extension)) { + continue; + } + + // Resolve target path and copy file. + Path targetPath = installationPath.resolve(entryPath); + Files.createDirectories(targetPath.getParent()); + Files.copy(tarball, targetPath, StandardCopyOption.REPLACE_EXISTING); } - - // Resolve path without 'package' prefix. - Path entryPath = Path.of(packagePrefix).relativize(Path.of(entry.getName())); - - // Skip file if it's extension is not permitted. - String extension = getFileExtension(entryPath.getFileName().toString()); - if(!ALLOWED_EXTENSIONS.contains(extension)) { - continue; - } - - // Resolve target path and copy file. - Path targetPath = installationPath.resolve(entryPath); - Files.createDirectories(targetPath.getParent()); - Files.copy(tarball, targetPath); } // Add package to the imports so it can be written to the import map later.