Compare commits
72 Commits
Author | SHA1 | Date |
---|---|---|
Taah | 2e40ddc6bc | |
Telesphoreo | ff9cf12acc | |
Telesphoreo | 94528860f2 | |
Telesphoreo | c83f8f1d8e | |
Focusvity | 31cd561b92 | |
Telesphoreo | f0582e03e6 | |
Telesphoreo | 4e593e1b5e | |
Telesphoreo | 026b29a98c | |
Telesphoreo | 266a8d6384 | |
Telesphoreo | 21ee54cf98 | |
Telesphoreo | 492c6bd8eb | |
Telesphoreo | a7c76e6089 | |
Telesphoreo | 7a996004f0 | |
Telesphoreo | 46ff09bf58 | |
Telesphoreo | 4426c4b4b7 | |
Telesphoreo | fc8811d453 | |
Telesphoreo | 459f62e0d3 | |
Telesphoreo | 4918cf55f3 | |
Telesphoreo | e9395e193a | |
Telesphoreo | a257fb2f9c | |
Telesphoreo | fc9924754a | |
Telesphoreo | 867e3ff79b | |
Telesphoreo | 8bef589b7a | |
Allink | 5a0ae8746c | |
Telesphoreo | 41a2475e3d | |
Telesphoreo | 0bcbc7e79c | |
Telesphoreo | 95043a33db | |
Telesphoreo | 93619dd05e | |
Taah | 244d027e5d | |
Telesphoreo | ceea975729 | |
Telesphoreo | 32f17a23f3 | |
Telesphoreo | 1148ff5da9 | |
Telesphoreo | a0ae4a9720 | |
Telesphoreo | 82238fa3ed | |
Telesphoreo | 6e152417b7 | |
Telesphoreo | 22bb27af24 | |
Telesphoreo | 2b24f0ee4c | |
Telesphoreo | 7769f34f74 | |
Telesphoreo | 8337e0dfca | |
Taah | 41459ad404 | |
Telesphoreo | 7760eb6ff2 | |
Telesphoreo | cbc7907aea | |
ayunami2000 | 1f91e9c684 | |
ayunami2000 | f8bd688fa5 | |
ayunami2000 | 2117cf7041 | |
ayunami2000 | 16ee5378ad | |
Telesphoreo | 391e146d60 | |
Telesphoreo | 4bca726338 | |
Telesphoreo | 820eb57a0a | |
ayunami2000 | 139374ba55 | |
ayunami2000 | 822bc5b483 | |
Telesphoreo | 3dde5f7007 | |
ayunami2000 | 6e79310ef8 | |
Telesphoreo | 1802d91fad | |
Telesphoreo | 3fa54cec72 | |
Telesphoreo | e1dd36a0fb | |
Telesphoreo | 33a72983d2 | |
ayunami2000 | b980ea837b | |
ayunami2000 | c7658647fc | |
ayunami2000 | 5823b0c790 | |
ayunami2000 | 7163042f1b | |
Telesphoreo | 0b3c6ddbec | |
Telesphoreo | 1ec17ce7e7 | |
Telesphoreo | 1a139034b5 | |
ayunami2000 | b6b5a7f227 | |
Telesphoreo | 01d2cf44ea | |
Telesphoreo | ef5ff6b540 | |
Telesphoreo | 2ce0a1e698 | |
Telesphoreo | e6e8439a72 | |
Telesphoreo | 7550db5fb0 | |
Telesphoreo | b4ab3b609a | |
Telesphoreo | c4ad009a34 |
|
@ -0,0 +1,18 @@
|
|||
name: Gradle
|
||||
|
||||
on: [ push ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
cache: gradle
|
||||
- name: Build with Gradle
|
||||
run: chmod a+x gradlew && ./gradlew build --no-daemon
|
|
@ -0,0 +1,49 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Plexus Code Style" version="1">
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="20" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
</value>
|
||||
</option>
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="CLASS_BRACE_STYLE" value="2" />
|
||||
<option name="METHOD_BRACE_STYLE" value="2" />
|
||||
<option name="LAMBDA_BRACE_STYLE" value="2" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="WHILE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="0" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Plexus Code Style" />
|
||||
</state>
|
||||
</component>
|
|
@ -0,0 +1,24 @@
|
|||
For those who are wanting to contribute, we fully encourage doing so. There are a few rules we require following when contributing however.
|
||||
|
||||
## Steps
|
||||
1. Make an issue and get feedback. It's important to know if your idea will be accepted before writing any code.
|
||||
- If it is a feature request, describe the feature and be extremely specific.
|
||||
- If it is a bug report, ensure you include how to reproduce the bug and the expected outcome
|
||||
- If it is an enhancement, describe your proposed changes. Ensure you are extremely specific.
|
||||
2. Fork this project
|
||||
3. Create a new branch that describes the new feature, enhancement, or bug fix. For example, this is good: `feature/add-xyz`. This is bad: `fix-this-lol`.
|
||||
4. Write the code that addresses your change.
|
||||
- Keep in mind that it **must** be formatted correctly. If you are using IntelliJ, there is a `codeStyle.xml` file that tells IntelliJ how to format your code. Check this link for information on how to use the file: https://www.jetbrains.com/help/idea/configuring-code-style.html#import-export-schemes
|
||||
- If you are not using IntelliJ, that is fine. We use the Plexus Code Style (which is almost the same as Allman) so please format your code accordingly.
|
||||
6. Push your changes to your new branch and make a PR based off of that branch.
|
||||
|
||||
## Requirements for a PR
|
||||
- The issue must be marked as approved
|
||||
- It must only address each specific issue. Don't make one PR for multiple issues.
|
||||
- Your PR must compile and work. If it does not compile or work, your PR will most likely be rejected.
|
||||
|
||||
## Code requirements
|
||||
- Most importantly, your code must be efficient. Your pull request may be rejected if your code is deemed inefficient or sloppy.
|
||||
- Do not repeat yourself. Create functions as needed if you're using large blocks of code over and over again.
|
||||
- Do not use an excessive amount of commits when making your PR. It makes the master branch look messy.
|
||||
- Your code must be consistent with Plex's codebase. If a function already exists, use it.
|
|
@ -1,2 +1,2 @@
|
|||
# Module-HTTPD
|
||||
# Module-HTTPD [![Build Status](https://ci.plex.us.org/job/Module-HTTPD/badge/icon)](https://ci.plex.us.org/job/Module-HTTPD/)
|
||||
The HTTPD module for Plex
|
||||
|
|
|
@ -5,91 +5,75 @@ plugins {
|
|||
}
|
||||
|
||||
group = "dev.plex"
|
||||
version = "1.0.1"
|
||||
version = "1.4-SNAPSHOT"
|
||||
description = "Module-HTTPD"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
|
||||
maven {
|
||||
url = uri("https://papermc.io/repo/repository/maven-public/")
|
||||
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||
}
|
||||
|
||||
maven {
|
||||
url = uri("https://nexus.telesphoreo.me/repository/plex/")
|
||||
}
|
||||
|
||||
maven {
|
||||
url = uri("https://nexus.telesphoreo.me/repository/totalfreedom/")
|
||||
}
|
||||
maven {
|
||||
url = uri("https://jitpack.io")
|
||||
content {
|
||||
includeGroup("com.github.MilkBowl")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.projectlombok:lombok:1.18.22")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.22")
|
||||
implementation("io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT")
|
||||
implementation("dev.plex:Plex:1.0")
|
||||
implementation("org.json:json:20220320")
|
||||
implementation("org.projectlombok:lombok:1.18.30")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
implementation("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
|
||||
implementation("dev.plex:server:1.4-SNAPSHOT")
|
||||
implementation("org.json:json:20231013")
|
||||
implementation("org.reflections:reflections:0.10.2")
|
||||
implementation("org.eclipse.jetty:jetty-server:11.0.8")
|
||||
implementation("org.eclipse.jetty:jetty-servlet:11.0.8")
|
||||
implementation("org.eclipse.jetty:jetty-proxy:11.0.8")
|
||||
implementation("com.github.MilkBowl:VaultAPI:1.7")
|
||||
implementation("org.eclipse.jetty:jetty-server:11.0.19")
|
||||
implementation("org.eclipse.jetty:jetty-servlet:11.0.19")
|
||||
implementation("org.eclipse.jetty:jetty-proxy:11.0.19")
|
||||
implementation("com.github.MilkBowl:VaultAPI:1.7.1") {
|
||||
exclude("org.bukkit", "bukkit")
|
||||
}
|
||||
implementation(platform("com.intellectualsites.bom:bom-newest:1.40")) // Ref: https://github.com/IntellectualSites/bom
|
||||
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Core")
|
||||
implementation("commons-io:commons-io:2.15.1")
|
||||
}
|
||||
|
||||
tasks.getByName<Jar>("jar") {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveBaseName.set("Module-HTTPD")
|
||||
archiveVersion.set("")
|
||||
from("src/main/resources") {
|
||||
exclude("dev/**")
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
tasks {
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||
|
||||
// Set the release flag. This configures what version bytecode the compiler will emit, as well as what JDK APIs are usable.
|
||||
// See https://openjdk.java.net/jeps/247 for more information.
|
||||
options.release.set(17)
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
}
|
||||
javadoc {
|
||||
options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
}
|
||||
processResources {
|
||||
filteringCharset = Charsets.UTF_8.name() // We want UTF-8 for everything
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.getByName<Jar>("jar") {
|
||||
// duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
archiveBaseName.set("Plex-HTTPD")
|
||||
archiveVersion.set("")
|
||||
// from("src/main/resources") {
|
||||
// include("**/**")
|
||||
// }
|
||||
// from("src/main/java") {
|
||||
// include("**/**")
|
||||
// }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDirs("src/main/java", "src/main/resources")
|
||||
include("**/**")
|
||||
exclude("**/**.java")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.getByName<Copy>("processResources") {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -1,5 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
@ -80,13 +80,11 @@ do
|
|||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
@ -133,22 +131,29 @@ location of your Java installation."
|
|||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
@ -193,6 +198,10 @@ if "$cygwin" || "$msys" ; then
|
|||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
|
@ -205,6 +214,12 @@ set -- \
|
|||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
|
@ -25,7 +25,8 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
package dev.plex;
|
||||
|
||||
import dev.plex.authentication.AuthenticationManager;
|
||||
import dev.plex.cache.FileCache;
|
||||
import dev.plex.config.ModuleConfig;
|
||||
import dev.plex.module.PlexModule;
|
||||
import dev.plex.request.impl.AdminsEndpoint;
|
||||
import dev.plex.request.impl.IndefBansEndpoint;
|
||||
import dev.plex.request.impl.ListEndpoint;
|
||||
import dev.plex.request.impl.PunishmentsEndpoint;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.SchematicUploadServlet;
|
||||
import dev.plex.request.impl.*;
|
||||
import dev.plex.util.PlexLog;
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import jakarta.servlet.MultipartConfigElement;
|
||||
import lombok.Getter;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.*;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class HTTPDModule extends PlexModule
|
||||
{
|
||||
|
@ -34,10 +32,17 @@ public class HTTPDModule extends PlexModule
|
|||
|
||||
public static ModuleConfig moduleConfig;
|
||||
|
||||
public static final FileCache fileCache = new FileCache();
|
||||
|
||||
public static final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html"));
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Override
|
||||
public void load()
|
||||
{
|
||||
moduleConfig = new ModuleConfig(this, "settings.yml");
|
||||
// Move it from /httpd/config.yml to /plugins/Plex/modules/Plex-HTTPD/config.yml
|
||||
moduleConfig = new ModuleConfig(this, "httpd/config.yml", "config.yml");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,10 +50,22 @@ public class HTTPDModule extends PlexModule
|
|||
{
|
||||
moduleConfig.load();
|
||||
PlexLog.debug("HTTPD Module Port: {0}", moduleConfig.getInt("server.port"));
|
||||
if (!setupPermissions() && getPlex().getSystem().equalsIgnoreCase("permissions") && !Bukkit.getPluginManager().isPluginEnabled("Vault"))
|
||||
if ((!Bukkit.getPluginManager().isPluginEnabled("Vault") || !setupPermissions()))
|
||||
{
|
||||
throw new RuntimeException("Plex-HTTPD requires the 'Vault' plugin as well as a Permissions plugin that hooks into 'Vault'. We recommend LuckPerms!");
|
||||
}
|
||||
|
||||
this.authenticationManager = new AuthenticationManager();
|
||||
if (this.authenticationManager.provider() != null)
|
||||
{
|
||||
PlexLog.debug(this.authenticationManager.provider().generateLogin());
|
||||
}
|
||||
else
|
||||
{
|
||||
PlexLog.debug("Provider was not found for Authentication so disabled");
|
||||
}
|
||||
|
||||
|
||||
serverThread = new Thread(() ->
|
||||
{
|
||||
Server server = new Server();
|
||||
|
@ -62,10 +79,22 @@ public class HTTPDModule extends PlexModule
|
|||
connector.setHost(moduleConfig.getString("server.bind-address"));
|
||||
connector.setPort(moduleConfig.getInt("server.port"));
|
||||
|
||||
new AdminsEndpoint();
|
||||
new IndefBansEndpoint();
|
||||
new IndexEndpoint();
|
||||
new ListEndpoint();
|
||||
new PunishmentsEndpoint();
|
||||
new CommandsEndpoint();
|
||||
new SchematicDownloadEndpoint();
|
||||
new SchematicUploadEndpoint();
|
||||
|
||||
ServletHolder uploadHolder = HTTPDModule.context.addServlet(SchematicUploadServlet.class, "/api/schematics/uploading");
|
||||
|
||||
File uploadLoc = new File(System.getProperty("java.io.tmpdir"), "schematic-temp-dir");
|
||||
if (!uploadLoc.exists())
|
||||
{
|
||||
uploadLoc.mkdirs();
|
||||
}
|
||||
uploadHolder.getRegistration().setMultipartConfig(new MultipartConfigElement(uploadLoc.getAbsolutePath(), 1024 * 1024 * 5, 1024 * 1024 * 25, 1024 * 1024));
|
||||
|
||||
server.setConnectors(new Connector[]{connector});
|
||||
server.setHandler(context);
|
||||
|
@ -106,4 +135,34 @@ public class HTTPDModule extends PlexModule
|
|||
permissions = rsp.getProvider();
|
||||
return permissions != null;
|
||||
}
|
||||
|
||||
public static File getWorldeditFolder()
|
||||
{
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("WorldEdit"))
|
||||
{
|
||||
return new File(Bukkit.getPluginManager().getPlugin("WorldEdit").getDataFolder() + "/schematics/");
|
||||
}
|
||||
else if (Bukkit.getPluginManager().isPluginEnabled("FastAsyncWorldEdit"))
|
||||
{
|
||||
return new File(Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit").getDataFolder() + "/schematics/");
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFileSystemCaseSensitive = !new File("a").equals(new File("A"));
|
||||
|
||||
public static boolean fileNameEquals(String filename1, String filename2)
|
||||
{
|
||||
if (isFileSystemCaseSensitive)
|
||||
{
|
||||
return filename1.equals(filename2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return filename1.equalsIgnoreCase(filename2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package dev.plex.authentication;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 6:37 PM [03-05-2024]
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
public class AuthenticatedUser
|
||||
{
|
||||
private final String ip;
|
||||
private final ZonedDateTime lastAuthenticated;
|
||||
private final LinkedList<String> roles = Lists.newLinkedList();
|
||||
private final UserType userType = UserType.UNKNOWN;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package dev.plex.authentication;
|
||||
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.authentication.impl.DiscordOAuth2Provider;
|
||||
import dev.plex.util.PlexLog;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 7:08 PM [03-05-2024]
|
||||
*/
|
||||
public class AuthenticationManager
|
||||
{
|
||||
private final OAuth2Provider provider;
|
||||
|
||||
public AuthenticationManager()
|
||||
{
|
||||
final boolean enabled = HTTPDModule.moduleConfig.getBoolean("authentication.enabled", false);
|
||||
if (!enabled)
|
||||
{
|
||||
provider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
PlexLog.debug("[HTTPD] Auth is enabled");
|
||||
|
||||
final String providerName = HTTPDModule.moduleConfig.getString("authentication.provider.name", "");
|
||||
if (providerName.isEmpty())
|
||||
{
|
||||
PlexLog.error("OAuth2 Authentication is enabled but no provider was given!");
|
||||
provider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
PlexLog.debug("[HTTPD] Provider name is {0}", providerName);
|
||||
|
||||
switch (providerName.toLowerCase())
|
||||
{
|
||||
case "discord" -> {
|
||||
provider = new DiscordOAuth2Provider();
|
||||
}
|
||||
case "xenforo" -> {
|
||||
throw new NotImplementedException("XenForo OAuth2 is not implemented yet!");
|
||||
}
|
||||
default -> {
|
||||
provider = null;
|
||||
}
|
||||
}
|
||||
|
||||
PlexLog.log("Using {0} provider for authentication", providerName);
|
||||
}
|
||||
|
||||
public OAuth2Provider provider()
|
||||
{
|
||||
return this.provider;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package dev.plex.authentication;
|
||||
|
||||
import org.eclipse.jetty.server.Response;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 6:36 PM [03-05-2024]
|
||||
*/
|
||||
public interface OAuth2Provider
|
||||
{
|
||||
HashMap<String, AuthenticatedUser> sessions();
|
||||
|
||||
AuthenticatedUser login(Response response, UserType type);
|
||||
|
||||
String[] roles(AuthenticatedUser user);
|
||||
|
||||
String generateLogin();
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package dev.plex.authentication;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 6:37 PM [03-05-2024]
|
||||
*/
|
||||
public enum UserType
|
||||
{
|
||||
DISCORD, UNKNOWN
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package dev.plex.authentication.impl;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.authentication.AuthenticatedUser;
|
||||
import dev.plex.authentication.OAuth2Provider;
|
||||
import dev.plex.authentication.UserType;
|
||||
import dev.plex.util.PlexLog;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 6:41 PM [03-05-2024]
|
||||
*/
|
||||
public class DiscordOAuth2Provider implements OAuth2Provider
|
||||
{
|
||||
private final HashMap<String, AuthenticatedUser> sessions = Maps.newHashMap();
|
||||
|
||||
private final String token;
|
||||
private final String clientId;
|
||||
private final String redirectUri;
|
||||
|
||||
public DiscordOAuth2Provider()
|
||||
{
|
||||
token = System.getenv("BOT_TOKEN").isEmpty() ? HTTPDModule.moduleConfig.getString("authentication.provider.discord.token", System.getProperty("BOT_TOKEN", "")) : System.getenv("BOT_TOKEN");
|
||||
clientId = HTTPDModule.moduleConfig.getString("authentication.provider.discord.clientId", "");
|
||||
redirectUri = URLEncoder.encode(HTTPDModule.moduleConfig.getString("authentication.provider.redirectUri", ""), StandardCharsets.UTF_8);
|
||||
|
||||
PlexLog.debug("[HTTPD] Client ID: {0}, Redirect URL: {1}", clientId, redirectUri);
|
||||
|
||||
if (redirectUri.isEmpty())
|
||||
{
|
||||
PlexLog.error("Provided authentication redirect url was empty for HTTPD!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.isEmpty())
|
||||
{
|
||||
PlexLog.error("Provided discord authentication token was empty for HTTPD!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (clientId.isEmpty())
|
||||
{
|
||||
PlexLog.error("Provided discord client ID was empty for HTTPD!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<String, AuthenticatedUser> sessions()
|
||||
{
|
||||
return sessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticatedUser login(Response response, UserType type)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] roles(AuthenticatedUser user)
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateLogin()
|
||||
{
|
||||
return String.format("https://discord.com/oauth2/authorize?client_id=%s&scope=%s&redirect_uri=%s",
|
||||
clientId,
|
||||
"identify%20guilds%20guilds.members.read",
|
||||
redirectUri);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package dev.plex.cache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class CacheItem
|
||||
{
|
||||
public String path;
|
||||
public byte[] file;
|
||||
public long timestamp;
|
||||
|
||||
public CacheItem(File f) throws IOException
|
||||
{
|
||||
this.path = f.getPath();
|
||||
this.file = Files.readAllBytes(f.toPath());
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package dev.plex.cache;
|
||||
|
||||
import com.google.common.collect.EvictingQueue;
|
||||
import dev.plex.HTTPDModule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Queue;
|
||||
|
||||
public class FileCache
|
||||
{
|
||||
private final Queue<CacheItem> cache = EvictingQueue.create(15);
|
||||
|
||||
public byte[] getFile(File file) throws IOException
|
||||
{
|
||||
return getFile(file.getPath());
|
||||
}
|
||||
|
||||
public byte[] getFile(String path) throws IOException
|
||||
{
|
||||
CacheItem theItem = cache.stream().filter(cacheItem -> HTTPDModule.fileNameEquals(cacheItem.path, path)).findFirst().orElse(null);
|
||||
if (theItem == null)
|
||||
{
|
||||
theItem = new CacheItem(new File(path));
|
||||
if (theItem.file.length < 1048576) cache.add(theItem);
|
||||
}
|
||||
if (System.currentTimeMillis() - theItem.timestamp > 3 * 60 * 1000) // 3 minutes
|
||||
{
|
||||
cache.remove(theItem);
|
||||
theItem = new CacheItem(new File(path));
|
||||
if (theItem.file.length < 1048576) cache.add(theItem);
|
||||
}
|
||||
return theItem.file;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import dev.plex.HTTPDModule;
|
|||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
|
||||
public class Log
|
||||
{
|
||||
public static void log(String message, Object... strings)
|
||||
|
|
|
@ -3,15 +3,20 @@ package dev.plex.request;
|
|||
import com.google.common.collect.Lists;
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.logging.Log;
|
||||
import jakarta.servlet.AsyncContext;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
||||
|
@ -43,7 +48,7 @@ public class AbstractServlet extends HttpServlet
|
|||
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
String ipAddress = req.getRemoteAddr();
|
||||
if (ipAddress == null)
|
||||
if (ipAddress.equals("127.0.0.1"))
|
||||
{
|
||||
ipAddress = req.getHeader("X-FORWARDED-FOR");
|
||||
}
|
||||
|
@ -69,8 +74,11 @@ public class AbstractServlet extends HttpServlet
|
|||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
try
|
||||
{
|
||||
Object object = mapping.method.invoke(this, req);
|
||||
resp.getWriter().println(object.toString());
|
||||
Object object = mapping.method.invoke(this, req, resp);
|
||||
if (object != null)
|
||||
{
|
||||
resp.getWriter().println(object.toString());
|
||||
}
|
||||
}
|
||||
catch (IOException | IllegalAccessException | InvocationTargetException e)
|
||||
{
|
||||
|
@ -79,6 +87,62 @@ public class AbstractServlet extends HttpServlet
|
|||
});
|
||||
}
|
||||
|
||||
public static String readFile(InputStream filename)
|
||||
{
|
||||
String base = HTTPDModule.template;
|
||||
String page = readFileReal(filename);
|
||||
String[] info = page.split("\n", 3);
|
||||
base = base.replace("${TITLE}", info[0]);
|
||||
base = base.replace("${ACTIVE_" + info[1] + "}", "active\" aria-current=\"page");
|
||||
base = base.replace("${ACTIVE_HOME}", "");
|
||||
base = base.replace("${ACTIVE_ADMINS}", "");
|
||||
base = base.replace("${ACTIVE_INDEFBANS}", "");
|
||||
base = base.replace("${ACTIVE_LIST}", "");
|
||||
base = base.replace("${ACTIVE_COMMANDS}", "");
|
||||
base = base.replace("${ACTIVE_PUNISHMENTS}", "");
|
||||
base = base.replace("${ACTIVE_SCHEMATICS}", "");
|
||||
base = base.replace("${CONTENT}", info[2]);
|
||||
return base;
|
||||
}
|
||||
|
||||
public static String readFileReal(InputStream filename)
|
||||
{
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
try
|
||||
{
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(Objects.requireNonNull(filename)));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null)
|
||||
{
|
||||
contentBuilder.append(str).append("\n");
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
|
||||
// Code from https://programming.guide/java/formatting-byte-size-to-human-readable-format.html
|
||||
public static String formattedSize(long bytes)
|
||||
{
|
||||
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
|
||||
if (absB < 1024)
|
||||
{
|
||||
return bytes + " B";
|
||||
}
|
||||
long value = absB;
|
||||
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
|
||||
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10)
|
||||
{
|
||||
value >>= 10;
|
||||
ci.next();
|
||||
}
|
||||
value *= Long.signum(bytes);
|
||||
return String.format("%.1f %ciB", value / 1024.0, ci.current());
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Mapping
|
||||
{
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package dev.plex.request;
|
||||
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.cache.DataUtils;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.util.PlexLog;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.Part;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SchematicUploadServlet extends HttpServlet
|
||||
{
|
||||
private static final Pattern schemNameMatcher = Pattern.compile("^[a-z0-9'!,_ -]{1,30}\\.schem(atic)?$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getRemoteAddr() == null)
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("Your IP address could not be detected. Please ensure you are using IPv4."));
|
||||
return;
|
||||
}
|
||||
PlexPlayer plexPlayer = DataUtils.getPlayerByIP(request.getRemoteAddr());
|
||||
if (plexPlayer == null)
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("Couldn't load your IP Address: " + request.getRemoteAddr() + ". Have you joined the server before?"));
|
||||
return;
|
||||
}
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(plexPlayer.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.schematics.upload"))
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("You do not have permission to upload schematics."));
|
||||
return;
|
||||
}
|
||||
File worldeditFolder = HTTPDModule.getWorldeditFolder();
|
||||
if (worldeditFolder == null)
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("Worldedit is not installed!"));
|
||||
return;
|
||||
}
|
||||
File[] schematics = worldeditFolder.listFiles();
|
||||
Part uploadPart;
|
||||
try
|
||||
{
|
||||
uploadPart = request.getPart("file");
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("That schematic is too large!"));
|
||||
return;
|
||||
}
|
||||
String filename = uploadPart.getSubmittedFileName().replaceAll("[^a-zA-Z0-9'!,_ .-]", "_");
|
||||
if (!schemNameMatcher.matcher(filename).matches())
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("That is not a valid schematic filename!"));
|
||||
return;
|
||||
}
|
||||
boolean alreadyExists = schematics != null && Arrays.stream(schematics).anyMatch(file -> HTTPDModule.fileNameEquals(file.getName(), filename));
|
||||
if (alreadyExists)
|
||||
{
|
||||
response.getWriter().println(schematicUploadBadHTML("A schematic with the name <b>" + filename + "</b> already exists!"));
|
||||
return;
|
||||
}
|
||||
InputStream inputStream = uploadPart.getInputStream();
|
||||
File schematicFile = new File(worldeditFolder, filename);
|
||||
FileUtils.copyInputStreamToFile(inputStream, schematicFile);
|
||||
ClipboardFormat schematicFormat = ClipboardFormats.findByFile(schematicFile);
|
||||
if (schematicFormat == null)
|
||||
{
|
||||
PlexLog.log("IP Address: " + request.getRemoteAddr() + " FAILED to upload schematic with filename: " + filename);
|
||||
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
||||
FileUtils.deleteQuietly(schematicFile);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
schematicFormat.getReader(new FileInputStream(schematicFile));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
PlexLog.log("IP Address: " + request.getRemoteAddr() + " FAILED to upload schematic with filename: " + filename);
|
||||
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
||||
FileUtils.deleteQuietly(schematicFile);
|
||||
return;
|
||||
}
|
||||
// Files.copy(inputStream, schematic.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
inputStream.close();
|
||||
response.getWriter().println(schematicUploadGoodHTML("Successfully uploaded <b>" + filename + "</b>."));
|
||||
PlexLog.log("IP Address: " + request.getRemoteAddr() + " uploaded schematic with filename: " + filename);
|
||||
}
|
||||
|
||||
private String schematicUploadBadHTML(String message)
|
||||
{
|
||||
String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
|
||||
private String schematicUploadGoodHTML(String message)
|
||||
{
|
||||
String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_good.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.cache.DataUtils;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.rank.enums.Rank;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import dev.plex.util.adapter.LocalDateTimeSerializer;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class AdminsEndpoint extends AbstractServlet
|
||||
{
|
||||
@GetMapping(endpoint = "/api/admins/")
|
||||
public String getAdmins(HttpServletRequest request)
|
||||
{
|
||||
String ipAddress = request.getRemoteAddr();
|
||||
if (ipAddress == null)
|
||||
{
|
||||
return "An IP address could not be detected. Please ensure you are connecting using IPv4.";
|
||||
}
|
||||
final PlexPlayer player = DataUtils.getPlayerByIP(ipAddress);
|
||||
if (player == null)
|
||||
{
|
||||
// This likely means they've never joined the server before. That's okay. We can just not return IPs.
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(Plex.get().getAdminList().getAllAdminPlayers().stream().peek(plexPlayer -> plexPlayer.setIps(Lists.newArrayList())).peek(plexPlayer -> plexPlayer.setPunishments(Lists.newArrayList())).collect(Collectors.toList()));
|
||||
}
|
||||
if (Plex.get().getSystem().equalsIgnoreCase("ranks"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using ranks check");
|
||||
if (!player.getRankFromString().isAtLeast(Rank.ADMIN))
|
||||
{
|
||||
// Don't return IPs either if the person is not an Admin or above.
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(Plex.get().getAdminList().getAllAdminPlayers().stream().peek(plexPlayer -> plexPlayer.setIps(Lists.newArrayList())).peek(plexPlayer -> plexPlayer.setPunishments(Lists.newArrayList())).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
else if (Plex.get().getSystem().equalsIgnoreCase("permissions"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.admins.access"))
|
||||
{
|
||||
// If the person doesn't have permission, don't return IPs
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(Plex.get().getAdminList().getAllAdminPlayers().stream().peek(plexPlayer -> plexPlayer.setIps(Lists.newArrayList())).peek(plexPlayer -> plexPlayer.setPunishments(Lists.newArrayList())).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(Plex.get().getAdminList().getAllAdminPlayers());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import dev.plex.command.PlexCommand;
|
||||
import dev.plex.command.annotation.CommandPermissions;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class AuthenticationEndpoint extends AbstractServlet
|
||||
{
|
||||
|
||||
|
||||
@GetMapping(endpoint = "/oauth2")
|
||||
public String login(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
// TODO: Nuh uh
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import dev.plex.command.PlexCommand;
|
||||
import dev.plex.command.annotation.CommandPermissions;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
|
||||
public class CommandsEndpoint extends AbstractServlet
|
||||
{
|
||||
|
||||
private final StringBuilder list = new StringBuilder();
|
||||
private boolean loadedCommands = false;
|
||||
|
||||
@GetMapping(endpoint = "/api/commands/")
|
||||
public String getCommands(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
if (!loadedCommands)
|
||||
{
|
||||
final SortedMap<String, List<Command>> commandMap = new TreeMap<>();
|
||||
final CommandMap map = Bukkit.getCommandMap();
|
||||
for (Command command : map.getKnownCommands().values())
|
||||
{
|
||||
String plugin = "Bukkit";
|
||||
if (command instanceof PluginIdentifiableCommand)
|
||||
{
|
||||
plugin = ((PluginIdentifiableCommand) command).getPlugin().getName();
|
||||
}
|
||||
|
||||
List<Command> pluginCommands = commandMap.computeIfAbsent(plugin, k -> new ArrayList<>());
|
||||
if (!pluginCommands.contains(command))
|
||||
{
|
||||
pluginCommands.add(command);
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : commandMap.keySet())
|
||||
{
|
||||
commandMap.get(key).sort(Comparator.comparing(Command::getName));
|
||||
StringBuilder rows = new StringBuilder();
|
||||
for (Command command : commandMap.get(key))
|
||||
{
|
||||
String permission = command.getPermission();
|
||||
if (command instanceof PlexCommand plexCmd)
|
||||
{
|
||||
CommandPermissions perms = plexCmd.getClass().getAnnotation(CommandPermissions.class);
|
||||
if (perms != null)
|
||||
{
|
||||
permission = (perms.permission().isBlank() ? "N/A" : perms.permission());
|
||||
}
|
||||
}
|
||||
|
||||
rows.append(createRow(command.getName(), command.getAliases(), command.getDescription(), command.getUsage(), permission));
|
||||
}
|
||||
|
||||
list.append(createTable(key, rows.toString())).append("\n");
|
||||
}
|
||||
|
||||
loadedCommands = true;
|
||||
}
|
||||
|
||||
return commandsHTML(list.toString());
|
||||
}
|
||||
|
||||
private String commandsHTML(String commandsList)
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/commands.html"));
|
||||
file = file.replace("${commands}", commandsList);
|
||||
return file;
|
||||
}
|
||||
|
||||
private String createTable(String pluginName, String commandRows)
|
||||
{
|
||||
return "<details id=\"" + pluginName + "\"><summary>" + pluginName + "</summary>\n"
|
||||
+ "<table id=\"" + pluginName + "Table\" class=\"table table-striped table-bordered\">\n"
|
||||
+ " <thead>\n <tr>\n <th scope=\"col\">Name (Aliases)</th>\n "
|
||||
+ "<th scope=\"col\">Description</th>\n "
|
||||
+ "<th scope=\"col\">Usage</th>\n "
|
||||
+ "<th scope=\"col\">Permission</th>\n </tr>\n</thead>\n"
|
||||
+ "<tbody>\n " + commandRows + "\n</tbody>\n</table>\n</details>";
|
||||
}
|
||||
|
||||
private String createRow(String name, List<String> aliases, String description, String usage, String permission)
|
||||
{
|
||||
return " <tr>\n <th scope=\"row\">" + name
|
||||
+ (aliases.isEmpty() || aliases.toString().equals("[]") ? "" : " (" + String.join(", ", aliases) + ")") + "</th>\n"
|
||||
+ " <th scope=\"row\">" + description + "</th>\n"
|
||||
+ " <th scope=\"row\"><code>" + cleanUsage(usage) + "</code></th>\n"
|
||||
+ " <th scope=\"row\">" + (permission != null ? permission.replaceAll(";", "<br>") : "N/A") + "</th>\n </tr>";
|
||||
}
|
||||
|
||||
private String cleanUsage(String usage)
|
||||
{
|
||||
usage = usage.replaceAll("<", "<").replaceAll(">", ">");
|
||||
if (usage.isBlank())
|
||||
{
|
||||
usage = "Not Provided";
|
||||
}
|
||||
return usage.startsWith("/") || usage.equals("Not Provided") ? usage : "/" + usage;
|
||||
}
|
||||
}
|
|
@ -5,47 +5,46 @@ import dev.plex.HTTPDModule;
|
|||
import dev.plex.Plex;
|
||||
import dev.plex.cache.DataUtils;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.rank.enums.Rank;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.UUID;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class IndefBansEndpoint extends AbstractServlet
|
||||
{
|
||||
private static final String TITLE = "Indefinite Bans - Plex HTTPD";
|
||||
|
||||
@GetMapping(endpoint = "/api/indefbans/")
|
||||
public String getBans(HttpServletRequest request)
|
||||
public String getBans(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
String ipAddress = request.getRemoteAddr();
|
||||
if (ipAddress == null)
|
||||
{
|
||||
return "An IP address could not be detected. Please ensure you are connecting using IPv4.";
|
||||
return indefbansHTML("An IP address could not be detected. Please ensure you are connecting using IPv4.");
|
||||
}
|
||||
final PlexPlayer player = DataUtils.getPlayerByIP(ipAddress);
|
||||
if (player == null)
|
||||
{
|
||||
return "Couldn't load your IP Address: " + ipAddress + ". Have you joined the server before?";
|
||||
return indefbansHTML("Couldn't load your IP Address: " + ipAddress + ". Have you joined the server before?");
|
||||
}
|
||||
if (Plex.get().getSystem().equalsIgnoreCase("ranks"))
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.indefbans.access"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using ranks check");
|
||||
if (!player.getRankFromString().isAtLeast(Rank.ADMIN))
|
||||
{
|
||||
return "Not a high enough rank to view this page.";
|
||||
}
|
||||
}
|
||||
else if (Plex.get().getSystem().equalsIgnoreCase("permissions"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.indefbans.access"))
|
||||
{
|
||||
return "Not enough permissions to view this page.";
|
||||
}
|
||||
return indefbansHTML("Not enough permissions to view this page.");
|
||||
}
|
||||
|
||||
response.setHeader("content-type", "application/json");
|
||||
return new GsonBuilder().setPrettyPrinting().create().toJson(Plex.get().getPunishmentManager().getIndefiniteBans().stream().toList());
|
||||
}
|
||||
|
||||
private String indefbansHTML(String message)
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/indefbans.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class IndexEndpoint extends AbstractServlet
|
||||
{
|
||||
@GetMapping(endpoint = "//")
|
||||
public String getIndex(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
return indexHTML();
|
||||
}
|
||||
|
||||
@GetMapping(endpoint = "/api/")
|
||||
public String getAPI(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
return indexHTML();
|
||||
}
|
||||
|
||||
private String indexHTML()
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/index.html"));
|
||||
String isAre = Bukkit.getOnlinePlayers().size() == 1 ? " is " : " are ";
|
||||
String pluralOnline = Bukkit.getOnlinePlayers().size() == 1 ? " player " : " players ";
|
||||
String pluralMax = Bukkit.getMaxPlayers() == 1 ? " player " : " players ";
|
||||
file = file.replace("${is_are}", isAre);
|
||||
file = file.replace("${server_online_players}", Bukkit.getOnlinePlayers().size() + pluralOnline);
|
||||
file = file.replace("${server_total_players}", Bukkit.getMaxPlayers() + pluralMax);
|
||||
return file;
|
||||
}
|
||||
}
|
|
@ -3,19 +3,22 @@ package dev.plex.request.impl;
|
|||
import com.google.gson.GsonBuilder;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.request.MappingHeaders;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ListEndpoint extends AbstractServlet
|
||||
{
|
||||
List<String> players = new ArrayList<>();
|
||||
|
||||
@GetMapping(endpoint = "/api/list/")
|
||||
public String getOnlinePlayers(HttpServletRequest request)
|
||||
@MappingHeaders(headers = "content-type;application/json")
|
||||
public String getOnlinePlayers(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
List<String> players = new ArrayList<>();
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
{
|
||||
players.add(player.getName());
|
||||
|
|
|
@ -2,69 +2,31 @@ package dev.plex.request.impl;
|
|||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.Plex;
|
||||
import dev.plex.cache.DataUtils;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.rank.enums.Rank;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import dev.plex.util.adapter.LocalDateTimeSerializer;
|
||||
import dev.plex.util.adapter.ZonedDateTimeAdapter;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PunishmentsEndpoint extends AbstractServlet
|
||||
{
|
||||
@GetMapping(endpoint = "/api/punishments/")
|
||||
public String getPunishments(HttpServletRequest request)
|
||||
public String getPunishments(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
String ipAddress = request.getRemoteAddr();
|
||||
if (ipAddress == null)
|
||||
{
|
||||
return "An IP address could not be detected. Please ensure you are connecting using IPv4.";
|
||||
return punishmentsHTML("An IP address could not be detected. Please ensure you are connecting using IPv4.");
|
||||
}
|
||||
if (request.getPathInfo() == null)
|
||||
if (request.getPathInfo() == null || request.getPathInfo().equals("/"))
|
||||
{
|
||||
/*StringBuilder contentBuilder = new StringBuilder();
|
||||
PlexLog.log(this.getClass().getClassLoader().getResource("punishments.html").getPath());
|
||||
try
|
||||
{
|
||||
BufferedReader in = new BufferedReader(new FileReader(this.getClass().getClassLoader().getResource("punishments.html").getFile().replace("!", "")));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null)
|
||||
{
|
||||
contentBuilder.append(str);
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
return contentBuilder.toString();*/
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<body>
|
||||
<div style="text-align: center;">
|
||||
<h2 style="font-family:Helvetica">Enter the UUID or username of the player you want to check</h2>
|
||||
<input id="test" type="text" autofocus>
|
||||
<button type="button" onclick="redirect()">Submit</button>
|
||||
|
||||
<script>
|
||||
function redirect() {
|
||||
var url = document.getElementById('test').value
|
||||
window.location = "punishments/" + url
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>""";
|
||||
return readFile(this.getClass().getResourceAsStream("/httpd/punishments.html"));
|
||||
}
|
||||
UUID pathUUID;
|
||||
String pathPlexPlayer;
|
||||
|
@ -83,36 +45,39 @@ public class PunishmentsEndpoint extends AbstractServlet
|
|||
final PlexPlayer player = DataUtils.getPlayerByIP(ipAddress);
|
||||
if (punishedPlayer == null)
|
||||
{
|
||||
return "This player has never joined the server before.";
|
||||
return punishmentsHTML("This player has never joined the server before.");
|
||||
}
|
||||
if (punishedPlayer.getPunishments().isEmpty())
|
||||
{
|
||||
return "This player has been a good boy. They have no punishments!";
|
||||
return punishmentsGoodHTML("This player has been a good boy. They have no punishments!");
|
||||
}
|
||||
if (player == null)
|
||||
{
|
||||
// If the player is null, give it to them without the IPs
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().peek(punishment -> punishment.setIp("")).toList());
|
||||
return new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().peek(punishment -> punishment.setIp("")).toList());
|
||||
}
|
||||
if (Plex.get().getSystem().equalsIgnoreCase("ranks"))
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.punishments.access"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using ranks check");
|
||||
if (!player.getRankFromString().isAtLeast(Rank.ADMIN))
|
||||
{
|
||||
// Don't return IPs either if the person is not an Admin or above.
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().peek(punishment -> punishment.setIp("")).toList());
|
||||
}
|
||||
// If the person doesn't have permission, don't return IPs
|
||||
return new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().peek(punishment -> punishment.setIp("")).toList());
|
||||
}
|
||||
else if (Plex.get().getSystem().equalsIgnoreCase("permissions"))
|
||||
{
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.punishments.access"))
|
||||
{
|
||||
// If the person doesn't have permission, don't return IPs
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().peek(punishment -> punishment.setIp("")).toList());
|
||||
}
|
||||
}
|
||||
return new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().toList());
|
||||
|
||||
response.setHeader("content-type", "application/json");
|
||||
return new GsonBuilder().registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter()).setPrettyPrinting().create().toJson(punishedPlayer.getPunishments().stream().toList());
|
||||
}
|
||||
|
||||
private String punishmentsHTML(String message)
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/punishments_error.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
|
||||
private String punishmentsGoodHTML(String message)
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/punishments_good.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SchematicDownloadEndpoint extends AbstractServlet
|
||||
{
|
||||
List<File> files = new ArrayList<>();
|
||||
|
||||
@GetMapping(endpoint = "/api/schematics/download/")
|
||||
public String downloadSchematic(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
if (request.getPathInfo() == null || request.getPathInfo().equals("/"))
|
||||
{
|
||||
return schematicHTML();
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputStream outputStream;
|
||||
try
|
||||
{
|
||||
outputStream = response.getOutputStream();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
schematicServe(request.getPathInfo().replace("/", ""), outputStream);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void schematicServe(String requestedSchematic, OutputStream outputStream)
|
||||
{
|
||||
File worldeditFolder = HTTPDModule.getWorldeditFolder();
|
||||
if (worldeditFolder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
File[] schems = worldeditFolder.listFiles();
|
||||
if (schems != null)
|
||||
{
|
||||
File schemFile = Arrays.stream(schems).filter(file -> file.getName().equals(requestedSchematic)).findFirst().orElse(null);
|
||||
if (schemFile != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] schemData = HTTPDModule.fileCache.getFile(schemFile);
|
||||
if (schemData != null)
|
||||
{
|
||||
outputStream.write(schemData);
|
||||
}
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String schematicHTML()
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/schematic_download.html"));
|
||||
File worldeditFolder = HTTPDModule.getWorldeditFolder();
|
||||
if (worldeditFolder == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (File worldeditFile : listFilesForFolder(worldeditFolder))
|
||||
{
|
||||
String fixedPath = worldeditFile.getPath().replace("plugins/FastAsyncWorldEdit/schematics/", "");
|
||||
fixedPath = fixedPath.replace("plugins/WorldEdit/schematics/", "");
|
||||
String sanitizedName = fixedPath.replaceAll("<", "<").replaceAll(">", ">");
|
||||
sb.append(" <tr>\n" +
|
||||
" <th scope=\"row\">\n <a href=\"" + fixedPath + "\" download>" + sanitizedName + "</a>\n </th>\n" +
|
||||
" <td>\n " + formattedSize(worldeditFile.length()) + "\n </td>\n" +
|
||||
" </tr>\n");
|
||||
}
|
||||
file = file.replace("${schematics}", sb.toString());
|
||||
files.clear();
|
||||
return file;
|
||||
}
|
||||
|
||||
public List<File> listFilesForFolder(final File folder)
|
||||
{
|
||||
for (File fileEntry : folder.listFiles())
|
||||
{
|
||||
if (fileEntry.isDirectory())
|
||||
{
|
||||
PlexLog.debug("Found directory");
|
||||
listFilesForFolder(fileEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
files.add(fileEntry);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package dev.plex.request.impl;
|
||||
|
||||
import dev.plex.HTTPDModule;
|
||||
import dev.plex.cache.DataUtils;
|
||||
import dev.plex.player.PlexPlayer;
|
||||
import dev.plex.request.AbstractServlet;
|
||||
import dev.plex.request.GetMapping;
|
||||
import dev.plex.util.PlexLog;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class SchematicUploadEndpoint extends AbstractServlet
|
||||
{
|
||||
@GetMapping(endpoint = "/api/schematics/upload/")
|
||||
public String uploadSchematic(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
String ipAddress = request.getRemoteAddr();
|
||||
if (ipAddress == null)
|
||||
{
|
||||
return schematicsHTML("An IP address could not be detected. Please ensure you are connecting using IPv4.");
|
||||
}
|
||||
final PlexPlayer player = DataUtils.getPlayerByIP(ipAddress);
|
||||
if (player == null)
|
||||
{
|
||||
return schematicsHTML("Couldn't load your IP Address: " + ipAddress + ". Have you joined the server before?");
|
||||
}
|
||||
PlexLog.debug("Plex-HTTPD using permissions check");
|
||||
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
if (!HTTPDModule.getPermissions().playerHas(null, offlinePlayer, "plex.httpd.schematics.upload"))
|
||||
{
|
||||
return schematicsHTML("You do not have permission to upload schematics.");
|
||||
}
|
||||
return readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload.html"));
|
||||
}
|
||||
|
||||
private String schematicsHTML(String message)
|
||||
{
|
||||
String file = readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html"));
|
||||
file = file.replace("${MESSAGE}", message);
|
||||
return file;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/Telesphoreo/bootstrap-color-switcher@master/script.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@200;400&display=swap" rel="stylesheet">
|
||||
<title>${TITLE} - Plex HTTPD</title>
|
||||
</head>
|
||||
<body style="font-family: 'IBM Plex Sans', sans-serif;">
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/" style="font-weight:200;">Plex HTTPD</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_HOME}" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_INDEFBANS}" href="/api/indefbans/">Indefinite Bans</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_LIST}" href="/api/list/">List</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_PUNISHMENTS}" href="/api/punishments/">Punishments</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle ${ACTIVE_SCHEMATICS}" id="navbarDropdownMenuLink" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Schematics
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||
<li><a class="dropdown-item" href="/api/schematics/download/">Download</a></li>
|
||||
<li><a class="dropdown-item" href="/api/schematics/upload/">Upload</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="text-align: center;" class="col-auto m-0 row justify-content-center p-4">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
Commands
|
||||
COMMANDS
|
||||
<style>
|
||||
summary {
|
||||
font-size: 24px;
|
||||
padding: 16px;
|
||||
}
|
||||
</style>
|
||||
<h2>Commands List</h2>
|
||||
<h5>A list of commands is below.</h5>
|
||||
<br><br>
|
||||
${commands}
|
|
@ -0,0 +1,15 @@
|
|||
server:
|
||||
bind-address: 0.0.0.0
|
||||
port: 27192
|
||||
logging: false
|
||||
|
||||
authentication:
|
||||
enabled: false
|
||||
|
||||
# Providers: discord
|
||||
provider:
|
||||
name: discord
|
||||
redirectUri: ""
|
||||
discord: # Fill if using discord provider
|
||||
clientId: ""
|
||||
token: "" # Can also use environment variable or system property BOT_TOKEN
|
|
@ -0,0 +1,4 @@
|
|||
Indefinite Bans
|
||||
INDEFBANS
|
||||
<h2>Indefinite Bans</h2>
|
||||
<h5 class="alert alert-danger mb-3 w-auto p-3" role="alert"><b>Error:</b> ${MESSAGE}</h5>
|
|
@ -0,0 +1,5 @@
|
|||
Home
|
||||
HOME
|
||||
<h2>Welcome to the Plex HTTPD!</h2>
|
||||
<h4>Use the sidebar to navigate the available pages.</h4>
|
||||
<h4><br>There ${is_are} currently ${server_online_players} online out of ${server_total_players} total.</h4>
|
|
@ -0,0 +1,23 @@
|
|||
Punishments
|
||||
PUNISHMENTS
|
||||
<h2>Punishment Search</h2>
|
||||
<label for="uuid"><h5>Enter the UUID or username of the player you want to lookup</h5></label>
|
||||
<div class="input-group mb-3 w-75 p-3">
|
||||
<input id="uuid" type="text" autocomplete="off" autofocus class="form-control">
|
||||
<button class="btn btn-outline-primary" type="submit"
|
||||
onclick="redirect();">Submit
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
function redirect() {
|
||||
const url = document.getElementById('uuid').value;
|
||||
window.location = "/api/punishments/" + url
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.getElementById('uuid').addEventListener('keypress', function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
redirect();
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,4 @@
|
|||
Punishments
|
||||
PUNISHMENTS
|
||||
<h2>Punishment Search</h2>
|
||||
<h5 class="alert alert-danger mb-3 w-auto p-3" role="alert"><b>Error:</b> ${MESSAGE}</h5>
|
|
@ -0,0 +1,4 @@
|
|||
Punishments
|
||||
PUNISHMENTS
|
||||
<h2>Punishment Search</h2>
|
||||
<h5 class="alert alert-success mb-3 w-auto p-3" role="alert">${MESSAGE}</h5>
|
|
@ -0,0 +1,35 @@
|
|||
Schematics
|
||||
SCHEMATICS
|
||||
<h2>Schematic Download</h2>
|
||||
<label for="schemList"><h5>A list of schematics is below. You can click on the schematic name to download it.</h5>
|
||||
</label>
|
||||
<div class="input-group mb-3 w-75 p-3">
|
||||
<input type="text" autocomplete="off" autofocus class="form-control" oninput="filterTable(this.value)"
|
||||
placeholder="Search for a schematic...">
|
||||
</div>
|
||||
<table id="schemList" class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${schematics}
|
||||
</tbody>
|
||||
</table>
|
||||
<script>
|
||||
let schemList = document.getElementById("schemList").getElementsByTagName("tbody")[0].getElementsByTagName("tr");
|
||||
|
||||
function filterTable(query) {
|
||||
for (let i = 0; i < schemList.length; i++) {
|
||||
let th = schemList[i].getElementsByTagName("th")[0];
|
||||
let schemName = th.textContent || th.innerText;
|
||||
if (schemName.toLowerCase().includes(query.toLowerCase())) {
|
||||
schemList[i].style.display = "";
|
||||
} else {
|
||||
schemList[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,13 @@
|
|||
Schematics
|
||||
SCHEMATICS
|
||||
<h2>Schematic Upload</h2>
|
||||
<div class="cos-xs-8 col-lg-5">
|
||||
<label for="formFile" class="form-label"><h5>Please select a schematic file to upload.</h5></label>
|
||||
<form class="input-group justify-content-center" enctype="multipart/form-data" method="post"
|
||||
action="/api/schematics/uploading">
|
||||
<div class="input-group">
|
||||
<input type="file" class="form-control" id="formFile" name="file" aria-describedby="formFile" aria-label="Upload">
|
||||
<button class="btn btn-outline-primary" type="submit">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
Schematics
|
||||
SCHEMATICS
|
||||
<h2>Schematic Upload</h2>
|
||||
<h5 class="alert alert-danger mb-3 w-auto p-3" role="alert"><b>Error:</b> ${MESSAGE}</h5>
|
|
@ -0,0 +1,4 @@
|
|||
Schematics
|
||||
SCHEMATICS
|
||||
<h2>Schematic Upload</h2>
|
||||
<h5 class="alert alert-success mb-3 w-auto p-3" role="alert">${MESSAGE}</h5>
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/Telesphoreo/bootstrap-color-switcher@master/script.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@200;400&display=swap" rel="stylesheet">
|
||||
<title>${TITLE} - Plex HTTPD</title>
|
||||
</head>
|
||||
<body style="font-family: 'IBM Plex Sans', sans-serif;">
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/" style="font-weight:200;">Plex HTTPD</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_HOME}" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_INDEFBANS}" href="/api/indefbans/">Indefinite Bans</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_LIST}" href="/api/list/">List</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link ${ACTIVE_PUNISHMENTS}" href="/api/punishments/">Punishments</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link ${ACTIVE_COMMANDS}" href="/api/commands/">Commands</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle ${ACTIVE_SCHEMATICS}" id="navbarDropdownMenuLink" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Schematics
|
||||
</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||
<li><a class="dropdown-item" href="/api/schematics/download/">Download</a></li>
|
||||
<li><a class="dropdown-item" href="/api/schematics/upload/">Upload</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="text-align: center;" class="col-auto m-0 row justify-content-center p-4">
|
||||
${CONTENT}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +1,4 @@
|
|||
name: Plex-HTTPD
|
||||
version: 1.0.1
|
||||
name: Module-HTTPD
|
||||
version: 1.4-SNAPSHOT
|
||||
description: HTTPD server for Plex
|
||||
main: dev.plex.HTTPDModule
|
|
@ -1,16 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<div style="text-align: center;">
|
||||
<h2 style="font-family:Helvetica">Enter the UUID or username of the player you want to check</h2>
|
||||
<input id="test" type="text" autofocus>
|
||||
<button type="button" onclick="redirect()">Submit</button>
|
||||
|
||||
<script>
|
||||
function redirect() {
|
||||
var url = document.getElementById('test').value
|
||||
window.location = "punishments/" + url
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +0,0 @@
|
|||
server:
|
||||
bind-address: 0.0.0.0
|
||||
port: 27192
|
||||
logging: false
|
Loading…
Reference in New Issue