mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-10-31 16:01:00 +01:00
3236/vite migration (#3354)
* Migrate to Vite * Remove asset-manifest.json which vite doesn't generate * Fix tree-shaking removing Export URL if-blocks from services * Implement address correction for the vite bundle * Fixed issue with single proxy online graph 403 without page.server.overview.players.online.graph Affects issues: - Close #3236
This commit is contained in:
parent
05cf96de0e
commit
bc424f062f
@ -113,13 +113,14 @@ task updateVersion(type: Copy) {
|
|||||||
|
|
||||||
node {
|
node {
|
||||||
download = true
|
download = true
|
||||||
version = "16.14.2"
|
version = "20.9.0"
|
||||||
nodeProjectDir = file("$rootDir/react/dashboard")
|
nodeProjectDir = file("$rootDir/react/dashboard")
|
||||||
}
|
}
|
||||||
|
|
||||||
task yarnBundle(type: YarnTask) {
|
task yarnBundle(type: YarnTask) {
|
||||||
inputs.files(fileTree("$rootDir/react/dashboard/src"))
|
inputs.files(fileTree("$rootDir/react/dashboard/src"))
|
||||||
inputs.file("$rootDir/react/dashboard/package.json")
|
inputs.file("$rootDir/react/dashboard/package.json")
|
||||||
|
inputs.file("$rootDir/react/dashboard/vite.config.js")
|
||||||
|
|
||||||
outputs.dir("$rootDir/react/dashboard/build")
|
outputs.dir("$rootDir/react/dashboard/build")
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.delivery.export;
|
package com.djrapitops.plan.delivery.export;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.rendering.BundleAddressCorrection;
|
||||||
import com.djrapitops.plan.delivery.web.AssetVersions;
|
import com.djrapitops.plan.delivery.web.AssetVersions;
|
||||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||||
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||||
@ -52,23 +53,25 @@ public class ReactExporter extends FileExporter {
|
|||||||
private final PlanConfig config;
|
private final PlanConfig config;
|
||||||
private final RootJSONResolver jsonHandler;
|
private final RootJSONResolver jsonHandler;
|
||||||
private final AssetVersions assetVersions;
|
private final AssetVersions assetVersions;
|
||||||
|
private final BundleAddressCorrection bundleAddressCorrection;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReactExporter(
|
public ReactExporter(
|
||||||
PlanFiles files,
|
PlanFiles files,
|
||||||
PlanConfig config,
|
PlanConfig config,
|
||||||
RootJSONResolver jsonHandler,
|
RootJSONResolver jsonHandler,
|
||||||
AssetVersions assetVersions
|
AssetVersions assetVersions,
|
||||||
|
BundleAddressCorrection bundleAddressCorrection
|
||||||
) {
|
) {
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.jsonHandler = jsonHandler;
|
this.jsonHandler = jsonHandler;
|
||||||
this.assetVersions = assetVersions;
|
this.assetVersions = assetVersions;
|
||||||
|
this.bundleAddressCorrection = bundleAddressCorrection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void exportReactFiles(Path toDirectory) throws IOException {
|
public void exportReactFiles(Path toDirectory) throws IOException {
|
||||||
exportIndexHtml(toDirectory);
|
exportIndexHtml(toDirectory);
|
||||||
exportAsset(toDirectory, "asset-manifest.json");
|
|
||||||
exportAsset(toDirectory, "favicon.ico");
|
exportAsset(toDirectory, "favicon.ico");
|
||||||
exportAsset(toDirectory, "logo192.png");
|
exportAsset(toDirectory, "logo192.png");
|
||||||
exportAsset(toDirectory, "logo512.png");
|
exportAsset(toDirectory, "logo512.png");
|
||||||
@ -104,17 +107,18 @@ public class ReactExporter extends FileExporter {
|
|||||||
Path to = toDirectory.resolve(path);
|
Path to = toDirectory.resolve(path);
|
||||||
Resource resource = files.getResourceFromJar("web/" + path);
|
Resource resource = files.getResourceFromJar("web/" + path);
|
||||||
// Make static asset loading work with subdirectory addresses
|
// Make static asset loading work with subdirectory addresses
|
||||||
if (path.endsWith(".css") || "asset-manifest.json".equals(path)) {
|
if (path.endsWith(".css")) {
|
||||||
String contents = resource.asString();
|
String contents = resource.asString();
|
||||||
String withReplacedStatic = StringUtils.replace(contents, "/static", getBasePath() + "/static");
|
String withReplaced = bundleAddressCorrection.correctAddressForExport(contents, path);
|
||||||
export(to, withReplacedStatic);
|
export(to, withReplaced);
|
||||||
} else if (path.endsWith(".js")) {
|
} else if (path.endsWith(".js")) {
|
||||||
String withReplacedConstants = StringUtils.replaceEach(
|
String withReplacedConstants = StringUtils.replaceEach(
|
||||||
resource.asString(),
|
resource.asString(),
|
||||||
new String[]{"PLAN_BASE_ADDRESS", "PLAN_EXPORTED_VERSION", ".p=\"/\""},
|
new String[]{"PLAN_BASE_ADDRESS", "PLAN_EXPORTED_VERSION"},
|
||||||
new String[]{config.get(WebserverSettings.EXTERNAL_LINK), "true", ".p=\"" + getBasePath() + "/\""}
|
new String[]{config.get(WebserverSettings.EXTERNAL_LINK), "true"}
|
||||||
);
|
);
|
||||||
export(to, withReplacedConstants);
|
String withReplaced = bundleAddressCorrection.correctAddressForExport(withReplacedConstants, path);
|
||||||
|
export(to, withReplaced);
|
||||||
} else {
|
} else {
|
||||||
export(to, resource);
|
export(to, resource);
|
||||||
}
|
}
|
||||||
@ -141,25 +145,11 @@ public class ReactExporter extends FileExporter {
|
|||||||
private void exportIndexHtml(Path toDirectory) throws IOException {
|
private void exportIndexHtml(Path toDirectory) throws IOException {
|
||||||
String contents = files.getResourceFromJar("web/index.html")
|
String contents = files.getResourceFromJar("web/index.html")
|
||||||
.asString();
|
.asString();
|
||||||
String basePath = getBasePath();
|
contents = bundleAddressCorrection.correctAddressForExport(contents, "index.html");
|
||||||
contents = StringUtils.replaceEach(contents,
|
|
||||||
new String[]{"/static", "/pageExtensionApi.js"},
|
|
||||||
new String[]{basePath + "/static", basePath + "/pageExtensionApi.js"});
|
|
||||||
|
|
||||||
export(toDirectory.resolve("index.html"), contents);
|
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 {
|
private void exportAsset(Path toDirectory, String asset) throws IOException {
|
||||||
export(toDirectory.resolve(asset), files.getResourceFromJar("web/" + asset));
|
export(toDirectory.resolve(asset), files.getResourceFromJar("web/" + asset));
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* 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.rendering;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.webserver.Addresses;
|
||||||
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
|
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In charge of correcting the root address in the javascript bundle.
|
||||||
|
* <p>
|
||||||
|
* The javascript bundle assumes everything is hosted at /,
|
||||||
|
* but hosting settings affect the address and it could be hosted at a subdirectory like /plan/
|
||||||
|
*
|
||||||
|
* @author AuroraLS3
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class BundleAddressCorrection {
|
||||||
|
|
||||||
|
private static final String STATIC = "static";
|
||||||
|
private static final Pattern JAVASCRIPT_ADDRESS_PATTERN = Pattern.compile("\"(\\./|/?static)(.+?)\\.(json|js|css)\"");
|
||||||
|
|
||||||
|
private final PlanConfig config;
|
||||||
|
private final Addresses addresses;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public BundleAddressCorrection(PlanConfig config, Addresses addresses) {
|
||||||
|
this.config = config;
|
||||||
|
this.addresses = addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExportBasePath() {
|
||||||
|
return addresses.getBasePath(config.get(WebserverSettings.EXTERNAL_LINK));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getWebserverBasePath() {
|
||||||
|
String address = addresses.getMainAddress()
|
||||||
|
.orElseGet(addresses::getFallbackLocalhostAddress);
|
||||||
|
return addresses.getBasePath(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String correctAddressForWebserver(String content, String fileName) {
|
||||||
|
String basePath = getWebserverBasePath();
|
||||||
|
return correctAddress(content, fileName, basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String correctAddressForExport(String content, String fileName) {
|
||||||
|
String basePath = getExportBasePath();
|
||||||
|
return correctAddress(content, fileName, basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// basePath is either empty if the address doesn't have a subdirectory, or a subdirectory.
|
||||||
|
@Nullable
|
||||||
|
private String correctAddress(String content, String fileName, String basePath) {
|
||||||
|
if (fileName.endsWith(".css")) {
|
||||||
|
return correctAddressInCss(content, basePath);
|
||||||
|
} else if (fileName.endsWith(".js")) {
|
||||||
|
return correctAddressInJavascript(content, basePath);
|
||||||
|
} else if ("index.html".equals(fileName)) {
|
||||||
|
return correctAddressInHtml(content, basePath);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String correctAddressInHtml(String content, String basePath) {
|
||||||
|
String endingSlash = basePath.endsWith("/") ? "" : "/";
|
||||||
|
return StringUtils.replaceEach(content,
|
||||||
|
new String[]{"src=\"/", "href=\"/"},
|
||||||
|
new String[]{"src=\"" + basePath + endingSlash, "href=\"" + basePath + endingSlash});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String correctAddressInCss(String content, String basePath) {
|
||||||
|
String endingSlash = basePath.endsWith("/") ? "" : "/";
|
||||||
|
return StringUtils.replace(content, "/static", basePath + endingSlash + STATIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String correctAddressInJavascript(String content, String basePath) {
|
||||||
|
int lastIndex = 0;
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
Matcher matcher = JAVASCRIPT_ADDRESS_PATTERN.matcher(content);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String addressStart = matcher.group(1);
|
||||||
|
String file = matcher.group(2);
|
||||||
|
String extension = matcher.group(3);
|
||||||
|
int startIndex = matcher.start();
|
||||||
|
int endIndex = matcher.end();
|
||||||
|
|
||||||
|
// If basePath is empty the website is hosted at root of the tree /
|
||||||
|
boolean atUrlRoot = basePath.isEmpty();
|
||||||
|
|
||||||
|
// This handles /static and static representation
|
||||||
|
boolean startsWithSlash = addressStart.startsWith("/");
|
||||||
|
String startingSlash = startsWithSlash ? "/" : "";
|
||||||
|
// This handles basePath containing a slash after subdirectory, such as /plan/ instead of /plan
|
||||||
|
String endingSlash = basePath.endsWith("/") ? "" : "/";
|
||||||
|
|
||||||
|
// Without subdirectory we can use the address as is, and it doesn't need changes,
|
||||||
|
// otherwise we can add the directory to the start.
|
||||||
|
String staticReplacement = atUrlRoot
|
||||||
|
? startingSlash + STATIC
|
||||||
|
: basePath + endingSlash + STATIC;
|
||||||
|
String relativeReplacement = atUrlRoot
|
||||||
|
? "./"
|
||||||
|
: basePath + endingSlash + "static/";
|
||||||
|
|
||||||
|
// Replaces basePath starting slash if the replaced thing does not start with slash
|
||||||
|
if (!startsWithSlash && staticReplacement.startsWith("/")) {
|
||||||
|
staticReplacement = staticReplacement.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replacement examples when basepath is empty, "/plan" or "/plan/"
|
||||||
|
// "./Filename-hash.js" -> "./Filename-hash.js" or "/plan/static/Filename-hash.js"
|
||||||
|
// "/static/Filename-hash.js" -> "/static/Filename-hash.js" or "/plan/static/Filename-hash.js"
|
||||||
|
// "static/Filename-hash.js" -> "static/Filename-hash.js" or "plan/static/Filename-hash.js"
|
||||||
|
String replacementAddress = StringUtils.equalsAny(addressStart, "/static", STATIC)
|
||||||
|
? staticReplacement
|
||||||
|
: relativeReplacement;
|
||||||
|
String replacement = '"' + replacementAddress + file + '.' + extension + '"';
|
||||||
|
|
||||||
|
output.append(content, lastIndex, startIndex) // Append non-match
|
||||||
|
.append(replacement); // Append replaced address
|
||||||
|
|
||||||
|
lastIndex = endIndex;
|
||||||
|
}
|
||||||
|
// Append rest of the content that didn't match
|
||||||
|
if (lastIndex < content.length()) {
|
||||||
|
output.append(content, lastIndex, content.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,11 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.delivery.rendering.pages;
|
package com.djrapitops.plan.delivery.rendering.pages;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.rendering.BundleAddressCorrection;
|
||||||
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
|
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
|
||||||
import com.djrapitops.plan.delivery.web.ResourceService;
|
import com.djrapitops.plan.delivery.web.ResourceService;
|
||||||
import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException;
|
import com.djrapitops.plan.delivery.web.resolver.exception.NotFoundException;
|
||||||
import com.djrapitops.plan.delivery.web.resource.WebResource;
|
import com.djrapitops.plan.delivery.web.resource.WebResource;
|
||||||
import com.djrapitops.plan.delivery.webserver.Addresses;
|
|
||||||
import com.djrapitops.plan.identification.ServerInfo;
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
import com.djrapitops.plan.identification.ServerUUID;
|
import com.djrapitops.plan.identification.ServerUUID;
|
||||||
import com.djrapitops.plan.settings.theme.Theme;
|
import com.djrapitops.plan.settings.theme.Theme;
|
||||||
@ -50,7 +50,7 @@ public class PageFactory {
|
|||||||
private final Lazy<PublicHtmlFiles> publicHtmlFiles;
|
private final Lazy<PublicHtmlFiles> publicHtmlFiles;
|
||||||
private final Lazy<Theme> theme;
|
private final Lazy<Theme> theme;
|
||||||
private final Lazy<DBSystem> dbSystem;
|
private final Lazy<DBSystem> dbSystem;
|
||||||
private final Lazy<Addresses> addresses;
|
private final Lazy<BundleAddressCorrection> bundleAddressCorrection;
|
||||||
private static final String ERROR_HTML_FILE = "error.html";
|
private static final String ERROR_HTML_FILE = "error.html";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -61,14 +61,14 @@ public class PageFactory {
|
|||||||
Lazy<Theme> theme,
|
Lazy<Theme> theme,
|
||||||
Lazy<DBSystem> dbSystem,
|
Lazy<DBSystem> dbSystem,
|
||||||
Lazy<ServerInfo> serverInfo,
|
Lazy<ServerInfo> serverInfo,
|
||||||
Lazy<Addresses> addresses
|
Lazy<BundleAddressCorrection> bundleAddressCorrection
|
||||||
) {
|
) {
|
||||||
this.versionChecker = versionChecker;
|
this.versionChecker = versionChecker;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.publicHtmlFiles = publicHtmlFiles;
|
this.publicHtmlFiles = publicHtmlFiles;
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
this.dbSystem = dbSystem;
|
this.dbSystem = dbSystem;
|
||||||
this.addresses = addresses;
|
this.bundleAddressCorrection = bundleAddressCorrection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page playersPage() throws IOException {
|
public Page playersPage() throws IOException {
|
||||||
@ -81,18 +81,12 @@ public class PageFactory {
|
|||||||
WebResource resource = ResourceService.getInstance().getResource(
|
WebResource resource = ResourceService.getInstance().getResource(
|
||||||
"Plan", fileName, () -> getPublicHtmlOrJarResource(fileName)
|
"Plan", fileName, () -> getPublicHtmlOrJarResource(fileName)
|
||||||
);
|
);
|
||||||
return new ReactPage(getBasePath(), resource);
|
return new ReactPage(bundleAddressCorrection.get(), resource);
|
||||||
} catch (UncheckedIOException readFail) {
|
} catch (UncheckedIOException readFail) {
|
||||||
throw readFail.getCause();
|
throw readFail.getCause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getBasePath() {
|
|
||||||
String address = addresses.get().getMainAddress()
|
|
||||||
.orElseGet(addresses.get()::getFallbackLocalhostAddress);
|
|
||||||
return addresses.get().getBasePath(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a server page.
|
* Create a server page.
|
||||||
*
|
*
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.delivery.rendering.pages;
|
package com.djrapitops.plan.delivery.rendering.pages;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.delivery.rendering.BundleAddressCorrection;
|
||||||
import com.djrapitops.plan.delivery.web.resource.WebResource;
|
import com.djrapitops.plan.delivery.web.resource.WebResource;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents React index.html.
|
* Represents React index.html.
|
||||||
@ -26,20 +26,17 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public class ReactPage implements Page {
|
public class ReactPage implements Page {
|
||||||
|
|
||||||
private final String basePath;
|
private final BundleAddressCorrection bundleAddressCorrection;
|
||||||
private final WebResource reactHtml;
|
private final WebResource reactHtml;
|
||||||
|
|
||||||
public ReactPage(String basePath, WebResource reactHtml) {
|
public ReactPage(BundleAddressCorrection bundleAddressCorrection, WebResource reactHtml) {
|
||||||
this.basePath = basePath;
|
this.bundleAddressCorrection = bundleAddressCorrection;
|
||||||
this.reactHtml = reactHtml;
|
this.reactHtml = reactHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toHtml() {
|
public String toHtml() {
|
||||||
return StringUtils.replaceEach(
|
return bundleAddressCorrection.correctAddressForWebserver(reactHtml.asString(), "index.html");
|
||||||
reactHtml.asString(),
|
|
||||||
new String[]{"/static", "/pageExtensionApi.js"},
|
|
||||||
new String[]{basePath + "/static", basePath + "/pageExtensionApi.js"});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
|
|||||||
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
|
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
|
||||||
import com.djrapitops.plan.delivery.formatting.Formatter;
|
import com.djrapitops.plan.delivery.formatting.Formatter;
|
||||||
import com.djrapitops.plan.delivery.formatting.Formatters;
|
import com.djrapitops.plan.delivery.formatting.Formatters;
|
||||||
|
import com.djrapitops.plan.delivery.rendering.BundleAddressCorrection;
|
||||||
import com.djrapitops.plan.delivery.rendering.html.icon.Family;
|
import com.djrapitops.plan.delivery.rendering.html.icon.Family;
|
||||||
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
|
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
|
||||||
import com.djrapitops.plan.delivery.rendering.pages.Page;
|
import com.djrapitops.plan.delivery.rendering.pages.Page;
|
||||||
@ -75,6 +76,7 @@ public class ResponseFactory {
|
|||||||
private final DBSystem dbSystem;
|
private final DBSystem dbSystem;
|
||||||
private final Theme theme;
|
private final Theme theme;
|
||||||
private final Lazy<Addresses> addresses;
|
private final Lazy<Addresses> addresses;
|
||||||
|
private final Lazy<BundleAddressCorrection> bundleAddressCorrection;
|
||||||
private final Formatter<Long> httpLastModifiedFormatter;
|
private final Formatter<Long> httpLastModifiedFormatter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -86,7 +88,8 @@ public class ResponseFactory {
|
|||||||
DBSystem dbSystem,
|
DBSystem dbSystem,
|
||||||
Formatters formatters,
|
Formatters formatters,
|
||||||
Theme theme,
|
Theme theme,
|
||||||
Lazy<Addresses> addresses
|
Lazy<Addresses> addresses,
|
||||||
|
Lazy<BundleAddressCorrection> bundleAddressCorrection
|
||||||
) {
|
) {
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.publicHtmlFiles = publicHtmlFiles;
|
this.publicHtmlFiles = publicHtmlFiles;
|
||||||
@ -97,6 +100,7 @@ public class ResponseFactory {
|
|||||||
this.addresses = addresses;
|
this.addresses = addresses;
|
||||||
|
|
||||||
httpLastModifiedFormatter = formatters.httpLastModifiedLong();
|
httpLastModifiedFormatter = formatters.httpLastModifiedLong();
|
||||||
|
this.bundleAddressCorrection = bundleAddressCorrection;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -232,9 +236,7 @@ public class ResponseFactory {
|
|||||||
String content = UnaryChain.of(resource.asString())
|
String content = UnaryChain.of(resource.asString())
|
||||||
.chain(this::replaceMainAddressPlaceholder)
|
.chain(this::replaceMainAddressPlaceholder)
|
||||||
.chain(theme::replaceThemeColors)
|
.chain(theme::replaceThemeColors)
|
||||||
.chain(contents -> StringUtils.replace(contents,
|
.chain(contents -> bundleAddressCorrection.get().correctAddressForWebserver(contents, fileName))
|
||||||
".p=\"/\"",
|
|
||||||
".p=\"" + getBasePath() + "/\""))
|
|
||||||
.apply();
|
.apply();
|
||||||
ResponseBuilder responseBuilder = Response.builder()
|
ResponseBuilder responseBuilder = Response.builder()
|
||||||
.setMimeType(MimeType.JS)
|
.setMimeType(MimeType.JS)
|
||||||
@ -244,7 +246,7 @@ public class ResponseFactory {
|
|||||||
if (fileName.contains(STATIC_BUNDLE_FOLDER)) {
|
if (fileName.contains(STATIC_BUNDLE_FOLDER)) {
|
||||||
resource.getLastModified().ifPresent(lastModified -> responseBuilder
|
resource.getLastModified().ifPresent(lastModified -> responseBuilder
|
||||||
// Can't cache main bundle in browser since base path might change
|
// Can't cache main bundle in browser since base path might change
|
||||||
.setHeader(HttpHeader.CACHE_CONTROL.asString(), fileName.contains("main") ? CacheStrategy.CHECK_ETAG : CacheStrategy.CACHE_IN_BROWSER)
|
.setHeader(HttpHeader.CACHE_CONTROL.asString(), fileName.contains("index") ? CacheStrategy.CHECK_ETAG : CacheStrategy.CACHE_IN_BROWSER)
|
||||||
.setHeader(HttpHeader.LAST_MODIFIED.asString(), httpLastModifiedFormatter.apply(lastModified))
|
.setHeader(HttpHeader.LAST_MODIFIED.asString(), httpLastModifiedFormatter.apply(lastModified))
|
||||||
.setHeader(HttpHeader.ETAG.asString(), lastModified));
|
.setHeader(HttpHeader.ETAG.asString(), lastModified));
|
||||||
}
|
}
|
||||||
@ -254,12 +256,6 @@ public class ResponseFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getBasePath() {
|
|
||||||
String address = addresses.get().getMainAddress()
|
|
||||||
.orElseGet(addresses.get()::getFallbackLocalhostAddress);
|
|
||||||
return addresses.get().getBasePath(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String replaceMainAddressPlaceholder(String resource) {
|
private String replaceMainAddressPlaceholder(String resource) {
|
||||||
String address = addresses.get().getAccessAddress()
|
String address = addresses.get().getAccessAddress()
|
||||||
.orElseGet(addresses.get()::getFallbackLocalhostAddress);
|
.orElseGet(addresses.get()::getFallbackLocalhostAddress);
|
||||||
@ -275,7 +271,7 @@ public class ResponseFactory {
|
|||||||
WebResource resource = getPublicOrJarResource(fileName);
|
WebResource resource = getPublicOrJarResource(fileName);
|
||||||
String content = UnaryChain.of(resource.asString())
|
String content = UnaryChain.of(resource.asString())
|
||||||
.chain(theme::replaceThemeColors)
|
.chain(theme::replaceThemeColors)
|
||||||
.chain(contents -> StringUtils.replace(contents, "/static", getBasePath() + "/static"))
|
.chain(contents -> bundleAddressCorrection.get().correctAddressForWebserver(contents, fileName))
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
ResponseBuilder responseBuilder = Response.builder()
|
ResponseBuilder responseBuilder = Response.builder()
|
||||||
|
@ -147,8 +147,9 @@ public class ResponseResolver {
|
|||||||
String plugin = "Plan";
|
String plugin = "Plan";
|
||||||
resolverService.registerResolver(plugin, "/robots.txt", fileResolver(responseFactory::robotsResponse));
|
resolverService.registerResolver(plugin, "/robots.txt", fileResolver(responseFactory::robotsResponse));
|
||||||
resolverService.registerResolver(plugin, "/manifest.json", fileResolver(() -> responseFactory.jsonFileResponse("manifest.json")));
|
resolverService.registerResolver(plugin, "/manifest.json", fileResolver(() -> responseFactory.jsonFileResponse("manifest.json")));
|
||||||
resolverService.registerResolver(plugin, "/asset-manifest.json", fileResolver(() -> responseFactory.jsonFileResponse("asset-manifest.json")));
|
|
||||||
resolverService.registerResolver(plugin, "/favicon.ico", fileResolver(responseFactory::faviconResponse));
|
resolverService.registerResolver(plugin, "/favicon.ico", fileResolver(responseFactory::faviconResponse));
|
||||||
|
resolverService.registerResolver(plugin, "/logo192.png", fileResolver(() -> responseFactory.imageResponse("logo192.png")));
|
||||||
|
resolverService.registerResolver(plugin, "/logo512.png", fileResolver(() -> responseFactory.imageResponse("logo512.png")));
|
||||||
resolverService.registerResolver(plugin, "/pageExtensionApi.js", fileResolver(() -> responseFactory.javaScriptResponse("pageExtensionApi.js")));
|
resolverService.registerResolver(plugin, "/pageExtensionApi.js", fileResolver(() -> responseFactory.javaScriptResponse("pageExtensionApi.js")));
|
||||||
|
|
||||||
resolverService.registerResolver(plugin, "/query", queryPageResolver);
|
resolverService.registerResolver(plugin, "/query", queryPageResolver);
|
||||||
|
@ -37,7 +37,7 @@ import java.util.Optional;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class StaticResourceResolver implements NoAuthResolver {
|
public class StaticResourceResolver implements NoAuthResolver {
|
||||||
|
|
||||||
private static final String PART_REGEX = "(vendor|css|js|img|static)";
|
private static final String PART_REGEX = "(static)";
|
||||||
public static final String PATH_REGEX = "^.*/" + PART_REGEX + "/.*";
|
public static final String PATH_REGEX = "^.*/" + PART_REGEX + "/.*";
|
||||||
|
|
||||||
private final ResponseFactory responseFactory;
|
private final ResponseFactory responseFactory;
|
||||||
|
@ -214,7 +214,7 @@ public class GraphsJSONResolver extends JSONResolver {
|
|||||||
case GRAPH_OPTIMIZED_PERFORMANCE:
|
case GRAPH_OPTIMIZED_PERFORMANCE:
|
||||||
return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS, WebPermission.PAGE_NETWORK_PERFORMANCE);
|
return List.of(WebPermission.PAGE_SERVER_PERFORMANCE_GRAPHS, WebPermission.PAGE_NETWORK_PERFORMANCE);
|
||||||
case GRAPH_ONLINE:
|
case GRAPH_ONLINE:
|
||||||
return List.of(WebPermission.PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH);
|
return List.of(WebPermission.PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH, WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE);
|
||||||
case GRAPH_UNIQUE_NEW:
|
case GRAPH_UNIQUE_NEW:
|
||||||
return List.of(WebPermission.PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_DAY_BY_DAY);
|
return List.of(WebPermission.PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_DAY_BY_DAY);
|
||||||
case GRAPH_HOURLY_UNIQUE_NEW:
|
case GRAPH_HOURLY_UNIQUE_NEW:
|
||||||
|
@ -106,7 +106,7 @@ public class ExportTestUtilities {
|
|||||||
|
|
||||||
assertFalse(driver.findElement(By.tagName("body")).getText().contains("Bad Gateway"), "502 Bad Gateway, nginx could not reach Plan");
|
assertFalse(driver.findElement(By.tagName("body")).getText().contains("Bad Gateway"), "502 Bad Gateway, nginx could not reach Plan");
|
||||||
|
|
||||||
Awaitility.await()
|
Awaitility.await("waitForElementToBeVisible .load-in")
|
||||||
.atMost(Duration.of(10, ChronoUnit.SECONDS))
|
.atMost(Duration.of(10, ChronoUnit.SECONDS))
|
||||||
.until(() -> getMainPageElement(driver).map(WebElement::isDisplayed).orElse(false));
|
.until(() -> getMainPageElement(driver).map(WebElement::isDisplayed).orElse(false));
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
|
|||||||
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
|
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
|
||||||
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
||||||
import com.djrapitops.plan.storage.database.Database;
|
import com.djrapitops.plan.storage.database.Database;
|
||||||
|
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
|
||||||
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
|
||||||
import com.djrapitops.plan.storage.database.transactions.commands.StoreWebUserTransaction;
|
import com.djrapitops.plan.storage.database.transactions.commands.StoreWebUserTransaction;
|
||||||
import com.djrapitops.plan.storage.database.transactions.events.StoreServerPlayerTransaction;
|
import com.djrapitops.plan.storage.database.transactions.events.StoreServerPlayerTransaction;
|
||||||
@ -47,6 +48,7 @@ import org.openqa.selenium.WebDriver;
|
|||||||
import org.openqa.selenium.chrome.ChromeDriver;
|
import org.openqa.selenium.chrome.ChromeDriver;
|
||||||
import org.openqa.selenium.logging.LogEntry;
|
import org.openqa.selenium.logging.LogEntry;
|
||||||
import org.openqa.selenium.logging.LogType;
|
import org.openqa.selenium.logging.LogType;
|
||||||
|
import org.testcontainers.shaded.org.awaitility.Awaitility;
|
||||||
import utilities.RandomData;
|
import utilities.RandomData;
|
||||||
import utilities.TestConstants;
|
import utilities.TestConstants;
|
||||||
import utilities.TestResources;
|
import utilities.TestResources;
|
||||||
@ -57,6 +59,7 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -291,8 +294,11 @@ class AccessControlVisibilityTest {
|
|||||||
|
|
||||||
private void registerProxy(Database database) throws ExecutionException, InterruptedException {
|
private void registerProxy(Database database) throws ExecutionException, InterruptedException {
|
||||||
database.executeTransaction(new StoreServerInformationTransaction(
|
database.executeTransaction(new StoreServerInformationTransaction(
|
||||||
new Server(TestConstants.SERVER_TWO_UUID, "Proxy", "https://localhost", TestConstants.VERSION)
|
new Server(null, TestConstants.SERVER_TWO_UUID, "Proxy", "https://localhost", true, TestConstants.VERSION)
|
||||||
)).get();
|
)).get();
|
||||||
|
Awaitility.await("Proxy was not registered")
|
||||||
|
.atMost(5, TimeUnit.SECONDS)
|
||||||
|
.until(() -> !database.query(ServerQueries.fetchProxyServers()).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@DisplayName("Network element is visible with permission")
|
@DisplayName("Network element is visible with permission")
|
||||||
|
@ -11,27 +11,18 @@
|
|||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link href="%PUBLIC_URL%/manifest.json" rel="manifest"/>
|
<link href="/manifest.json" rel="manifest"/>
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>Plan | Player Analytics</title>
|
<title>Plan | Player Analytics</title>
|
||||||
|
|
||||||
|
|
||||||
<link crossorigin="anonymous"
|
<link crossorigin="anonymous"
|
||||||
href="https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext"
|
href="https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="%PUBLIC_URL%/pageExtensionApi.js"></script>
|
<script src="/pageExtensionApi.js"></script>
|
||||||
<script>/* This script tag will be replaced with scripts */</script>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<script src="/src/index.jsx" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -3,6 +3,7 @@
|
|||||||
"name": "dashboard",
|
"name": "dashboard",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"proxy": "http://localhost:8800",
|
"proxy": "http://localhost:8800",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
@ -37,17 +38,14 @@
|
|||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^13.5.0",
|
||||||
"react-mcjsonchat": "^1.0.0",
|
"react-mcjsonchat": "^1.0.0",
|
||||||
"react-router-dom": "6",
|
"react-router-dom": "6",
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"source-map-explorer": "^2.5.2",
|
"source-map-explorer": "^2.5.2",
|
||||||
"swagger-ui": "^5.10.3",
|
"swagger-ui": "^5.10.3",
|
||||||
"web-vitals": "^3.0.2"
|
"web-vitals": "^3.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "vite",
|
||||||
"build": "react-scripts build",
|
"build": "vite build",
|
||||||
"test": "react-scripts test",
|
|
||||||
"eject": "react-scripts eject",
|
|
||||||
"analyze": "source-map-explorer 'build/static/js/*.js'"
|
"analyze": "source-map-explorer 'build/static/js/*.js'"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
@ -67,5 +65,9 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.2.0",
|
||||||
|
"vite": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user