Implemented support for subdirectory addresses

Export of React version of frontend now supports exporting to a subdirectory
So now you can access exported site at /plan/... if it is hosted there.

This might impact reverse proxy setups positively, but that has not yet been tested.
The hypothetical positive impact is the inclusion of subdirectory in the React-router
configuration, since now it can handle the reverse-proxy subdirectory in URL.
This commit is contained in:
Aurora Lahtela 2023-01-06 12:12:45 +02:00
parent ac2fa2ecce
commit 5082f80030
13 changed files with 355 additions and 121 deletions

View File

@ -126,7 +126,7 @@ public class Exporter extends FileExporter {
if (config.isFalse(ExportSettings.PLAYER_PAGES)) return false;
try {
playerPageExporter.export(toDirectory, playerUUID, playerName);
playerPageExporter.export(toDirectory, playerUUID);
return true;
} catch (IOException | NotFoundException e) {
throw new ExportException("Failed to export player: " + playerName + ", " + e.toString(), e);

View File

@ -16,8 +16,12 @@
*/
package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer;
import com.djrapitops.plan.delivery.rendering.html.Html;
import com.djrapitops.plan.delivery.web.resource.WebResource;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.storage.file.Resource;
import org.apache.commons.lang3.StringUtils;
@ -99,4 +103,19 @@ abstract class FileExporter {
);
}
void exportReactRedirects(Path toDirectory, PlanFiles files, PlanConfig config, String[] redirections) throws IOException {
String redirectPageHtml = files.getResourceFromJar("web/export-redirect.html").asString();
PlaceholderReplacer placeholderReplacer = new PlaceholderReplacer();
placeholderReplacer.put("PLAN_ADDRESS", config.get(WebserverSettings.EXTERNAL_LINK));
redirectPageHtml = placeholderReplacer.apply(redirectPageHtml);
for (String redirection : redirections) {
exportReactRedirect(toDirectory, redirectPageHtml, redirection);
}
}
private void exportReactRedirect(Path toDirectory, String redirectHtml, String path) throws IOException {
export(toDirectory.resolve(path).resolve("index.html"), redirectHtml);
}
}

View File

