package com.alttd.webinterface.http; import com.alttd.webinterface.config.Config; 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.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; /** * HTTP server that exposes the /notify/.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); app.get("/notify/{uuid}/{file}.json", this::handleNotifyRequestUuid); 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/.json endpoint. */ private void handleNotifyRequest(Context ctx) { String uri = ctx.path(); log.info("Received download request: {} {}", ctx.method(), uri); String fileName = ctx.pathParam("file") + ".json"; log.info("Requested file: {}", fileName); CompletableFuture> optionalCompletableFuture = FileDownloadService.downloadFileAsync(fileName); downloadFile(ctx, optionalCompletableFuture); } /** * Handles requests to the /notify/.json endpoint. */ private void handleNotifyRequestUuid(Context ctx) { String uri = ctx.path(); log.info("Received UUID request: {} {}", ctx.method(), uri); String stringUUID = ctx.pathParam("uuid"); log.info("Requested uuid: {}", stringUUID); UUID uuid; try { uuid = UUID.fromString(stringUUID); } catch (Exception e) { log.error("Invalid UUID: {}", stringUUID, e); ctx.status(HttpStatus.BAD_REQUEST); ctx.contentType("text/plain"); ctx.result("Invalid UUID format"); return; } String fileName = ctx.pathParam("file") + ".json"; log.info("Requested uuid file: {}", fileName); CompletableFuture> optionalCompletableFuture = FileDownloadService.downloadFileAsync(uuid, fileName); downloadFile(ctx, optionalCompletableFuture); } private void downloadFile(Context ctx, CompletableFuture> optionalCompletableFuture) { optionalCompletableFuture.thenAccept(fileData -> { if (fileData.isPresent()) { String fileName = ctx.pathParam("file") + ".json"; String uuid = null; try { uuid = ctx.pathParam("uuid"); } catch (Exception ignored) { log.debug("UUID not present"); } boolean saved = saveFileToDisk(fileData.get(), fileName, uuid); if (saved) { ctx.status(HttpStatus.OK); ctx.contentType("text/plain"); ctx.result("File downloaded and saved successfully"); } else { ctx.status(HttpStatus.INTERNAL_SERVER_ERROR); ctx.contentType("text/plain"); ctx.result("Failed to save file to disk"); } } 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", e); ctx.status(HttpStatus.INTERNAL_SERVER_ERROR); ctx.contentType("text/plain"); ctx.result("Failed to handle download"); return null; }); } /** * Saves the downloaded file to disk. * * @param fileData The binary content of the file * @param fileName The name of the file * @param uuid Optional UUID for organizing files * @return true if the file was saved successfully, false otherwise */ private boolean saveFileToDisk(byte[] fileData, String fileName, String uuid) { try { Path basePath = Paths.get(Config.DOWNLOAD_DIR); Path filePath; if (uuid != null) { filePath = basePath.resolve(uuid).resolve(fileName); } else { filePath = basePath.resolve(fileName); } Files.createDirectories(filePath.getParent()); Files.write(filePath, fileData); log.info("File saved to: {}", filePath.toAbsolutePath()); return true; } catch (IOException e) { log.error("Error saving file to disk", e); return false; } } }