diff --git a/Dockerfile b/Dockerfile index 671c44d..391741a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,12 @@ ARG APP_VERSION=0.0.0-SNAPSHOT RUN sh ./gradlew --no-daemon "-PappVersion=${APP_VERSION}" shadowJar FROM eclipse-temurin:25-jre +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY --from=builder /app/build/libs/InfinityBot-*-all.jar /app/InfinityBot.jar USER 10001:10001 +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ + CMD ["curl", "--fail", "--silent", "--show-error", "--noproxy", "*", "--max-time", "4", "http://127.0.0.1:8080/health"] ENTRYPOINT ["java", "--enable-native-access=ALL-UNNAMED", "-jar", "/app/InfinityBot.jar"] diff --git a/README.md b/README.md index ff8e74d..29ed602 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ poToken/visitor-data pairs are supplied manually and are not automatically gener Cipher support solves signature deciphering, **not** YouTube IP blocks, age restrictions, or all sign-in challenges. Test playback on the intended deployment host. See [youtube-source remote cipher documentation](https://github.com/lavalink-devs/youtube-source#using-a-remote-cipher-server). +## Healthcheck + +The Docker image includes a healthcheck that polls JDA's Discord connection state, checking that the bot is connected rather than just running. + ## Build locally Requires JDK 25. JDAVE provides Discord voice encryption (DAVE). diff --git a/src/main/java/net/theinfinitymc/infinitybot/HealthCheck.java b/src/main/java/net/theinfinitymc/infinitybot/HealthCheck.java new file mode 100644 index 0000000..d1be2a0 --- /dev/null +++ b/src/main/java/net/theinfinitymc/infinitybot/HealthCheck.java @@ -0,0 +1,24 @@ +package net.theinfinitymc.infinitybot; + +import com.sun.net.httpserver.HttpServer; +import net.dv8tion.jda.api.JDA; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** Local readiness check backed by JDA's live Discord gateway state. */ +public final class HealthCheck { + private HealthCheck() {} + + static void start(JDA jda) throws IOException { + HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 8080), 0); + server.createContext("/health", exchange -> { + try { + exchange.sendResponseHeaders(jda.getStatus() == JDA.Status.CONNECTED ? 200 : 503, -1); + } finally { + exchange.close(); + } + }); + server.start(); + } +} diff --git a/src/main/java/net/theinfinitymc/infinitybot/InfinityBot.java b/src/main/java/net/theinfinitymc/infinitybot/InfinityBot.java index a76df74..81bd00f 100644 --- a/src/main/java/net/theinfinitymc/infinitybot/InfinityBot.java +++ b/src/main/java/net/theinfinitymc/infinitybot/InfinityBot.java @@ -36,6 +36,7 @@ public static void main(String[] args) { .build(); listener.registerCommands(jda, audioManager); updateActivity(); + HealthCheck.start(jda); } catch (Exception exception) { throw new IllegalStateException("Failed to load InfinityBot.", exception); }