Add notification server and file download service implementation
- Introduce `NotificationServer` for handling HTTP notifications. - Register `ProxyShutdownEvent` to stop the notification server on shutdown. - Add `FileDownloadService` for asynchronous file downloads using the configured endpoint. - Update `Config` to include `download-endpoint`. - Add Javalin and SLF4J dependencies for the HTTP server.
This commit is contained in:
parent
df8f08544b
commit
d4359bf480
|
|
@ -18,6 +18,8 @@ dependencies {
|
||||||
compileOnly("com.gitlab.ruany:LiteBansAPI:0.6.1")
|
compileOnly("com.gitlab.ruany:LiteBansAPI:0.6.1")
|
||||||
compileOnly("org.projectlombok:lombok:1.18.30")
|
compileOnly("org.projectlombok:lombok:1.18.30")
|
||||||
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
annotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||||
|
implementation("io.javalin:javalin:6.6.0") // Javalin for HTTP server
|
||||||
|
implementation("org.slf4j:slf4j-api:2.0.9") // Required by Javalin
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ package com.alttd.webinterface;
|
||||||
import com.alttd.webinterface.commands.Login;
|
import com.alttd.webinterface.commands.Login;
|
||||||
import com.alttd.webinterface.commands.Reload;
|
import com.alttd.webinterface.commands.Reload;
|
||||||
import com.alttd.webinterface.config.Config;
|
import com.alttd.webinterface.config.Config;
|
||||||
|
import com.alttd.webinterface.http.NotificationServer;
|
||||||
import com.alttd.webinterface.listeners.ProxyPlayerListener;
|
import com.alttd.webinterface.listeners.ProxyPlayerListener;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.velocitypowered.api.event.Subscribe;
|
import com.velocitypowered.api.event.Subscribe;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||||
|
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -18,6 +20,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
)
|
)
|
||||||
public class WebInterface {
|
public class WebInterface {
|
||||||
private final ProxyServer server;
|
private final ProxyServer server;
|
||||||
|
private NotificationServer notificationServer;
|
||||||
|
private static final int HTTP_PORT = 8080; // Default port, could be made configurable
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WebInterface(ProxyServer proxyServer) {
|
public WebInterface(ProxyServer proxyServer) {
|
||||||
|
|
@ -29,11 +33,36 @@ public class WebInterface {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
server.getEventManager().register(this, new ProxyPlayerListener());
|
server.getEventManager().register(this, new ProxyPlayerListener());
|
||||||
loadCommands();
|
loadCommands();
|
||||||
|
startNotificationServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||||
|
stopNotificationServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startNotificationServer() {
|
||||||
|
notificationServer = new NotificationServer(HTTP_PORT);
|
||||||
|
notificationServer.startServer();
|
||||||
|
log.info("Started notification server on port {}", HTTP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopNotificationServer() {
|
||||||
|
if (notificationServer != null) {
|
||||||
|
notificationServer.stopServer();
|
||||||
|
log.info("Stopped notification server");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadConfig() {
|
public void reloadConfig() {
|
||||||
Config.init();
|
Config.init();
|
||||||
log.info("Reloaded WebInterface config.");
|
log.info("Reloaded WebInterface config.");
|
||||||
|
|
||||||
|
// Restart the notification server to apply any configuration changes
|
||||||
|
if (notificationServer != null) {
|
||||||
|
stopNotificationServer();
|
||||||
|
}
|
||||||
|
startNotificationServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadCommands() {// all (proxy)commands go here
|
public void loadCommands() {// all (proxy)commands go here
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ public final class Config {
|
||||||
|
|
||||||
public static String LOGIN_CODE_ENDPOINT = "https://alttd.com/login/requestNewUserLogin/<uuid>";
|
public static String LOGIN_CODE_ENDPOINT = "https://alttd.com/login/requestNewUserLogin/<uuid>";
|
||||||
public static String LOGIN_ENDPOINT = "https://alttd.com/login";
|
public static String LOGIN_ENDPOINT = "https://alttd.com/login";
|
||||||
|
public static String DOWNLOAD_ENDPOINT = "https://alttd.com/particles/download";
|
||||||
public static String SECRET = "";
|
public static String SECRET = "";
|
||||||
private static void site() {
|
private static void site() {
|
||||||
if (SECRET.isEmpty()) {
|
if (SECRET.isEmpty()) {
|
||||||
|
|
@ -210,6 +211,7 @@ public final class Config {
|
||||||
}
|
}
|
||||||
SECRET = getString("secret", SECRET);
|
SECRET = getString("secret", SECRET);
|
||||||
LOGIN_ENDPOINT = getString("login-endpoint", LOGIN_ENDPOINT);
|
LOGIN_ENDPOINT = getString("login-endpoint", LOGIN_ENDPOINT);
|
||||||
|
DOWNLOAD_ENDPOINT = getString("download-endpoint", DOWNLOAD_ENDPOINT);
|
||||||
LOGIN_CODE_ENDPOINT = getString("login-code-endpoint", LOGIN_CODE_ENDPOINT);
|
LOGIN_CODE_ENDPOINT = getString("login-code-endpoint", LOGIN_CODE_ENDPOINT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.alttd.webinterface.http;
|
||||||
|
|
||||||
|
import com.alttd.webinterface.web_interact.FileDownloadService;
|
||||||
|
import io.javalin.Javalin;
|
||||||
|
import io.javalin.http.Context;
|
||||||
|
import io.javalin.http.HttpStatus;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP server that exposes the /notify/<file>.json endpoint.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class NotificationServer {
|
||||||
|
private final int port;
|
||||||
|
private Javalin app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new NotificationServer that listens on the specified port.
|
||||||
|
*
|
||||||
|
* @param port The port to listen on
|
||||||
|
*/
|
||||||
|
public NotificationServer(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the server.
|
||||||
|
*/
|
||||||
|
public void startServer() {
|
||||||
|
try {
|
||||||
|
app = Javalin.create(config -> {
|
||||||
|
config.showJavalinBanner = false;
|
||||||
|
}).start(port);
|
||||||
|
|
||||||
|
app.get("/notify/{file}.json", this::handleNotifyRequest);
|
||||||
|
|
||||||
|
log.info("NotificationServer started on port {}", port);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to start NotificationServer", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the server.
|
||||||
|
*/
|
||||||
|
public void stopServer() {
|
||||||
|
if (app != null) {
|
||||||
|
app.stop();
|
||||||
|
log.info("NotificationServer stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles requests to the /notify/<file>.json endpoint.
|
||||||
|
*/
|
||||||
|
private void handleNotifyRequest(Context ctx) {
|
||||||
|
String uri = ctx.path();
|
||||||
|
log.info("Received request: {} {}", ctx.method(), uri);
|
||||||
|
|
||||||
|
String fileName = ctx.pathParam("file") + ".json";
|
||||||
|
log.info("Requested file: {}", fileName);
|
||||||
|
|
||||||
|
FileDownloadService.downloadFileAsync(fileName).thenAccept(fileData -> {
|
||||||
|
if (fileData.isPresent()) {
|
||||||
|
ctx.contentType("application/json");
|
||||||
|
ctx.result(fileData.get());
|
||||||
|
} else {
|
||||||
|
ctx.status(HttpStatus.NOT_FOUND);
|
||||||
|
ctx.contentType("text/plain");
|
||||||
|
ctx.result("File not found or download failed");
|
||||||
|
}
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
log.error("Error downloading file: {}", fileName, e);
|
||||||
|
ctx.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
ctx.contentType("text/plain");
|
||||||
|
ctx.result("Failed to handle download");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.alttd.webinterface.web_interact;
|
||||||
|
|
||||||
|
import com.alttd.webinterface.config.Config;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for downloading files from the configured download endpoint.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FileDownloadService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously downloads a file from the configured download endpoint.
|
||||||
|
*
|
||||||
|
* @param fileName The name of the file to download
|
||||||
|
* @return A CompletableFuture containing an Optional with the file content if successful,
|
||||||
|
* or an empty Optional otherwise
|
||||||
|
*/
|
||||||
|
public static CompletableFuture<Optional<byte[]>> downloadFileAsync(String fileName) {
|
||||||
|
String downloadUrl = Config.DOWNLOAD_ENDPOINT;
|
||||||
|
if (!downloadUrl.endsWith("/")) {
|
||||||
|
downloadUrl += "/";
|
||||||
|
}
|
||||||
|
downloadUrl += fileName;
|
||||||
|
|
||||||
|
log.debug("Downloading file from {}", downloadUrl);
|
||||||
|
|
||||||
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(downloadUrl))
|
||||||
|
.header("Authorization", "SECRET " + Config.SECRET)
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return client.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray())
|
||||||
|
.thenApply(response -> {
|
||||||
|
if (response.statusCode() == HttpServletResponse.SC_OK) {
|
||||||
|
log.debug("Successfully downloaded file: {}", fileName);
|
||||||
|
return Optional.of(response.body());
|
||||||
|
} else {
|
||||||
|
log.error("Failed to download file: {}. Status code: {}", fileName, response.statusCode());
|
||||||
|
return Optional.<byte[]>empty();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exceptionally(e -> {
|
||||||
|
log.error("Exception occurred while downloading file: {}", fileName, e);
|
||||||
|
return Optional.empty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user