api url with redirect to discord invite

This commit is contained in:
2025-07-09 21:21:21 +02:00
parent ee72f8824b
commit 2dabb1885c
5 changed files with 138 additions and 0 deletions
@@ -98,4 +98,8 @@ public class Main {
LOGGER.info("Welcome to the Inviter Application!"); LOGGER.info("Welcome to the Inviter Application!");
} }
public static Storage getStorage() {
return storage;
}
} }
@@ -2,6 +2,7 @@ package me.youhavetrouble.inviter.http;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import me.youhavetrouble.inviter.Main; import me.youhavetrouble.inviter.Main;
import me.youhavetrouble.inviter.http.endpoints.HandlerKernel;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@@ -14,6 +15,7 @@ public class ApiServer {
public ApiServer(String hostname, int port) throws IllegalArgumentException, IOException { public ApiServer(String hostname, int port) throws IllegalArgumentException, IOException {
server = HttpServer.create(new InetSocketAddress(hostname, port), 0); server = HttpServer.create(new InetSocketAddress(hostname, port), 0);
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
server.createContext("/", new HandlerKernel());
server.start(); server.start();
Main.LOGGER.info("Http API server started on {}:{}", hostname, port); Main.LOGGER.info("Http API server started on {}:{}", hostname, port);
} }
@@ -0,0 +1,15 @@
package me.youhavetrouble.inviter.http.endpoints;
import com.sun.net.httpserver.HttpExchange;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.regex.Pattern;
public interface EndpointHandler {
@NotNull Pattern pathPattern();
void handle(@NotNull HttpExchange exchange) throws IOException;
}
@@ -0,0 +1,74 @@
package me.youhavetrouble.inviter.http.endpoints;
import com.sun.net.httpserver.HttpExchange;
import me.youhavetrouble.inviter.DiscordInvite;
import me.youhavetrouble.inviter.Main;
import me.youhavetrouble.inviter.storage.Storage;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.regex.Pattern;
public class GetDiscordInviteByGuildId implements EndpointHandler {
private final Pattern pathPattern = Pattern.compile("^/api/v1/discord/\\d{10,18}$");
@NotNull
@Override
public Pattern pathPattern() {
return pathPattern;
}
@Override
public void handle(@NotNull HttpExchange exchange) throws IOException {
if (!exchange.getRequestMethod().equals("GET")) {
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
return;
}
String path = exchange.getRequestURI().getPath();
String[] parts = path.split("/");
String guildId = parts[parts.length - 1];
long guildIdLong;
try {
guildIdLong = Long.parseLong(guildId);
} catch (NumberFormatException e) {
exchange.sendResponseHeaders(400, -1); // Bad Request
return;
}
Storage storage = Main.getStorage();
DiscordInvite invite = storage.getInvite(guildIdLong);
if (invite == null) {
exchange.sendResponseHeaders(404, -1); // Not Found
return;
}
String inviteUrl = "https://discord.gg/" + invite.code();
switch (exchange.getRequestHeaders().getFirst("Accept")) {
case "text/plain" -> {
exchange.getResponseHeaders().set("Content-Type", "text/plain");
exchange.sendResponseHeaders(200, inviteUrl.length());
exchange.getResponseBody().write(inviteUrl.getBytes());
}
case "application/json" -> {
exchange.getResponseHeaders().set("Content-Type", "application/json");
String jsonResponse = "{\"url\": \"" + inviteUrl + "\"}";
exchange.sendResponseHeaders(200, jsonResponse.length());
exchange.getResponseBody().write(jsonResponse.getBytes());
}
default -> {
exchange.getResponseHeaders().set("Location", inviteUrl);
exchange.sendResponseHeaders(307, -1);
}
}
}
}
@@ -0,0 +1,43 @@
package me.youhavetrouble.inviter.http.endpoints;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import me.youhavetrouble.inviter.Main;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class HandlerKernel implements HttpHandler {
private final Set<EndpointHandler> handlers = new HashSet<>();
public HandlerKernel() {
handlers.add(new GetDiscordInviteByGuildId());
}
@Override
public void handle(HttpExchange exchange) throws IOException {
String path = exchange.getRequestURI().getPath();
if (path.endsWith("/")) path = path.substring(0, path.length() - 1);
exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");
exchange.getResponseHeaders().set("Access-Control-Allow-Methods", "*");
exchange.getResponseHeaders().set("Access-Control-Allow-Headers", "Content-Type");
for (EndpointHandler handler : handlers) {
if (handler.pathPattern().matcher(path).matches()) {
handler.handle(exchange);
return;
}
}
try {
exchange.sendResponseHeaders(404, -1);
exchange.getResponseBody().close();
} catch (Exception e) {
Main.LOGGER.error("Error handling request for {}: {}", path, e.getMessage());
}
}
}