mirror of
https://github.com/plexusorg/Module-HTTPD.git
synced 2026-06-04 09:06:54 +00:00
Refactor
This commit is contained in:
@@ -3,7 +3,6 @@ package dev.plex;
|
|||||||
import dev.plex.assets.MinecraftAssetsManager;
|
import dev.plex.assets.MinecraftAssetsManager;
|
||||||
import dev.plex.authentication.AuthenticationManager;
|
import dev.plex.authentication.AuthenticationManager;
|
||||||
import dev.plex.cache.FileCache;
|
import dev.plex.cache.FileCache;
|
||||||
import dev.plex.api.PlexApi;
|
|
||||||
import dev.plex.config.ModuleConfig;
|
import dev.plex.config.ModuleConfig;
|
||||||
import dev.plex.logging.Log;
|
import dev.plex.logging.Log;
|
||||||
import dev.plex.module.PlexModule;
|
import dev.plex.module.PlexModule;
|
||||||
@@ -33,35 +32,41 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
public class HTTPDModule extends PlexModule
|
public class HTTPDModule extends PlexModule
|
||||||
{
|
{
|
||||||
public static ServletContextHandler context;
|
@Getter
|
||||||
|
private ServletContextHandler context;
|
||||||
private Thread serverThread;
|
private Thread serverThread;
|
||||||
private AtomicReference<Server> atomicServer = new AtomicReference<>();
|
private final AtomicReference<Server> atomicServer = new AtomicReference<>();
|
||||||
|
|
||||||
public static ModuleConfig moduleConfig;
|
|
||||||
private static PlexApi plexApi;
|
|
||||||
|
|
||||||
public static PlexApi plexApi()
|
|
||||||
{
|
|
||||||
return plexApi;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final FileCache fileCache = new FileCache();
|
|
||||||
|
|
||||||
public static final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html"));
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static AuthenticationManager authenticationManager;
|
private ModuleConfig moduleConfig;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static File accessLogFile;
|
private final FileCache fileCache = new FileCache();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static MinecraftAssetsManager minecraftAssetsManager;
|
private final String template = AbstractServlet.readFileReal(HTTPDModule.class.getResourceAsStream("/httpd/template.html"));
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private File accessLogFile;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private MinecraftAssetsManager minecraftAssetsManager;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private StatsBroadcaster statsBroadcaster;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private PlayersBroadcaster playersBroadcaster;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private PlayerInventoryBroadcaster playerInventoryBroadcaster;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load()
|
public void load()
|
||||||
{
|
{
|
||||||
plexApi = api();
|
|
||||||
// Move it from /httpd/config.yml to /plugins/Plex/modules/Plex-HTTPD/config.yml
|
// Move it from /httpd/config.yml to /plugins/Plex/modules/Plex-HTTPD/config.yml
|
||||||
moduleConfig = new ModuleConfig(this, "httpd/config.yml", "config.yml");
|
moduleConfig = new ModuleConfig(this, "httpd/config.yml", "config.yml");
|
||||||
}
|
}
|
||||||
@@ -70,17 +75,18 @@ public class HTTPDModule extends PlexModule
|
|||||||
public void enable()
|
public void enable()
|
||||||
{
|
{
|
||||||
moduleConfig.load();
|
moduleConfig.load();
|
||||||
HTTPDModule.plexApi().logging().debug("HTTPD Module Port: {0}", moduleConfig.getInt("server.port"));
|
api().logging().debug("HTTPD Module Port: {0}", moduleConfig.getInt("server.port"));
|
||||||
|
|
||||||
accessLogFile = new File(getDataFolder(), moduleConfig.getString("server.logging.file-path", "httpd.log"));
|
accessLogFile = new File(getDataFolder(), moduleConfig.getString("server.logging.file-path", "httpd.log"));
|
||||||
|
Log.configure(moduleConfig, accessLogFile);
|
||||||
|
|
||||||
minecraftAssetsManager = new MinecraftAssetsManager(getDataFolder().toPath());
|
minecraftAssetsManager = new MinecraftAssetsManager(getDataFolder().toPath(), api());
|
||||||
minecraftAssetsManager.refreshAsync();
|
minecraftAssetsManager.refreshAsync();
|
||||||
|
|
||||||
authenticationManager = new AuthenticationManager();
|
authenticationManager = new AuthenticationManager(this);
|
||||||
if (authenticationManager.provider() == null)
|
if (authenticationManager.provider() == null)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("Authentication is disabled or misconfigured");
|
api().logging().debug("Authentication is disabled or misconfigured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -110,33 +116,37 @@ public class HTTPDModule extends PlexModule
|
|||||||
connector.setIdleTimeout(moduleConfig.getLong("server.limits.idle-timeout-ms", 15_000L));
|
connector.setIdleTimeout(moduleConfig.getLong("server.limits.idle-timeout-ms", 15_000L));
|
||||||
connector.setAcceptQueueSize(moduleConfig.getInt("server.limits.accept-queue", 32));
|
connector.setAcceptQueueSize(moduleConfig.getInt("server.limits.accept-queue", 32));
|
||||||
|
|
||||||
context.addFilter(new FilterHolder(new RateLimitFilter()), "/*", EnumSet.of(DispatcherType.REQUEST));
|
context.addFilter(new FilterHolder(new RateLimitFilter(moduleConfig)), "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||||
|
|
||||||
StatsBroadcaster.get().start();
|
statsBroadcaster = new StatsBroadcaster(this);
|
||||||
PlayersBroadcaster.get().start();
|
playersBroadcaster = new PlayersBroadcaster(this);
|
||||||
PlayerInventoryBroadcaster.get().start();
|
playerInventoryBroadcaster = new PlayerInventoryBroadcaster(this);
|
||||||
|
statsBroadcaster.start();
|
||||||
|
playersBroadcaster.start();
|
||||||
|
playerInventoryBroadcaster.start();
|
||||||
|
|
||||||
new IndefBansEndpoint();
|
new IndefBansEndpoint(this);
|
||||||
new IndexEndpoint();
|
new IndexEndpoint(this);
|
||||||
new ListEndpoint();
|
new ListEndpoint(this);
|
||||||
new PunishmentsEndpoint();
|
new PunishmentsEndpoint(this);
|
||||||
new CommandsEndpoint();
|
new CommandsEndpoint(this);
|
||||||
new SchematicDownloadEndpoint();
|
new SchematicDownloadEndpoint(this);
|
||||||
new SchematicUploadEndpoint();
|
new SchematicUploadEndpoint(this);
|
||||||
new PlayersEndpoint();
|
new PlayersEndpoint(this);
|
||||||
new PlayerAdminEndpoint();
|
new PlayerAdminEndpoint(this);
|
||||||
new AssetsEndpoint();
|
new AssetsEndpoint(this);
|
||||||
new PunishmentsUIEndpoint();
|
new PunishmentsUIEndpoint(this);
|
||||||
new IndefBansUIEndpoint();
|
new IndefBansUIEndpoint(this);
|
||||||
new AuthenticationEndpoint();
|
new AuthenticationEndpoint(this);
|
||||||
|
|
||||||
HTTPDModule.context.addServlet(StatsStreamServlet.class, "/api/stats/stream");
|
context.addServlet(new ServletHolder(new StatsStreamServlet(statsBroadcaster)), "/api/stats/stream");
|
||||||
HTTPDModule.context.addServlet(PlayersStreamServlet.class, "/api/players/stream");
|
context.addServlet(new ServletHolder(new PlayersStreamServlet(playersBroadcaster)), "/api/players/stream");
|
||||||
HTTPDModule.context.addServlet(StaffPlayersStreamServlet.class, "/api/players/stream/staff");
|
context.addServlet(new ServletHolder(new StaffPlayersStreamServlet(this, playersBroadcaster)), "/api/players/stream/staff");
|
||||||
HTTPDModule.context.addServlet(PlayerActionServlet.class, "/api/admin/action");
|
context.addServlet(new ServletHolder(new PlayerActionServlet(this)), "/api/admin/action");
|
||||||
HTTPDModule.context.addServlet(PlayerInventoryStreamServlet.class, "/api/player/inventory/stream");
|
context.addServlet(new ServletHolder(new PlayerInventoryStreamServlet(this, playerInventoryBroadcaster)), "/api/player/inventory/stream");
|
||||||
|
|
||||||
ServletHolder uploadHolder = HTTPDModule.context.addServlet(SchematicUploadServlet.class, "/api/schematics/uploading");
|
ServletHolder uploadHolder = new ServletHolder(new SchematicUploadServlet(this));
|
||||||
|
context.addServlet(uploadHolder, "/api/schematics/uploading");
|
||||||
|
|
||||||
File uploadLoc = new File(System.getProperty("java.io.tmpdir"), "schematic-temp-dir");
|
File uploadLoc = new File(System.getProperty("java.io.tmpdir"), "schematic-temp-dir");
|
||||||
if (!uploadLoc.exists())
|
if (!uploadLoc.exists())
|
||||||
@@ -160,16 +170,19 @@ public class HTTPDModule extends PlexModule
|
|||||||
}
|
}
|
||||||
}, "Jetty-Server");
|
}, "Jetty-Server");
|
||||||
serverThread.start();
|
serverThread.start();
|
||||||
HTTPDModule.plexApi().logging().info("Starting Jetty server on port " + moduleConfig.getInt("server.port"));
|
api().logging().info("Starting Jetty server on port " + moduleConfig.getInt("server.port"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable()
|
public void disable()
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("Stopping Jetty server");
|
api().logging().debug("Stopping Jetty server");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StatsBroadcaster.get().shutdown();
|
if (statsBroadcaster != null)
|
||||||
|
{
|
||||||
|
statsBroadcaster.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
@@ -177,7 +190,10 @@ public class HTTPDModule extends PlexModule
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PlayersBroadcaster.get().shutdown();
|
if (playersBroadcaster != null)
|
||||||
|
{
|
||||||
|
playersBroadcaster.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
@@ -185,7 +201,10 @@ public class HTTPDModule extends PlexModule
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PlayerInventoryBroadcaster.get().shutdown();
|
if (playerInventoryBroadcaster != null)
|
||||||
|
{
|
||||||
|
playerInventoryBroadcaster.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
@@ -193,8 +212,12 @@ public class HTTPDModule extends PlexModule
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
atomicServer.get().stop();
|
Server server = atomicServer.get();
|
||||||
atomicServer.get().destroy();
|
if (server != null)
|
||||||
|
{
|
||||||
|
server.stop();
|
||||||
|
server.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.plex.assets;
|
package dev.plex.assets;
|
||||||
|
|
||||||
import dev.plex.HTTPDModule;
|
import dev.plex.api.PlexApi;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -35,12 +35,14 @@ public class MinecraftAssetsManager
|
|||||||
private final AtomicBoolean ready = new AtomicBoolean(false);
|
private final AtomicBoolean ready = new AtomicBoolean(false);
|
||||||
private final AtomicBoolean refreshStarted = new AtomicBoolean(false);
|
private final AtomicBoolean refreshStarted = new AtomicBoolean(false);
|
||||||
private final String minecraftVersion;
|
private final String minecraftVersion;
|
||||||
|
private final PlexApi api;
|
||||||
|
|
||||||
public MinecraftAssetsManager(Path dataFolder)
|
public MinecraftAssetsManager(Path dataFolder, PlexApi api)
|
||||||
{
|
{
|
||||||
this.root = dataFolder.resolve("minecraft-assets");
|
this.root = dataFolder.resolve("minecraft-assets");
|
||||||
this.versionFile = root.resolve("version.txt");
|
this.versionFile = root.resolve("version.txt");
|
||||||
this.minecraftVersion = detectMinecraftVersion();
|
this.minecraftVersion = detectMinecraftVersion();
|
||||||
|
this.api = api;
|
||||||
this.client = HttpClient.newBuilder()
|
this.client = HttpClient.newBuilder()
|
||||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||||
.connectTimeout(Duration.ofSeconds(20))
|
.connectTimeout(Duration.ofSeconds(20))
|
||||||
@@ -63,7 +65,7 @@ public class MinecraftAssetsManager
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().info("Unable to download Minecraft assets for HTTPD inventory view: " + e.getMessage());
|
api.logging().info("Unable to download Minecraft assets for HTTPD inventory view: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -90,13 +92,13 @@ public class MinecraftAssetsManager
|
|||||||
String cachedVersion = Files.exists(versionFile) ? Files.readString(versionFile).trim() : "";
|
String cachedVersion = Files.exists(versionFile) ? Files.readString(versionFile).trim() : "";
|
||||||
if (minecraftVersion.equals(cachedVersion) && hasAssets())
|
if (minecraftVersion.equals(cachedVersion) && hasAssets())
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("HTTPD Minecraft assets are already cached for {0}", minecraftVersion);
|
api.logging().debug("HTTPD Minecraft assets are already cached for {0}", minecraftVersion);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cachedVersion.isEmpty() && !minecraftVersion.equals(cachedVersion))
|
if (!cachedVersion.isEmpty() && !minecraftVersion.equals(cachedVersion))
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().info("Minecraft version changed from " + cachedVersion + " to " + minecraftVersion + "; recreating HTTPD asset cache");
|
api.logging().info("Minecraft version changed from " + cachedVersion + " to " + minecraftVersion + "; recreating HTTPD asset cache");
|
||||||
}
|
}
|
||||||
recreateCache();
|
recreateCache();
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,7 @@ public class MinecraftAssetsManager
|
|||||||
deleteDirectory(root);
|
deleteDirectory(root);
|
||||||
Files.createDirectories(root);
|
Files.createDirectories(root);
|
||||||
|
|
||||||
HTTPDModule.plexApi().logging().info("Downloading Minecraft " + minecraftVersion + " client assets for HTTPD inventory view");
|
api.logging().info("Downloading Minecraft " + minecraftVersion + " client assets for HTTPD inventory view");
|
||||||
JSONObject version = findVersionJson();
|
JSONObject version = findVersionJson();
|
||||||
String clientUrl = version.getJSONObject("downloads").getJSONObject("client").getString("url");
|
String clientUrl = version.getJSONObject("downloads").getJSONObject("client").getString("url");
|
||||||
|
|
||||||
@@ -142,7 +144,7 @@ public class MinecraftAssetsManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
Files.writeString(versionFile, minecraftVersion + System.lineSeparator());
|
Files.writeString(versionFile, minecraftVersion + System.lineSeparator());
|
||||||
HTTPDModule.plexApi().logging().info("HTTPD Minecraft assets cached for " + minecraftVersion);
|
api.logging().info("HTTPD Minecraft assets cached for " + minecraftVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject findVersionJson() throws IOException, InterruptedException
|
private JSONObject findVersionJson() throws IOException, InterruptedException
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ public class AuthenticationManager
|
|||||||
{
|
{
|
||||||
private final OAuth2Provider provider;
|
private final OAuth2Provider provider;
|
||||||
|
|
||||||
public AuthenticationManager()
|
public AuthenticationManager(HTTPDModule module)
|
||||||
{
|
{
|
||||||
final boolean enabled = HTTPDModule.moduleConfig.getBoolean("authentication.enabled", false);
|
final boolean enabled = module.getModuleConfig().getBoolean("authentication.enabled", false);
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
{
|
{
|
||||||
provider = null;
|
provider = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPDModule.plexApi().logging().info("[HTTPD] XenForo OAuth2 authentication is enabled");
|
module.api().logging().info("[HTTPD] XenForo OAuth2 authentication is enabled");
|
||||||
provider = new XenForoOAuth2Provider();
|
provider = new XenForoOAuth2Provider(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OAuth2Provider provider()
|
public OAuth2Provider provider()
|
||||||
|
|||||||
@@ -49,19 +49,21 @@ public class XenForoOAuth2Provider implements OAuth2Provider
|
|||||||
private final String clientSecret;
|
private final String clientSecret;
|
||||||
private final String redirectUri;
|
private final String redirectUri;
|
||||||
private final Duration sessionTtl;
|
private final Duration sessionTtl;
|
||||||
|
private final HTTPDModule module;
|
||||||
|
|
||||||
public XenForoOAuth2Provider()
|
public XenForoOAuth2Provider(HTTPDModule module)
|
||||||
{
|
{
|
||||||
String domain = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.domain", "");
|
this.module = module;
|
||||||
this.clientId = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.clientId", "");
|
String domain = module.getModuleConfig().getString("authentication.provider.xenforo.domain", "");
|
||||||
this.clientSecret = HTTPDModule.moduleConfig.getString("authentication.provider.xenforo.clientSecret", "");
|
this.clientId = module.getModuleConfig().getString("authentication.provider.xenforo.clientId", "");
|
||||||
this.redirectUri = HTTPDModule.moduleConfig.getString("authentication.provider.redirectUri", "");
|
this.clientSecret = module.getModuleConfig().getString("authentication.provider.xenforo.clientSecret", "");
|
||||||
long ttlMinutes = HTTPDModule.moduleConfig.getLong("authentication.provider.xenforo.sessionMinutes", 1440L);
|
this.redirectUri = module.getModuleConfig().getString("authentication.provider.redirectUri", "");
|
||||||
|
long ttlMinutes = module.getModuleConfig().getLong("authentication.provider.xenforo.sessionMinutes", 1440L);
|
||||||
this.sessionTtl = Duration.ofMinutes(Math.max(ttlMinutes, 1L));
|
this.sessionTtl = Duration.ofMinutes(Math.max(ttlMinutes, 1L));
|
||||||
|
|
||||||
if (domain.isEmpty() || clientId.isEmpty() || clientSecret.isEmpty() || redirectUri.isEmpty())
|
if (domain.isEmpty() || clientId.isEmpty() || clientSecret.isEmpty() || redirectUri.isEmpty())
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().error("XenForo OAuth2 misconfigured: domain, clientId, clientSecret, redirectUri are all required.");
|
module.api().logging().error("XenForo OAuth2 misconfigured: domain, clientId, clientSecret, redirectUri are all required.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String base = "https://" + domain.replaceFirst("^https?://", "").replaceAll("/+$", "");
|
String base = "https://" + domain.replaceFirst("^https?://", "").replaceAll("/+$", "");
|
||||||
@@ -285,7 +287,7 @@ public class XenForoOAuth2Provider implements OAuth2Provider
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("Failed to revoke XenForo token: {0}", e.getMessage());
|
module.api().logging().debug("Failed to revoke XenForo token: {0}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.plex.logging;
|
package dev.plex.logging;
|
||||||
|
|
||||||
import dev.plex.HTTPDModule;
|
import dev.plex.config.ModuleConfig;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -11,19 +11,30 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
|
||||||
public class Log
|
public class Log
|
||||||
{
|
{
|
||||||
private static final DateTimeFormatter STAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS z");
|
private static final DateTimeFormatter STAMP = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS z");
|
||||||
|
|
||||||
|
private static BooleanSupplier consoleLoggingEnabled = () -> false;
|
||||||
|
private static BooleanSupplier fileLoggingEnabled = () -> false;
|
||||||
|
private static File accessLogFile;
|
||||||
private static BufferedWriter writer;
|
private static BufferedWriter writer;
|
||||||
private static File writerTarget;
|
private static File writerTarget;
|
||||||
|
|
||||||
|
public static synchronized void configure(ModuleConfig moduleConfig, File target)
|
||||||
|
{
|
||||||
|
consoleLoggingEnabled = () -> moduleConfig.getBoolean("server.logging.console", false);
|
||||||
|
fileLoggingEnabled = () -> moduleConfig.getBoolean("server.logging.file", true);
|
||||||
|
accessLogFile = target;
|
||||||
|
}
|
||||||
|
|
||||||
public static void log(String message, Object... strings)
|
public static void log(String message, Object... strings)
|
||||||
{
|
{
|
||||||
String formatted = format(message, strings);
|
String formatted = format(message, strings);
|
||||||
writeFile(formatted);
|
writeFile(formatted);
|
||||||
if (HTTPDModule.moduleConfig != null && HTTPDModule.moduleConfig.getBoolean("server.logging.console", false))
|
if (consoleLoggingEnabled.getAsBoolean())
|
||||||
{
|
{
|
||||||
Bukkit.getConsoleSender().sendMessage(Component.text("[Plex HTTPD] ").color(NamedTextColor.DARK_AQUA).append(Component.text(formatted).color(NamedTextColor.GRAY)));
|
Bukkit.getConsoleSender().sendMessage(Component.text("[Plex HTTPD] ").color(NamedTextColor.DARK_AQUA).append(Component.text(formatted).color(NamedTextColor.GRAY)));
|
||||||
}
|
}
|
||||||
@@ -42,6 +53,9 @@ public class Log
|
|||||||
writer = null;
|
writer = null;
|
||||||
writerTarget = null;
|
writerTarget = null;
|
||||||
}
|
}
|
||||||
|
consoleLoggingEnabled = () -> false;
|
||||||
|
fileLoggingEnabled = () -> false;
|
||||||
|
accessLogFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String format(String message, Object... strings)
|
private static String format(String message, Object... strings)
|
||||||
@@ -59,9 +73,8 @@ public class Log
|
|||||||
|
|
||||||
private static synchronized void writeFile(String formatted)
|
private static synchronized void writeFile(String formatted)
|
||||||
{
|
{
|
||||||
if (HTTPDModule.moduleConfig == null) return;
|
if (!fileLoggingEnabled.getAsBoolean()) return;
|
||||||
if (!HTTPDModule.moduleConfig.getBoolean("server.logging.file", true)) return;
|
File target = accessLogFile;
|
||||||
File target = HTTPDModule.getAccessLogFile();
|
|
||||||
if (target == null) return;
|
if (target == null) return;
|
||||||
if (writer == null || !target.equals(writerTarget))
|
if (writer == null || !target.equals(writerTarget))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package dev.plex.ratelimit;
|
package dev.plex.ratelimit;
|
||||||
|
|
||||||
import dev.plex.HTTPDModule;
|
import dev.plex.config.ModuleConfig;
|
||||||
import dev.plex.logging.Log;
|
import dev.plex.logging.Log;
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
@@ -28,14 +28,14 @@ public class RateLimitFilter implements Filter
|
|||||||
private final ConcurrentHashMap<String, TokenBucket> ipBuckets = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, TokenBucket> ipBuckets = new ConcurrentHashMap<>();
|
||||||
private final AtomicLong nextEvictMillis = new AtomicLong(System.currentTimeMillis() + EVICT_INTERVAL_MILLIS);
|
private final AtomicLong nextEvictMillis = new AtomicLong(System.currentTimeMillis() + EVICT_INTERVAL_MILLIS);
|
||||||
|
|
||||||
public RateLimitFilter()
|
public RateLimitFilter(ModuleConfig config)
|
||||||
{
|
{
|
||||||
this.enabled = HTTPDModule.moduleConfig.getBoolean("rate-limit.enabled", true);
|
this.enabled = config.getBoolean("rate-limit.enabled", true);
|
||||||
double globalCapacity = HTTPDModule.moduleConfig.getDouble("rate-limit.global.capacity", 200.0);
|
double globalCapacity = config.getDouble("rate-limit.global.capacity", 200.0);
|
||||||
double globalRate = HTTPDModule.moduleConfig.getDouble("rate-limit.global.per-second", 100.0);
|
double globalRate = config.getDouble("rate-limit.global.per-second", 100.0);
|
||||||
this.globalBucket = new TokenBucket(globalCapacity, globalRate);
|
this.globalBucket = new TokenBucket(globalCapacity, globalRate);
|
||||||
this.ipCapacity = HTTPDModule.moduleConfig.getDouble("rate-limit.per-ip.capacity", 30.0);
|
this.ipCapacity = config.getDouble("rate-limit.per-ip.capacity", 30.0);
|
||||||
this.ipRefillPerSecond = HTTPDModule.moduleConfig.getDouble("rate-limit.per-ip.per-second", 10.0);
|
this.ipRefillPerSecond = config.getDouble("rate-limit.per-ip.per-second", 10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import org.eclipse.jetty.ee10.servlet.ServletHolder;
|
|||||||
public class AbstractServlet extends HttpServlet
|
public class AbstractServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
private final List<Mapping> GET_MAPPINGS = Lists.newArrayList();
|
private final List<Mapping> GET_MAPPINGS = Lists.newArrayList();
|
||||||
|
protected final HTTPDModule module;
|
||||||
|
|
||||||
public AbstractServlet()
|
public AbstractServlet(HTTPDModule module)
|
||||||
{
|
{
|
||||||
|
this.module = module;
|
||||||
for (Method declaredMethod : this.getClass().getDeclaredMethods())
|
for (Method declaredMethod : this.getClass().getDeclaredMethods())
|
||||||
{
|
{
|
||||||
declaredMethod.setAccessible(true);
|
declaredMethod.setAccessible(true);
|
||||||
@@ -46,7 +48,7 @@ public class AbstractServlet extends HttpServlet
|
|||||||
ServletHolder holder = new ServletHolder(this);
|
ServletHolder holder = new ServletHolder(this);
|
||||||
String endpoint = getMapping.endpoint();
|
String endpoint = getMapping.endpoint();
|
||||||
String pattern = endpoint.endsWith("/") ? endpoint + "*" : endpoint;
|
String pattern = endpoint.endsWith("/") ? endpoint + "*" : endpoint;
|
||||||
HTTPDModule.context.addServlet(holder, pattern);
|
module.getContext().addServlet(holder, pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,11 +65,6 @@ public class AbstractServlet extends HttpServlet
|
|||||||
String requestPath = getRequestPath(req);
|
String requestPath = getRequestPath(req);
|
||||||
Log.log(ipAddress + " visited endpoint " + requestPath);
|
Log.log(ipAddress + " visited endpoint " + requestPath);
|
||||||
|
|
||||||
/*Enumeration<String> headerz = req.getHeaderNames();
|
|
||||||
while (headerz.hasMoreElements()) {
|
|
||||||
String header = headerz.nextElement();
|
|
||||||
HTTPDModule.plexApi().logging().debug("Header: {0} Value {1}", header, req.getHeader(header));
|
|
||||||
}*/
|
|
||||||
GET_MAPPINGS.stream().filter(mapping -> endpointMatchesRequest(mapping.getMapping().endpoint(), requestPath)).forEach(mapping ->
|
GET_MAPPINGS.stream().filter(mapping -> endpointMatchesRequest(mapping.getMapping().endpoint(), requestPath)).forEach(mapping ->
|
||||||
{
|
{
|
||||||
resp.setCharacterEncoding("UTF-8");
|
resp.setCharacterEncoding("UTF-8");
|
||||||
@@ -137,27 +134,42 @@ public class AbstractServlet extends HttpServlet
|
|||||||
return requestPath.isEmpty() ? "/" : requestPath;
|
return requestPath.isEmpty() ? "/" : requestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticatedUser currentUser(HttpServletRequest request)
|
protected AuthenticatedUser currentUser(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
AuthenticationManager manager = HTTPDModule.getAuthenticationManager();
|
return currentUser(module, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthenticatedUser currentUser(HTTPDModule module, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
AuthenticationManager manager = module.getAuthenticationManager();
|
||||||
if (manager == null) return null;
|
if (manager == null) return null;
|
||||||
OAuth2Provider provider = manager.provider();
|
OAuth2Provider provider = manager.provider();
|
||||||
if (provider == null) return null;
|
if (provider == null) return null;
|
||||||
return provider.lookup(request);
|
return provider.lookup(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticatedUser currentStaff(HttpServletRequest request)
|
protected AuthenticatedUser currentStaff(HttpServletRequest request)
|
||||||
{
|
{
|
||||||
AuthenticatedUser user = currentUser(request);
|
return currentStaff(module, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthenticatedUser currentStaff(HTTPDModule module, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
AuthenticatedUser user = currentUser(module, request);
|
||||||
return (user != null && user.staff()) ? user : null;
|
return (user != null && user.staff()) ? user : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String signInPrompt(String action)
|
protected String signInPrompt(String action)
|
||||||
{
|
{
|
||||||
return signInPrompt(null, action);
|
return signInPrompt(null, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String signInPrompt(HttpServletRequest request, String action)
|
protected String signInPrompt(HttpServletRequest request, String action)
|
||||||
|
{
|
||||||
|
return signInPrompt(module, request, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String signInPrompt(HTTPDModule module, HttpServletRequest request, String action)
|
||||||
{
|
{
|
||||||
String href = "/oauth2/login";
|
String href = "/oauth2/login";
|
||||||
if (request != null)
|
if (request != null)
|
||||||
@@ -170,9 +182,14 @@ public class AbstractServlet extends HttpServlet
|
|||||||
return "You must <a class=\"text-primary underline\" href=\"" + href + "\">sign in</a> as staff " + action + ".";
|
return "You must <a class=\"text-primary underline\" href=\"" + href + "\">sign in</a> as staff " + action + ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readFile(InputStream filename)
|
protected String readFile(InputStream filename)
|
||||||
{
|
{
|
||||||
String base = HTTPDModule.template;
|
return readFile(module, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readFile(HTTPDModule module, InputStream filename)
|
||||||
|
{
|
||||||
|
String base = module.getTemplate();
|
||||||
String page = readFileReal(filename);
|
String page = readFileReal(filename);
|
||||||
String[] info = page.split("\\r?\\n", 3);
|
String[] info = page.split("\\r?\\n", 3);
|
||||||
String title = info.length > 0 ? info[0] : "";
|
String title = info.length > 0 ? info[0] : "";
|
||||||
|
|||||||
@@ -27,12 +27,18 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
private static final List<String> PERMANENT_ACTIONS = List.of("ban", "mute");
|
private static final List<String> PERMANENT_ACTIONS = List.of("ban", "mute");
|
||||||
private static final List<String> TEMP_ACTIONS = List.of("tempban", "tempmute", "freeze");
|
private static final List<String> TEMP_ACTIONS = List.of("tempban", "tempmute", "freeze");
|
||||||
private static final List<String> INVENTORY_ACTIONS = List.of("clear-inventory", "clear-selected");
|
private static final List<String> INVENTORY_ACTIONS = List.of("clear-inventory", "clear-selected");
|
||||||
|
private final HTTPDModule module;
|
||||||
|
|
||||||
|
public PlayerActionServlet(HTTPDModule module)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
{
|
{
|
||||||
AuthenticatedUser staff = AbstractServlet.currentStaff(request);
|
AuthenticatedUser staff = AbstractServlet.currentStaff(module, request);
|
||||||
if (staff == null)
|
if (staff == null)
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
@@ -71,7 +77,7 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlexPlayerView target = HTTPDModule.plexApi().players().byUuid(uuid).orElse(null);
|
PlexPlayerView target = module.api().players().byUuid(uuid).orElse(null);
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
@@ -119,11 +125,11 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
|
|
||||||
final boolean kick = action.equals("ban") || action.equals("tempban");
|
final boolean kick = action.equals("ban") || action.equals("tempban");
|
||||||
final PunishmentRequest toApply = punishment;
|
final PunishmentRequest toApply = punishment;
|
||||||
HTTPDModule.plexApi().scheduler().runGlobal(() ->
|
module.api().scheduler().runGlobal(() ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().punishments().punish(target, toApply);
|
module.api().punishments().punish(target, toApply);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
@@ -135,7 +141,7 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
Player online = Bukkit.getPlayer(uuid);
|
Player online = Bukkit.getPlayer(uuid);
|
||||||
if (online != null)
|
if (online != null)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().scheduler().runEntity(online, () ->
|
module.api().scheduler().runEntity(online, () ->
|
||||||
{
|
{
|
||||||
try { online.kick(Component.text("You have been banned: " + toApply.reason())); }
|
try { online.kick(Component.text("You have been banned: " + toApply.reason())); }
|
||||||
catch (Throwable t) { t.printStackTrace(); }
|
catch (Throwable t) { t.printStackTrace(); }
|
||||||
@@ -147,7 +153,7 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
response.sendRedirect("/player/" + uuid);
|
response.sendRedirect("/player/" + uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleInventoryAction(HttpServletRequest request, HttpServletResponse response, AuthenticatedUser staff, UUID uuid, PlexPlayerView target, String action, String slot)
|
private void handleInventoryAction(HttpServletRequest request, HttpServletResponse response, AuthenticatedUser staff, UUID uuid, PlexPlayerView target, String action, String slot)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
String ipAddress = request.getRemoteAddr();
|
String ipAddress = request.getRemoteAddr();
|
||||||
@@ -159,11 +165,11 @@ public class PlayerActionServlet extends HttpServlet
|
|||||||
|
|
||||||
Log.log(ipAddress + " (xf:" + staff.username() + ") issued " + action + " on " + target.name() + " (" + uuid + ")" + (slot == null || slot.isBlank() ? "" : " slot " + slot));
|
Log.log(ipAddress + " (xf:" + staff.username() + ") issued " + action + " on " + target.name() + " (" + uuid + ")" + (slot == null || slot.isBlank() ? "" : " slot " + slot));
|
||||||
|
|
||||||
HTTPDModule.plexApi().scheduler().runGlobal(() ->
|
module.api().scheduler().runGlobal(() ->
|
||||||
{
|
{
|
||||||
Player online = Bukkit.getPlayer(uuid);
|
Player online = Bukkit.getPlayer(uuid);
|
||||||
if (online == null) return;
|
if (online == null) return;
|
||||||
HTTPDModule.plexApi().scheduler().runEntity(online, () ->
|
module.api().scheduler().runEntity(online, () ->
|
||||||
{
|
{
|
||||||
PlayerInventory inv = online.getInventory();
|
PlayerInventory inv = online.getInventory();
|
||||||
if ("clear-inventory".equals(action))
|
if ("clear-inventory".equals(action))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.plex.request;
|
package dev.plex.request;
|
||||||
|
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.logging.Log;
|
import dev.plex.logging.Log;
|
||||||
import dev.plex.request.impl.PlayerInventoryBroadcaster;
|
import dev.plex.request.impl.PlayerInventoryBroadcaster;
|
||||||
import jakarta.servlet.AsyncContext;
|
import jakarta.servlet.AsyncContext;
|
||||||
@@ -16,11 +17,20 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class PlayerInventoryStreamServlet extends HttpServlet
|
public class PlayerInventoryStreamServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
|
private final HTTPDModule module;
|
||||||
|
private final PlayerInventoryBroadcaster broadcaster;
|
||||||
|
|
||||||
|
public PlayerInventoryStreamServlet(HTTPDModule module, PlayerInventoryBroadcaster broadcaster)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
this.broadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
{
|
{
|
||||||
if (AbstractServlet.currentStaff(request) == null)
|
if (AbstractServlet.currentStaff(module, request) == null)
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
return;
|
return;
|
||||||
@@ -51,7 +61,6 @@ public class PlayerInventoryStreamServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
Log.log(ipAddress + " opened inventory stream for " + uuid);
|
Log.log(ipAddress + " opened inventory stream for " + uuid);
|
||||||
|
|
||||||
PlayerInventoryBroadcaster broadcaster = PlayerInventoryBroadcaster.get();
|
|
||||||
if (broadcaster.atCapacity())
|
if (broadcaster.atCapacity())
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ import java.io.PrintWriter;
|
|||||||
|
|
||||||
public class PlayersStreamServlet extends HttpServlet
|
public class PlayersStreamServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
|
private final PlayersBroadcaster broadcaster;
|
||||||
|
|
||||||
|
public PlayersStreamServlet(PlayersBroadcaster broadcaster)
|
||||||
|
{
|
||||||
|
this.broadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
@@ -27,7 +34,6 @@ public class PlayersStreamServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
Log.log(ipAddress + " opened SSE stream /api/players/stream");
|
Log.log(ipAddress + " opened SSE stream /api/players/stream");
|
||||||
|
|
||||||
PlayersBroadcaster broadcaster = PlayersBroadcaster.get();
|
|
||||||
if (broadcaster.atCapacity())
|
if (broadcaster.atCapacity())
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||||
|
|||||||
@@ -22,14 +22,20 @@ import java.util.regex.Pattern;
|
|||||||
public class SchematicUploadServlet extends HttpServlet
|
public class SchematicUploadServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
private static final Pattern schemNameMatcher = Pattern.compile("^[a-z0-9'!,_ -]{1,30}\\.schem(atic)?$", Pattern.CASE_INSENSITIVE);
|
private static final Pattern schemNameMatcher = Pattern.compile("^[a-z0-9'!,_ -]{1,30}\\.schem(atic)?$", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final HTTPDModule module;
|
||||||
|
|
||||||
|
public SchematicUploadServlet(HTTPDModule module)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||||
{
|
{
|
||||||
AuthenticatedUser user = AbstractServlet.currentStaff(request);
|
AuthenticatedUser user = AbstractServlet.currentStaff(module, request);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt(request, "to upload schematics")));
|
response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt(module, request, "to upload schematics")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File worldeditFolder = HTTPDModule.getWorldeditFolder();
|
File worldeditFolder = HTTPDModule.getWorldeditFolder();
|
||||||
@@ -67,7 +73,7 @@ public class SchematicUploadServlet extends HttpServlet
|
|||||||
ClipboardFormat schematicFormat = ClipboardFormats.findByFile(schematicFile);
|
ClipboardFormat schematicFormat = ClipboardFormats.findByFile(schematicFile);
|
||||||
if (schematicFormat == null)
|
if (schematicFormat == null)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename);
|
module.api().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename);
|
||||||
Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename);
|
Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename);
|
||||||
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
||||||
FileUtils.deleteQuietly(schematicFile);
|
FileUtils.deleteQuietly(schematicFile);
|
||||||
@@ -79,7 +85,7 @@ public class SchematicUploadServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename);
|
module.api().logging().info(user.username() + " FAILED to upload schematic with filename: " + filename);
|
||||||
Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename);
|
Log.log("{0} (xf:{1}) FAILED to upload schematic {2}", user.username(), user.userId(), filename);
|
||||||
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
response.getWriter().println(schematicUploadBadHTML("Schematic is not a valid format."));
|
||||||
FileUtils.deleteQuietly(schematicFile);
|
FileUtils.deleteQuietly(schematicFile);
|
||||||
@@ -87,20 +93,20 @@ public class SchematicUploadServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
response.getWriter().println(schematicUploadGoodHTML("Successfully uploaded <b>" + filename + "</b>."));
|
response.getWriter().println(schematicUploadGoodHTML("Successfully uploaded <b>" + filename + "</b>."));
|
||||||
HTTPDModule.plexApi().logging().info(user.username() + " uploaded schematic with filename: " + filename);
|
module.api().logging().info(user.username() + " uploaded schematic with filename: " + filename);
|
||||||
Log.log("{0} (xf:{1}) uploaded schematic {2}", user.username(), user.userId(), filename);
|
Log.log("{0} (xf:{1}) uploaded schematic {2}", user.username(), user.userId(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String schematicUploadBadHTML(String message)
|
private String schematicUploadBadHTML(String message)
|
||||||
{
|
{
|
||||||
String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html"));
|
String file = AbstractServlet.readFile(module, this.getClass().getResourceAsStream("/httpd/schematic_upload_bad.html"));
|
||||||
file = file.replace("${MESSAGE}", message);
|
file = file.replace("${MESSAGE}", message);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String schematicUploadGoodHTML(String message)
|
private String schematicUploadGoodHTML(String message)
|
||||||
{
|
{
|
||||||
String file = AbstractServlet.readFile(this.getClass().getResourceAsStream("/httpd/schematic_upload_good.html"));
|
String file = AbstractServlet.readFile(module, this.getClass().getResourceAsStream("/httpd/schematic_upload_good.html"));
|
||||||
file = file.replace("${MESSAGE}", message);
|
file = file.replace("${MESSAGE}", message);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.plex.request;
|
package dev.plex.request;
|
||||||
|
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.logging.Log;
|
import dev.plex.logging.Log;
|
||||||
import dev.plex.request.impl.PlayersBroadcaster;
|
import dev.plex.request.impl.PlayersBroadcaster;
|
||||||
import jakarta.servlet.AsyncContext;
|
import jakarta.servlet.AsyncContext;
|
||||||
@@ -15,11 +16,20 @@ import java.io.PrintWriter;
|
|||||||
|
|
||||||
public class StaffPlayersStreamServlet extends HttpServlet
|
public class StaffPlayersStreamServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
|
private final HTTPDModule module;
|
||||||
|
private final PlayersBroadcaster broadcaster;
|
||||||
|
|
||||||
|
public StaffPlayersStreamServlet(HTTPDModule module, PlayersBroadcaster broadcaster)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
this.broadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
{
|
{
|
||||||
if (AbstractServlet.currentStaff(request) == null)
|
if (AbstractServlet.currentStaff(module, request) == null)
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
return;
|
return;
|
||||||
@@ -33,7 +43,6 @@ public class StaffPlayersStreamServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
Log.log(ipAddress + " opened SSE stream /api/players/stream/staff");
|
Log.log(ipAddress + " opened SSE stream /api/players/stream/staff");
|
||||||
|
|
||||||
PlayersBroadcaster broadcaster = PlayersBroadcaster.get();
|
|
||||||
if (broadcaster.atCapacity())
|
if (broadcaster.atCapacity())
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ import java.io.PrintWriter;
|
|||||||
|
|
||||||
public class StatsStreamServlet extends HttpServlet
|
public class StatsStreamServlet extends HttpServlet
|
||||||
{
|
{
|
||||||
|
private final StatsBroadcaster broadcaster;
|
||||||
|
|
||||||
|
public StatsStreamServlet(StatsBroadcaster broadcaster)
|
||||||
|
{
|
||||||
|
this.broadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException
|
throws ServletException, IOException
|
||||||
@@ -27,7 +34,6 @@ public class StatsStreamServlet extends HttpServlet
|
|||||||
}
|
}
|
||||||
Log.log(ipAddress + " opened SSE stream /api/stats/stream");
|
Log.log(ipAddress + " opened SSE stream /api/stats/stream");
|
||||||
|
|
||||||
StatsBroadcaster broadcaster = StatsBroadcaster.get();
|
|
||||||
if (broadcaster.atCapacity())
|
if (broadcaster.atCapacity())
|
||||||
{
|
{
|
||||||
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ public class AssetsEndpoint extends AbstractServlet
|
|||||||
private static final Pattern MODEL_PATH = Pattern.compile("(item|block)/[a-z0-9_]+\\.json");
|
private static final Pattern MODEL_PATH = Pattern.compile("(item|block)/[a-z0-9_]+\\.json");
|
||||||
private static final Pattern ITEM_DEF_PATH = Pattern.compile("[a-z0-9_]+\\.json");
|
private static final Pattern ITEM_DEF_PATH = Pattern.compile("[a-z0-9_]+\\.json");
|
||||||
|
|
||||||
|
public AssetsEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/assets/dashboard.js")
|
@GetMapping(endpoint = "/assets/dashboard.js")
|
||||||
@MappingHeaders(headers = {"content-type;application/javascript; charset=utf-8", "cache-control;public, max-age=300"})
|
@MappingHeaders(headers = {"content-type;application/javascript; charset=utf-8", "cache-control;public, max-age=300"})
|
||||||
@@ -97,7 +101,7 @@ public class AssetsEndpoint extends AbstractServlet
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void servePathUnder(HttpServletRequest request, HttpServletResponse response, String urlPrefix, Pattern allowed, String cacheCategory, String resourcePrefix)
|
private void servePathUnder(HttpServletRequest request, HttpServletResponse response, String urlPrefix, Pattern allowed, String cacheCategory, String resourcePrefix)
|
||||||
{
|
{
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
if (!uri.startsWith(urlPrefix))
|
if (!uri.startsWith(urlPrefix))
|
||||||
@@ -123,13 +127,13 @@ public class AssetsEndpoint extends AbstractServlet
|
|||||||
serveResource(resourcePrefix + rest, response);
|
serveResource(resourcePrefix + rest, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean serveCached(String category, String relativePath, HttpServletResponse response)
|
private boolean serveCached(String category, String relativePath, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
if (HTTPDModule.getMinecraftAssetsManager() == null)
|
if (module.getMinecraftAssetsManager() == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Path path = HTTPDModule.getMinecraftAssetsManager().resolve(category, relativePath);
|
Path path = module.getMinecraftAssetsManager().resolve(category, relativePath);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -21,10 +21,15 @@ public class AuthenticationEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
private static final String RETURN_TO_COOKIE = "plex_return_to";
|
private static final String RETURN_TO_COOKIE = "plex_return_to";
|
||||||
|
|
||||||
|
public AuthenticationEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/oauth2/login")
|
@GetMapping(endpoint = "/oauth2/login")
|
||||||
public String login(HttpServletRequest request, HttpServletResponse response) throws IOException
|
public String login(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider();
|
OAuth2Provider provider = module.getAuthenticationManager().provider();
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled.");
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled.");
|
||||||
@@ -51,7 +56,7 @@ public class AuthenticationEndpoint extends AbstractServlet
|
|||||||
@GetMapping(endpoint = "/oauth2/callback")
|
@GetMapping(endpoint = "/oauth2/callback")
|
||||||
public String callback(HttpServletRequest request, HttpServletResponse response) throws IOException
|
public String callback(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider();
|
OAuth2Provider provider = module.getAuthenticationManager().provider();
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled.");
|
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled.");
|
||||||
@@ -63,7 +68,7 @@ public class AuthenticationEndpoint extends AbstractServlet
|
|||||||
}
|
}
|
||||||
catch (AuthenticationException e)
|
catch (AuthenticationException e)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().error("OAuth2 callback failed: " + e.getMessage());
|
module.api().logging().error("OAuth2 callback failed: " + e.getMessage());
|
||||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
response.setContentType("text/html; charset=UTF-8");
|
response.setContentType("text/html; charset=UTF-8");
|
||||||
return "<!doctype html><meta charset=utf-8><title>Sign-in failed</title>"
|
return "<!doctype html><meta charset=utf-8><title>Sign-in failed</title>"
|
||||||
@@ -84,7 +89,7 @@ public class AuthenticationEndpoint extends AbstractServlet
|
|||||||
@GetMapping(endpoint = "/oauth2/logout")
|
@GetMapping(endpoint = "/oauth2/logout")
|
||||||
public String logout(HttpServletRequest request, HttpServletResponse response) throws IOException
|
public String logout(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider();
|
OAuth2Provider provider = module.getAuthenticationManager().provider();
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
response.sendRedirect("/");
|
response.sendRedirect("/");
|
||||||
@@ -106,7 +111,7 @@ public class AuthenticationEndpoint extends AbstractServlet
|
|||||||
@MappingHeaders(headers = "content-type;application/json")
|
@MappingHeaders(headers = "content-type;application/json")
|
||||||
public String me(HttpServletRequest request, HttpServletResponse response)
|
public String me(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
OAuth2Provider provider = HTTPDModule.getAuthenticationManager().provider();
|
OAuth2Provider provider = module.getAuthenticationManager().provider();
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
return "{\"authenticated\":false,\"reason\":\"disabled\"}";
|
return "{\"authenticated\":false,\"reason\":\"disabled\"}";
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ public class CommandsEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
private String cachedHtml;
|
private String cachedHtml;
|
||||||
|
|
||||||
|
public CommandsEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/commands/")
|
@GetMapping(endpoint = "/api/commands/")
|
||||||
public String getCommands(HttpServletRequest request, HttpServletResponse response)
|
public String getCommands(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -34,12 +39,12 @@ public class CommandsEndpoint extends AbstractServlet
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String buildSections()
|
private String buildSections()
|
||||||
{
|
{
|
||||||
final SortedMap<String, List<CommandInfo>> commandMap = new TreeMap<>();
|
final SortedMap<String, List<CommandInfo>> commandMap = new TreeMap<>();
|
||||||
|
|
||||||
List<CommandInfo> plexCommands = commandMap.computeIfAbsent("Plex", k -> new ArrayList<>());
|
List<CommandInfo> plexCommands = commandMap.computeIfAbsent("Plex", k -> new ArrayList<>());
|
||||||
for (PlexCommand command : HTTPDModule.plexApi().commands().registeredCommands())
|
for (PlexCommand command : module.api().commands().registeredCommands())
|
||||||
{
|
{
|
||||||
plexCommands.add(CommandInfo.from(command));
|
plexCommands.add(CommandInfo.from(command));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
public class IndefBansEndpoint extends AbstractServlet
|
public class IndefBansEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public IndefBansEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/indefbans/")
|
@GetMapping(endpoint = "/api/indefbans/")
|
||||||
public String getBans(HttpServletRequest request, HttpServletResponse response)
|
public String getBans(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -20,7 +25,7 @@ public class IndefBansEndpoint extends AbstractServlet
|
|||||||
}
|
}
|
||||||
|
|
||||||
response.setHeader("content-type", "application/json");
|
response.setHeader("content-type", "application/json");
|
||||||
return new GsonBuilder().setPrettyPrinting().create().toJson(HTTPDModule.plexApi().punishments().indefiniteBans());
|
return new GsonBuilder().setPrettyPrinting().create().toJson(module.api().punishments().indefiniteBans());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String indefbansHTML(String message)
|
private String indefbansHTML(String message)
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class IndefBansUIEndpoint extends AbstractServlet
|
public class IndefBansUIEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public IndefBansUIEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/indefbans/")
|
@GetMapping(endpoint = "/indefbans/")
|
||||||
public String getBans(HttpServletRequest request, HttpServletResponse response)
|
public String getBans(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -22,7 +27,7 @@ public class IndefBansUIEndpoint extends AbstractServlet
|
|||||||
return errorHTML(signInPrompt(request, "to view this page"));
|
return errorHTML(signInPrompt(request, "to view this page"));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<? extends IndefiniteBanView> bans = HTTPDModule.plexApi().punishments().indefiniteBans();
|
List<? extends IndefiniteBanView> bans = module.api().punishments().indefiniteBans();
|
||||||
return listHTML(bans);
|
return listHTML(bans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.plex.request.impl;
|
package dev.plex.request.impl;
|
||||||
|
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.request.AbstractServlet;
|
import dev.plex.request.AbstractServlet;
|
||||||
import dev.plex.request.GetMapping;
|
import dev.plex.request.GetMapping;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@@ -7,6 +8,11 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
public class IndexEndpoint extends AbstractServlet
|
public class IndexEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public IndexEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "//")
|
@GetMapping(endpoint = "//")
|
||||||
public String getIndex(HttpServletRequest request, HttpServletResponse response)
|
public String getIndex(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.plex.request.impl;
|
package dev.plex.request.impl;
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.request.AbstractServlet;
|
import dev.plex.request.AbstractServlet;
|
||||||
import dev.plex.request.GetMapping;
|
import dev.plex.request.GetMapping;
|
||||||
import dev.plex.request.MappingHeaders;
|
import dev.plex.request.MappingHeaders;
|
||||||
@@ -14,6 +15,11 @@ import org.bukkit.entity.Player;
|
|||||||
|
|
||||||
public class ListEndpoint extends AbstractServlet
|
public class ListEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public ListEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/list/")
|
@GetMapping(endpoint = "/api/list/")
|
||||||
@MappingHeaders(headers = "content-type;application/json")
|
@MappingHeaders(headers = "content-type;application/json")
|
||||||
public String getOnlinePlayers(HttpServletRequest request, HttpServletResponse response)
|
public String getOnlinePlayers(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ public class PlayerAdminEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
|
||||||
|
|
||||||
|
public PlayerAdminEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/player/")
|
@GetMapping(endpoint = "/player/")
|
||||||
public String getPlayer(HttpServletRequest request, HttpServletResponse response)
|
public String getPlayer(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -54,15 +59,15 @@ public class PlayerAdminEndpoint extends AbstractServlet
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PlexPlayerView lookupPlayer(String query)
|
private PlexPlayerView lookupPlayer(String query)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return HTTPDModule.plexApi().players().byUuid(UUID.fromString(query)).orElse(null);
|
return module.api().players().byUuid(UUID.fromString(query)).orElse(null);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ignored)
|
catch (IllegalArgumentException ignored)
|
||||||
{
|
{
|
||||||
return HTTPDModule.plexApi().players().byName(query).orElse(null);
|
return module.api().players().byName(query).orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import org.bukkit.persistence.PersistentDataContainer;
|
|||||||
*/
|
*/
|
||||||
public final class PlayerInventoryBroadcaster
|
public final class PlayerInventoryBroadcaster
|
||||||
{
|
{
|
||||||
private static final PlayerInventoryBroadcaster INSTANCE = new PlayerInventoryBroadcaster();
|
|
||||||
private static final long REFRESH_TICKS = 20L; // 1 second
|
private static final long REFRESH_TICKS = 20L; // 1 second
|
||||||
private static final int MAX_NAME_CHARS = 256;
|
private static final int MAX_NAME_CHARS = 256;
|
||||||
private static final int MAX_LORE_LINES = 20;
|
private static final int MAX_LORE_LINES = 20;
|
||||||
@@ -53,11 +52,7 @@ public final class PlayerInventoryBroadcaster
|
|||||||
private static final int MAX_PDC_KEYS = 64;
|
private static final int MAX_PDC_KEYS = 64;
|
||||||
private static final int MAX_PDC_KEY_CHARS = 128;
|
private static final int MAX_PDC_KEY_CHARS = 128;
|
||||||
|
|
||||||
public static PlayerInventoryBroadcaster get()
|
private final HTTPDModule module;
|
||||||
{
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<UUID, Set<Subscriber>> subscribers = new ConcurrentHashMap<>();
|
private final Map<UUID, Set<Subscriber>> subscribers = new ConcurrentHashMap<>();
|
||||||
private final Map<UUID, String> cachedPayloads = new ConcurrentHashMap<>();
|
private final Map<UUID, String> cachedPayloads = new ConcurrentHashMap<>();
|
||||||
private final AtomicInteger subscriberCount = new AtomicInteger();
|
private final AtomicInteger subscriberCount = new AtomicInteger();
|
||||||
@@ -66,14 +61,17 @@ public final class PlayerInventoryBroadcaster
|
|||||||
private ScheduledTask refreshTask;
|
private ScheduledTask refreshTask;
|
||||||
private int maxConnections = 32;
|
private int maxConnections = 32;
|
||||||
|
|
||||||
private PlayerInventoryBroadcaster() {}
|
public PlayerInventoryBroadcaster(HTTPDModule module)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void start()
|
public synchronized void start()
|
||||||
{
|
{
|
||||||
if (executor != null) return;
|
if (executor != null) return;
|
||||||
|
|
||||||
maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32);
|
maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32);
|
||||||
int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2));
|
int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2));
|
||||||
|
|
||||||
executor = Executors.newScheduledThreadPool(threads, r ->
|
executor = Executors.newScheduledThreadPool(threads, r ->
|
||||||
{
|
{
|
||||||
@@ -84,11 +82,11 @@ public final class PlayerInventoryBroadcaster
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::tick, 1L, REFRESH_TICKS);
|
refreshTask = module.api().scheduler().runGlobalTimer(this::tick, 1L, REFRESH_TICKS);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("PlayerInventoryBroadcaster: could not register refresh task: " + t.getMessage());
|
module.api().logging().debug("PlayerInventoryBroadcaster: could not register refresh task: " + t.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -97,7 +95,7 @@ public final class PlayerInventoryBroadcaster
|
|||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("PlayerInventoryBroadcaster: NBT-API preload failed: " + t.getMessage());
|
module.api().logging().debug("PlayerInventoryBroadcaster: NBT-API preload failed: " + t.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +179,7 @@ public final class PlayerInventoryBroadcaster
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () ->
|
ScheduledTask task = module.api().scheduler().runEntity(player, () ->
|
||||||
{
|
{
|
||||||
String json;
|
String json;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -36,14 +36,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public final class PlayersBroadcaster
|
public final class PlayersBroadcaster
|
||||||
{
|
{
|
||||||
private static final PlayersBroadcaster INSTANCE = new PlayersBroadcaster();
|
|
||||||
private static final long REFRESH_TICKS = 100L; // 5 seconds at 20 TPS
|
private static final long REFRESH_TICKS = 100L; // 5 seconds at 20 TPS
|
||||||
|
|
||||||
public static PlayersBroadcaster get()
|
private final HTTPDModule module;
|
||||||
{
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Set<Subscriber> subscribers = ConcurrentHashMap.newKeySet();
|
private final Set<Subscriber> subscribers = ConcurrentHashMap.newKeySet();
|
||||||
private final AtomicInteger subscriberCount = new AtomicInteger();
|
private final AtomicInteger subscriberCount = new AtomicInteger();
|
||||||
private final AtomicBoolean refreshScheduled = new AtomicBoolean(false);
|
private final AtomicBoolean refreshScheduled = new AtomicBoolean(false);
|
||||||
@@ -56,14 +51,17 @@ public final class PlayersBroadcaster
|
|||||||
private Listener listener;
|
private Listener listener;
|
||||||
private int maxConnections = 32;
|
private int maxConnections = 32;
|
||||||
|
|
||||||
private PlayersBroadcaster() {}
|
public PlayersBroadcaster(HTTPDModule module)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void start()
|
public synchronized void start()
|
||||||
{
|
{
|
||||||
if (executor != null) return;
|
if (executor != null) return;
|
||||||
|
|
||||||
maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32);
|
maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32);
|
||||||
int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2));
|
int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2));
|
||||||
|
|
||||||
executor = Executors.newScheduledThreadPool(threads, r ->
|
executor = Executors.newScheduledThreadPool(threads, r ->
|
||||||
{
|
{
|
||||||
@@ -75,20 +73,20 @@ public final class PlayersBroadcaster
|
|||||||
listener = new PlayersListener();
|
listener = new PlayersListener();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().listeners().register(listener);
|
module.api().listeners().register(listener);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("PlayersBroadcaster: could not register Bukkit listener: " + t.getMessage());
|
module.api().logging().debug("PlayersBroadcaster: could not register Bukkit listener: " + t.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
refreshTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::refreshAndBroadcast, 1L, REFRESH_TICKS);
|
refreshTask = module.api().scheduler().runGlobalTimer(this::refreshAndBroadcast, 1L, REFRESH_TICKS);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("PlayersBroadcaster: could not register refresh task: " + t.getMessage());
|
module.api().logging().debug("PlayersBroadcaster: could not register refresh task: " + t.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +171,7 @@ public final class PlayersBroadcaster
|
|||||||
Player player = online.get(i);
|
Player player = online.get(i);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ScheduledTask task = HTTPDModule.plexApi().scheduler().runEntity(player, () ->
|
ScheduledTask task = module.api().scheduler().runEntity(player, () ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -276,7 +274,7 @@ public final class PlayersBroadcaster
|
|||||||
if (!refreshScheduled.compareAndSet(false, true)) return;
|
if (!refreshScheduled.compareAndSet(false, true)) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().scheduler().runGlobalLater(() ->
|
module.api().scheduler().runGlobalLater(() ->
|
||||||
{
|
{
|
||||||
refreshScheduled.set(false);
|
refreshScheduled.set(false);
|
||||||
refreshAndBroadcast();
|
refreshAndBroadcast();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.plex.request.impl;
|
package dev.plex.request.impl;
|
||||||
|
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.request.AbstractServlet;
|
import dev.plex.request.AbstractServlet;
|
||||||
import dev.plex.request.GetMapping;
|
import dev.plex.request.GetMapping;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
@@ -7,6 +8,11 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
public class PlayersEndpoint extends AbstractServlet
|
public class PlayersEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public PlayersEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/players/")
|
@GetMapping(endpoint = "/players/")
|
||||||
public String getPlayers(HttpServletRequest request, HttpServletResponse response)
|
public String getPlayers(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class PunishmentsEndpoint extends AbstractServlet
|
public class PunishmentsEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public PunishmentsEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/punishments/")
|
@GetMapping(endpoint = "/api/punishments/")
|
||||||
public String getPunishments(HttpServletRequest request, HttpServletResponse response)
|
public String getPunishments(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -28,11 +33,11 @@ public class PunishmentsEndpoint extends AbstractServlet
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
UUID pathUUID = UUID.fromString(request.getPathInfo().replace("/", ""));
|
UUID pathUUID = UUID.fromString(request.getPathInfo().replace("/", ""));
|
||||||
punishedPlayer = HTTPDModule.plexApi().players().byUuid(pathUUID).orElse(null);
|
punishedPlayer = module.api().players().byUuid(pathUUID).orElse(null);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ignored)
|
catch (IllegalArgumentException ignored)
|
||||||
{
|
{
|
||||||
punishedPlayer = HTTPDModule.plexApi().players().byName(request.getPathInfo().replace("/", "")).orElse(null);
|
punishedPlayer = module.api().players().byName(request.getPathInfo().replace("/", "")).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (punishedPlayer == null)
|
if (punishedPlayer == null)
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ public class PunishmentsUIEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
|
||||||
|
|
||||||
|
public PunishmentsUIEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/punishments/")
|
@GetMapping(endpoint = "/punishments/")
|
||||||
public String getPunishments(HttpServletRequest request, HttpServletResponse response)
|
public String getPunishments(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -49,15 +54,15 @@ public class PunishmentsUIEndpoint extends AbstractServlet
|
|||||||
return resultsHTML(punished, punishments, showIps);
|
return resultsHTML(punished, punishments, showIps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PlexPlayerView lookupPlayer(String query)
|
private PlexPlayerView lookupPlayer(String query)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return HTTPDModule.plexApi().players().byUuid(UUID.fromString(query)).orElse(null);
|
return module.api().players().byUuid(UUID.fromString(query)).orElse(null);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException ignored)
|
catch (IllegalArgumentException ignored)
|
||||||
{
|
{
|
||||||
return HTTPDModule.plexApi().players().byName(query).orElse(null);
|
return module.api().players().byName(query).orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ public class SchematicDownloadEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
List<File> files = new ArrayList<>();
|
List<File> files = new ArrayList<>();
|
||||||
|
|
||||||
|
public SchematicDownloadEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/schematics/download/")
|
@GetMapping(endpoint = "/api/schematics/download/")
|
||||||
public String downloadSchematic(HttpServletRequest request, HttpServletResponse response)
|
public String downloadSchematic(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
@@ -57,7 +62,7 @@ public class SchematicDownloadEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] schemData = HTTPDModule.fileCache.getFile(schemFile);
|
byte[] schemData = module.getFileCache().getFile(schemFile);
|
||||||
if (schemData != null)
|
if (schemData != null)
|
||||||
{
|
{
|
||||||
outputStream.write(schemData);
|
outputStream.write(schemData);
|
||||||
@@ -71,11 +76,11 @@ public class SchematicDownloadEndpoint extends AbstractServlet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logDownload(HttpServletRequest request, File schemFile)
|
private void logDownload(HttpServletRequest request, File schemFile)
|
||||||
{
|
{
|
||||||
AuthenticatedUser user = currentUser(request);
|
AuthenticatedUser user = currentUser(request);
|
||||||
String who = user != null ? user.username() + " (xf:" + user.userId() + ")" : request.getRemoteAddr();
|
String who = user != null ? user.username() + " (xf:" + user.userId() + ")" : request.getRemoteAddr();
|
||||||
HTTPDModule.plexApi().logging().info("{0} downloaded schematic {1}", who, schemFile.getName());
|
module.api().logging().info("{0} downloaded schematic {1}", who, schemFile.getName());
|
||||||
Log.log("{0} downloaded schematic {1}", who, schemFile.getName());
|
Log.log("{0} downloaded schematic {1}", who, schemFile.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +123,7 @@ public class SchematicDownloadEndpoint extends AbstractServlet
|
|||||||
{
|
{
|
||||||
if (fileEntry.isDirectory())
|
if (fileEntry.isDirectory())
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("Found directory");
|
module.api().logging().debug("Found directory");
|
||||||
listFilesForFolder(fileEntry);
|
listFilesForFolder(fileEntry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.plex.request.impl;
|
package dev.plex.request.impl;
|
||||||
|
|
||||||
|
import dev.plex.HTTPDModule;
|
||||||
import dev.plex.authentication.AuthenticatedUser;
|
import dev.plex.authentication.AuthenticatedUser;
|
||||||
import dev.plex.request.AbstractServlet;
|
import dev.plex.request.AbstractServlet;
|
||||||
import dev.plex.request.GetMapping;
|
import dev.plex.request.GetMapping;
|
||||||
@@ -8,6 +9,11 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
public class SchematicUploadEndpoint extends AbstractServlet
|
public class SchematicUploadEndpoint extends AbstractServlet
|
||||||
{
|
{
|
||||||
|
public SchematicUploadEndpoint(HTTPDModule module)
|
||||||
|
{
|
||||||
|
super(module);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(endpoint = "/api/schematics/upload/")
|
@GetMapping(endpoint = "/api/schematics/upload/")
|
||||||
public String uploadSchematic(HttpServletRequest request, HttpServletResponse response)
|
public String uploadSchematic(HttpServletRequest request, HttpServletResponse response)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,13 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public final class StatsBroadcaster
|
public final class StatsBroadcaster
|
||||||
{
|
{
|
||||||
private static final StatsBroadcaster INSTANCE = new StatsBroadcaster();
|
private final HTTPDModule module;
|
||||||
|
|
||||||
public static StatsBroadcaster get()
|
|
||||||
{
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Set<Subscriber> subscribers = ConcurrentHashMap.newKeySet();
|
private final Set<Subscriber> subscribers = ConcurrentHashMap.newKeySet();
|
||||||
private final AtomicInteger subscriberCount = new AtomicInteger();
|
private final AtomicInteger subscriberCount = new AtomicInteger();
|
||||||
|
|
||||||
@@ -58,15 +52,18 @@ public final class StatsBroadcaster
|
|||||||
private int maxConnections = 32;
|
private int maxConnections = 32;
|
||||||
private long broadcastIntervalMs = 2000L;
|
private long broadcastIntervalMs = 2000L;
|
||||||
|
|
||||||
private StatsBroadcaster() {}
|
public StatsBroadcaster(HTTPDModule module)
|
||||||
|
{
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void start()
|
public synchronized void start()
|
||||||
{
|
{
|
||||||
if (executor != null) return;
|
if (executor != null) return;
|
||||||
|
|
||||||
maxConnections = HTTPDModule.moduleConfig.getInt("server.sse.max-connections", 32);
|
maxConnections = module.getModuleConfig().getInt("server.sse.max-connections", 32);
|
||||||
broadcastIntervalMs = HTTPDModule.moduleConfig.getLong("server.sse.broadcast-interval-ms", 2000L);
|
broadcastIntervalMs = module.getModuleConfig().getLong("server.sse.broadcast-interval-ms", 2000L);
|
||||||
int threads = Math.max(1, HTTPDModule.moduleConfig.getInt("server.sse.threads", 2));
|
int threads = Math.max(1, module.getModuleConfig().getInt("server.sse.threads", 2));
|
||||||
|
|
||||||
executor = Executors.newScheduledThreadPool(threads, r ->
|
executor = Executors.newScheduledThreadPool(threads, r ->
|
||||||
{
|
{
|
||||||
@@ -77,11 +74,11 @@ public final class StatsBroadcaster
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bukkitTask = HTTPDModule.plexApi().scheduler().runGlobalTimer(this::sampleBukkit, 1L, 40L);
|
bukkitTask = module.api().scheduler().runGlobalTimer(this::sampleBukkit, 1L, 40L);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
HTTPDModule.plexApi().logging().debug("StatsBroadcaster: could not register Bukkit sampling task: " + t.getMessage());
|
module.api().logging().debug("StatsBroadcaster: could not register Bukkit sampling task: " + t.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastTask = executor.scheduleAtFixedRate(
|
broadcastTask = executor.scheduleAtFixedRate(
|
||||||
|
|||||||
Reference in New Issue
Block a user