@ -118,23 +118,24 @@ public class NetworkPageExporter extends FileExporter {
export(to, exportPaths.resolveExportPaths(html));
}
public static String[] getRedirections() {
return new String[]{
"network",
"network/overview",
"network/serversOverview",
"network/sessions",
"network/playerbase",
"network/join-addresses",
"network/players",
"network/geolocations",
"network/plugins-overview",
};
}
private void exportReactRedirects(Path toDirectory) throws IOException {
if (config.isFalse(PluginSettings.FRONTEND_BETA)) return;
Resource redirect = files.getResourceFromJar("web/export-redirect.html");
exportReactRedirect(toDirectory, redirect, "network");
exportReactRedirect(toDirectory, redirect, "network/overview");
exportReactRedirect(toDirectory, redirect, "network/serversOverview");
exportReactRedirect(toDirectory, redirect, "network/sessions");
exportReactRedirect(toDirectory, redirect, "network/playerbase");
exportReactRedirect(toDirectory, redirect, "network/join-addresses");
exportReactRedirect(toDirectory, redirect, "network/players");
exportReactRedirect(toDirectory, redirect, "network/geolocations");
exportReactRedirect(toDirectory, redirect, "network/plugins-overview");
}
private void exportReactRedirect(Path toDirectory, Resource redirectHtml, String path) throws IOException {
export(toDirectory.resolve(path).resolve("index.html"), redirectHtml.asString());
exportReactRedirects(toDirectory, files, config, getRedirections());
}
/**

View File

@ -91,11 +91,10 @@ public class PlayerPageExporter extends FileExporter {
*
* @param toDirectory Path to Export directory
* @param playerUUID UUID of the player
* @param playerName Name of the player
* @throws IOException If a template can not be read from jar/disk or the result written
* @throws NotFoundException If a file or resource that is being exported can not be found
*/
public void export(Path toDirectory, UUID playerUUID, String playerName) throws IOException {
public void export(Path toDirectory, UUID playerUUID) throws IOException {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState == Database.State.CLOSED || dbState == Database.State.CLOSING) return;
if (Boolean.FALSE.equals(dbSystem.getDatabase().query(PlayerFetchQueries.isPlayerRegistered(playerUUID)))) {
@ -130,14 +129,7 @@ public class PlayerPageExporter extends FileExporter {
private void exportReactRedirects(Path toDirectory, UUID playerUUID) throws IOException {
if (config.isFalse(PluginSettings.FRONTEND_BETA)) return;
Resource redirectPage = files.getResourceFromJar("web/export-redirect.html");
for (String redirection : getRedirections(playerUUID)) {
exportReactRedirect(toDirectory, redirectPage, redirection);
}
}
private void exportReactRedirect(Path toDirectory, Resource redirectHtml, String path) throws IOException {
export(toDirectory.resolve(path).resolve("index.html"), redirectHtml.asString());
exportReactRedirects(toDirectory, files, config, getRedirections(playerUUID));
}
private void exportJSON(ExportPaths exportPaths, Path toDirectory, UUID playerUUID) throws IOException {

View File

@ -118,12 +118,8 @@ public class PlayersPageExporter extends FileExporter {
private void exportReactRedirects(Path toDirectory) throws IOException {
if (config.isFalse(PluginSettings.FRONTEND_BETA)) return;
Resource redirect = files.getResourceFromJar("web/export-redirect.html");
exportReactRedirect(toDirectory, redirect, "players");
}
private void exportReactRedirect(Path toDirectory, Resource redirectHtml, String path) throws IOException {
export(toDirectory.resolve(path).resolve("index.html"), redirectHtml.asString());
String[] redirections = {"players"};
exportReactRedirects(toDirectory, files, config, redirections);
}
private void exportJSON(Path toDirectory) throws IOException {

View File

@ -67,7 +67,7 @@ public class ReactExporter extends FileExporter {
}
public void exportReactFiles(Path toDirectory) throws IOException {
exportAsset(toDirectory, "index.html");
exportIndexHtml(toDirectory);
exportAsset(toDirectory, "asset-manifest.json");
exportAsset(toDirectory, "favicon.ico");
exportAsset(toDirectory, "logo192.png");
@ -101,11 +101,16 @@ public class ReactExporter extends FileExporter {
for (String path : paths) {
Path to = toDirectory.resolve(path);
Resource resource = files.getResourceFromJar("web/" + path);
if (path.endsWith(".js")) {
// Make static asset loading work with subdirectory addresses
if (path.endsWith(".css") || "asset-manifest.json".equals(path)) {
String contents = resource.asString();
String withReplacedStatic = StringUtils.replace(contents, "/static", getBasePath() + "/static");
export(to, withReplacedStatic);
} else if (path.endsWith(".js")) {
String withReplacedConstants = StringUtils.replaceEach(
resource.asString(),
new String[]{"PLAN_BASE_ADDRESS", "PLAN_EXPORTED_VERSION"},
new String[]{config.get(WebserverSettings.EXTERNAL_LINK), "true"}
new String[]{"PLAN_BASE_ADDRESS", "PLAN_EXPORTED_VERSION", "n.p=\"/\""},
new String[]{config.get(WebserverSettings.EXTERNAL_LINK), "true", "n.p=\"" + getBasePath() + "/\""}
);
export(to, withReplacedConstants);
} else {
@ -131,6 +136,26 @@ public class ReactExporter extends FileExporter {
}
}
private void exportIndexHtml(Path toDirectory) throws IOException {
String contents = files.getResourceFromJar("web/index.html")
.asString();
String basePath = getBasePath();
contents = StringUtils.replace(contents, "/static", basePath + "/static");
export(toDirectory.resolve("index.html"), contents);
}
private String getBasePath() {
String basePath = config.get(WebserverSettings.EXTERNAL_LINK)
.replace("http://", "")
.replace("https://", "");
if (StringUtils.contains(basePath, '/')) {
return basePath.substring(StringUtils.indexOf(basePath, '/'));
} else {
return "";
}
}
private void exportAsset(Path toDirectory, String asset) throws IOException {
export(toDirectory.resolve(asset), files.getResourceFromJar("web/" + asset));
}

View File

@ -152,14 +152,7 @@ public class ServerPageExporter extends FileExporter {
private void exportReactRedirects(Path toDirectory, ServerUUID serverUUID) throws IOException {
if (config.isFalse(PluginSettings.FRONTEND_BETA)) return;
Resource redirectPage = files.getResourceFromJar("web/export-redirect.html");
for (String redirection : getRedirections(serverUUID)) {
exportReactRedirect(toDirectory, redirectPage, redirection);
}
}
private void exportReactRedirect(Path toDirectory, Resource redirectHtml, String path) throws IOException {
export(toDirectory.resolve(path).resolve("index.html"), redirectHtml.asString());
exportReactRedirects(toDirectory, files, config, getRedirections(serverUUID));
}
/**

View File

@ -4,7 +4,17 @@
<meta content="AuroraLS3" name="author">
<meta content="noindex, nofollow" name="robots">
<title>Plan | Player Analytics</title>
<script>window.location.href = `/?redirect=${encodeURIComponent(window.location.pathname + window.location.hash + window.location.search)}`</script>
<script>
const address = `${PLAN_ADDRESS}`;
const currentAddress = window.location.pathname + window.location.hash + window.location.search;
let basePath = address.replace("http://", "")
.replace("https://", "");
if (basePath.includes('/')) {
basePath = basePath.substring(basePath.indexOf('/') + 1);
}
const redirectTo = currentAddress.replace(basePath, '');
window.location.href = address + `/?redirect=${encodeURIComponent(redirectTo)}`;
</script>
</head>
<body>
<noscript>Please enable javascript.</noscript>

View File

@ -17,49 +17,34 @@
package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.gathering.domain.DataMap;
import com.djrapitops.plan.gathering.domain.FinishedSession;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreSessionTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreWorldNameTransaction;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.java.Lists;
import extension.FullSystemExtension;
import extension.SeleniumExtension;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.logging.LogEntry;
import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.shaded.org.apache.commons.io.FileUtils;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import org.testcontainers.utility.DockerImageName;
import utilities.RandomData;
import utilities.TestConstants;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static com.djrapitops.plan.delivery.export.ExportTestUtilities.*;
/**
* @author AuroraLS3
@ -98,23 +83,7 @@ class ExportRegressionTest {
system.enable();
serverUUID = system.getServerInfo().getServerUUID();
savePlayerData(system.getDatabaseSystem().getDatabase(), serverUUID);
export(system.getExportSystem().getExporter(), system.getDatabaseSystem().getDatabase());
}
private static void export(Exporter exporter, Database database) throws Exception {
exporter.exportReact();
assertTrue(exporter.exportServerPage(database.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID.toString()))
.orElseThrow(AssertionError::new)));
assertTrue(exporter.exportPlayersPage());
assertTrue(exporter.exportPlayerPage(TestConstants.PLAYER_ONE_UUID, TestConstants.PLAYER_ONE_NAME));
}
private static void savePlayerData(Database database, ServerUUID serverUUID) {
UUID uuid = TestConstants.PLAYER_ONE_UUID;
database.executeTransaction(new PlayerRegisterTransaction(uuid, RandomData::randomTime, TestConstants.PLAYER_ONE_NAME));
FinishedSession session = new FinishedSession(uuid, serverUUID, 1000L, 11000L, 500L, new DataMap());
database.executeTransaction(new StoreWorldNameTransaction(serverUUID, "world"));
database.executeTransaction(new StoreSessionTransaction(session));
export(system.getExportSystem().getExporter(), system.getDatabaseSystem().getDatabase(), serverUUID);
}
@AfterAll
@ -124,54 +93,21 @@ class ExportRegressionTest {
}
@AfterEach
void clearExportDirectory(WebDriver driver) {
void clearBrowserConsole(WebDriver driver) {
SeleniumExtension.newTab(driver);
}
@TestFactory
Collection<DynamicTest> exportedWebpageDoesNotHaveErrors(ChromeDriver driver) throws Exception {
List<String> endpointsToTest = Lists.builder(String.class)
.add("/")
.addAll(ServerPageExporter.getRedirections(serverUUID))
.addAll(PlayerPageExporter.getRedirections(TestConstants.PLAYER_ONE_UUID))
.add("/players")
.build();
Collection<DynamicTest> exportedWebpageDoesNotHaveErrors(ChromeDriver driver) {
List<String> endpointsToTest = getEndpointsToTest(serverUUID);
return endpointsToTest.stream().map(
endpoint -> DynamicTest.dynamicTest("Exported page does not log errors to js console " + endpoint, () -> {
String address = "http://" + webserver.getHost() + ":" + webserver.getMappedPort(8080)
+ (endpoint.startsWith("/") ? endpoint : '/' + endpoint);
System.out.println("GET: " + address);
driver.get(address);
new WebDriverWait(driver, Duration.of(10, ChronoUnit.SECONDS)).until(
webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
Awaitility.await()
.atMost(Duration.of(10, ChronoUnit.SECONDS))
.until(() -> getElement(driver).map(WebElement::isDisplayed).orElse(false));
List<LogEntry> logs = new ArrayList<>();
logs.addAll(driver.manage().logs().get(LogType.CLIENT).getAll());
logs.addAll(driver.manage().logs().get(LogType.BROWSER).getAll());
List<LogEntry> logs = getLogsAfterRequestToAddress(driver, address);
assertNoLogs(logs);
})
).collect(Collectors.toList());
}
private Optional<WebElement> getElement(ChromeDriver driver) {
try {
return Optional.of(driver.findElement(By.className("load-in")));
} catch (NoSuchElementException e) {
return Optional.empty();
}
}
private void assertNoLogs(List<LogEntry> logs) {
List<String> loggedLines = logs.stream()
.map(log -> "\n" + log.getLevel().getName() + " " + log.getMessage())
.toList();
assertTrue(loggedLines.isEmpty(), () -> "Browser console included " + loggedLines.size() + " logs: " + loggedLines);
}
}

View File

@ -0,0 +1,116 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.file.PlanFiles;
import extension.FullSystemExtension;
import extension.SeleniumExtension;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.logging.LogEntry;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.shaded.org.apache.commons.io.FileUtils;
import org.testcontainers.utility.DockerImageName;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static com.djrapitops.plan.delivery.export.ExportTestUtilities.*;
/**
* Tests exported website when exported to /plan/...
*
* @author AuroraLS3
*/
@Testcontainers(disabledWithoutDocker = true)
@ExtendWith(SeleniumExtension.class)
@ExtendWith(FullSystemExtension.class)
class ExportSubdirRegressionTest {
public static GenericContainer<?> webserver;
private static Path exportDir;
private static ServerUUID serverUUID;
@BeforeAll
static void setUp(PlanFiles files, PlanConfig config, PlanSystem system) throws Exception {
exportDir = files.getDataDirectory().resolve("export");
Files.createDirectories(exportDir.resolve("plan"));
System.out.println("Export to " + exportDir.resolve("plan").toFile().getAbsolutePath());
webserver = new GenericContainer<>(DockerImageName.parse("halverneus/static-file-server:latest"))
.withExposedPorts(8080)
.withFileSystemBind(exportDir.toFile().getAbsolutePath(), "/web")
.waitingFor(new HttpWaitStrategy());
webserver.start();
config.set(PluginSettings.FRONTEND_BETA, true);
config.set(WebserverSettings.DISABLED, true);
config.set(WebserverSettings.EXTERNAL_LINK, "http://" + webserver.getHost() + ":" + webserver.getMappedPort(8080) + "/plan");
// Avoid accidentally DDoS:ing head image service during tests.
config.set(DisplaySettings.PLAYER_HEAD_IMG_URL, "data:image/png;base64,AA==");
// Using .resolve("plan") here to export to /web/plan
config.set(ExportSettings.HTML_EXPORT_PATH, exportDir.resolve("plan").toFile().getAbsolutePath());
config.set(ExportSettings.SERVER_PAGE, true);
config.set(ExportSettings.PLAYERS_PAGE, true);
config.set(ExportSettings.PLAYER_PAGES, true);
system.enable();
serverUUID = system.getServerInfo().getServerUUID();
savePlayerData(system.getDatabaseSystem().getDatabase(), serverUUID);
export(system.getExportSystem().getExporter(), system.getDatabaseSystem().getDatabase(), serverUUID);
}
@AfterAll
static void tearDown(PlanSystem system) throws IOException {
system.disable();
FileUtils.cleanDirectory(exportDir.toFile());
}
@AfterEach
void clearBrowserConsole(WebDriver driver) {
SeleniumExtension.newTab(driver);
}
@TestFactory
Collection<DynamicTest> exportedWebpageDoesNotHaveErrors(ChromeDriver driver) {
List<String> endpointsToTest = getEndpointsToTest(serverUUID);
return endpointsToTest.stream().map(
endpoint -> DynamicTest.dynamicTest("Exported page does not log errors to js console " + endpoint, () -> {
String address = "http://" + webserver.getHost() + ":" + webserver.getMappedPort(8080) + "/plan"
+ (endpoint.startsWith("/") ? endpoint : '/' + endpoint);
List<LogEntry> logs = getLogsAfterRequestToAddress(driver, address);
assertNoLogsExceptFaviconError(logs);
})
).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,134 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.gathering.domain.DataMap;
import com.djrapitops.plan.gathering.domain.FinishedSession;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreSessionTransaction;
import com.djrapitops.plan.storage.database.transactions.events.StoreWorldNameTransaction;
import com.djrapitops.plan.utilities.java.Lists;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.logging.LogEntry;
import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import utilities.RandomData;
import utilities.TestConstants;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author AuroraLS3
*/
class ExportTestUtilities {
private ExportTestUtilities() {
/* Static utility method class */
}
static void assertNoLogs(List<LogEntry> logs) {
List<String> loggedLines = logs.stream()
.map(log -> "\n" + log.getLevel().getName() + " " + log.getMessage())
.toList();
assertTrue(loggedLines.isEmpty(), () -> "Browser console included " + loggedLines.size() + " logs: " + loggedLines);
}
static void assertNoLogsExceptFaviconError(List<LogEntry> logs) {
List<String> loggedLines = logs.stream()
.map(log -> "\n" + log.getLevel().getName() + " " + log.getMessage())
.filter(log -> !log.contains("favicon.ico") && !log.contains("manifest.json"))
.toList();
assertTrue(loggedLines.isEmpty(), () -> "Browser console included " + loggedLines.size() + " logs: " + loggedLines);
}
static Optional<WebElement> getElement(ChromeDriver driver) {
try {
return Optional.of(driver.findElement(By.className("load-in")));
} catch (NoSuchElementException e) {
return Optional.empty();
}
}
static List<LogEntry> getLogsAfterRequestToAddress(ChromeDriver driver, String address) {
System.out.println("GET: " + address);
driver.get(address);
new WebDriverWait(driver, Duration.of(10, ChronoUnit.SECONDS)).until(
webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
Awaitility.await()
.atMost(Duration.of(10, ChronoUnit.SECONDS))
.until(() -> getElement(driver).map(WebElement::isDisplayed).orElse(false));
List<LogEntry> logs = new ArrayList<>();
logs.addAll(driver.manage().logs().get(LogType.CLIENT).getAll());
logs.addAll(driver.manage().logs().get(LogType.BROWSER).getAll());
return logs;
}
static void export(Exporter exporter, Database database, ServerUUID serverUUID) throws Exception {
exporter.exportReact();
assertTrue(exporter.exportServerPage(database.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID.toString()))
.orElseThrow(AssertionError::new)));
assertTrue(exporter.exportPlayersPage());
assertTrue(exporter.exportPlayerPage(TestConstants.PLAYER_ONE_UUID, TestConstants.PLAYER_ONE_NAME));
}
static void savePlayerData(Database database, ServerUUID serverUUID) {
UUID uuid = TestConstants.PLAYER_ONE_UUID;
database.executeTransaction(new PlayerRegisterTransaction(uuid, RandomData::randomTime, TestConstants.PLAYER_ONE_NAME));
FinishedSession session = new FinishedSession(uuid, serverUUID, 1000L, 11000L, 500L, new DataMap());
database.executeTransaction(new StoreWorldNameTransaction(serverUUID, "world"));
database.executeTransaction(new StoreSessionTransaction(session));
}
static List<String> getEndpointsToTest(ServerUUID serverUUID) {
return Lists.builder(String.class)
.add("/")
.addAll(ServerPageExporter.getRedirections(serverUUID))
.addAll(PlayerPageExporter.getRedirections(TestConstants.PLAYER_ONE_UUID))
.add("/players")
.build();
}
@SuppressWarnings("unused") // Test debugging method
static void logExportDirectoryContents(Path directory) throws IOException {
System.out.println("Contents of " + directory);
try (Stream<Path> walk = Files.walk(directory)) {
walk.forEach(System.out::println);
}
}
}

View File

@ -12,7 +12,7 @@ import {MetadataContextProvider} from "./hooks/metadataHook";
import {AuthenticationContextProvider} from "./hooks/authenticationHook";
import {NavigationContextProvider} from "./hooks/navigationHook";
import MainPageRedirect from "./components/navigation/MainPageRedirect";
import {staticSite} from "./service/backendConfiguration";
import {baseAddress, staticSite} from "./service/backendConfiguration";
const PlayerPage = React.lazy(() => import("./views/layout/PlayerPage"));
const PlayerOverview = React.lazy(() => import("./views/player/PlayerOverview"));
@ -81,6 +81,18 @@ const Lazy = ({children}) => (
</React.Suspense>
)
const getBasename = () => {
if (staticSite && baseAddress) {
const addressWithoutProtocol = baseAddress
.replace("http://", "")
.replace("https://", "");
const startOfPath = addressWithoutProtocol.indexOf("/");
return startOfPath >= 0 ? addressWithoutProtocol.substring(startOfPath) : "";
} else {
return "";
}
}
function App() {
axios.defaults.withCredentials = true;
@ -88,7 +100,7 @@ function App() {
<div className="App">
<ContextProviders>
<div id="wrapper">
<BrowserRouter>
<BrowserRouter basename={getBasename()}>
<Routes>
<Route path="" element={<MainPageRedirect/>}/>
<Route path="/" element={<MainPageRedirect/>}/>

View File

@ -15,11 +15,11 @@ export const baseAddress = javaReplaced.address.startsWith('PLAN_') || !isCurren
export const staticSite = javaReplaced.isStatic === 'true';
export const doSomeGetRequest = async (url, statusOptions) => {
return doSomeRequest(url, statusOptions, async () => axios.get(url));
return doSomeRequest(url, statusOptions, async () => axios.get(baseAddress + url));
}
export const doSomePostRequest = async (url, statusOptions, body) => {
return doSomeRequest(url, statusOptions, async () => axios.post(url, body));
return doSomeRequest(url, statusOptions, async () => axios.post(baseAddress + url, body));
}
export const doSomeRequest = async (url, statusOptions, axiosFunction) => {