1
0
Fork 0

feat(docker): gate Discord bot on DISCORD_TOKEN presence in container entrypoint

This commit is contained in:
Jason Fraley 2026-08-13 20:04:27 -04:00
parent 7e0532f43e
commit 6c97b1a41c
3 changed files with 59 additions and 0 deletions

View file

@ -121,9 +121,12 @@ EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/api/health" >/dev/null 2>&1 || exit 1 CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/api/health" >/dev/null 2>&1 || exit 1
COPY --chmod=755 docker-entrypoint.sh /docker-entrypoint.sh
USER node USER node
# Next.js standalone server (node process). Plain `node server.js` — Bun is # Next.js standalone server (node process). Plain `node server.js` — Bun is
# installed for the `docker exec` one-shot bins, not for the web runtime # installed for the `docker exec` one-shot bins, not for the web runtime
# (Next is shipped and tested on Node). # (Next is shipped and tested on Node).
ENTRYPOINT ["/bin/sh", "/docker-entrypoint.sh"]
CMD ["node", "server.js"] CMD ["node", "server.js"]

33
docker-entrypoint.sh Executable file
View file

@ -0,0 +1,33 @@
#!/bin/sh
# docker-entrypoint.sh — Polaris Task Force container entrypoint (PID 1).
# Always starts the web server (CMD: node server.js, received as "$@").
# Starts the Discord bot (`bun run bot`) ONLY when DISCORD_TOKEN is set
# (Coolify per-environment activation gate; dev/stage leave it unset).
# Forwards SIGTERM/SIGINT to both children, reaps them, and exits with
# the web server's exit status.
"$@" &
WEB_PID=$!
BOT_PID=""
if [ -n "${DISCORD_TOKEN:-}" ]; then
echo "[entrypoint] DISCORD_TOKEN is set — starting Discord bot" >&2
bun run bot &
BOT_PID=$!
else
echo "[entrypoint] DISCORD_TOKEN is not set — Discord bot disabled" >&2
fi
forward() {
signal=$1
[ -z "$WEB_PID" ] || kill -"$signal" "$WEB_PID" 2>/dev/null || true
[ -z "$BOT_PID" ] || kill -"$signal" "$BOT_PID" 2>/dev/null || true
}
trap 'forward TERM' TERM
trap 'forward INT' INT
while kill -0 "$WEB_PID" 2>/dev/null; do
wait "$WEB_PID"
status=$?
done
if [ -n "$BOT_PID" ]; then
kill -TERM "$BOT_PID" 2>/dev/null || true
wait "$BOT_PID" 2>/dev/null || true
fi
exit "${status:-0}"

View file

@ -15,6 +15,29 @@ const main = async () => {
intents: [GatewayIntentBits.Guilds], intents: [GatewayIntentBits.Guilds],
}); });
let shuttingDown = false;
const shutdown = async (signal: NodeJS.Signals): Promise<void> => {
if (shuttingDown) {
payload.logger.warn(`[Bot] ${signal} received again — forcing exit`);
process.exit(143);
}
shuttingDown = true;
payload.logger.info(`[Bot] ${signal} received — shutting down gracefully`);
try {
if (client.isReady()) {
await client.destroy();
}
payload.logger.info("[Bot] Discord client destroyed");
process.exit(0);
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error";
payload.logger.error(`[Bot] Error during shutdown: ${message}`);
process.exit(1);
}
};
process.on("SIGTERM", () => void shutdown("SIGTERM"));
process.on("SIGINT", () => void shutdown("SIGINT"));
const commandExecutors = new Map( const commandExecutors = new Map(
commands.map((command) => [command.builder.name, command.execute]), commands.map((command) => [command.builder.name, command.execute]),
); );