diff --git a/Dockerfile b/Dockerfile index 21d4680..3fb72f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,18 @@ # ── Stage 1: install production dependencies ────────────────────────────────── -# node:20-alpine is used only for npm ci; it never ships to production. FROM node:20-alpine AS deps WORKDIR /app -# package-lock.json* — the wildcard makes the COPY succeed even if the lock -# file is absent, so the image can be built from a clean checkout without any -# local Node installation. COPY package.json package-lock.json* ./ RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi # ── Stage 2: hardened, minimal runtime ──────────────────────────────────────── -# gcr.io/distroless/nodejs20-debian12:nonroot contains only the Node runtime. -# No shell, no package manager, no OS utilities → drastically reduced attack -# surface and near-zero CVEs from OS packages. Runs as uid 65532 (nonroot). FROM gcr.io/distroless/nodejs20-debian12:nonroot WORKDIR /app COPY --from=deps /app/node_modules ./node_modules -# Copy application source COPY server ./server COPY public ./public COPY config ./config @@ -30,10 +22,7 @@ ENV PORT=8080 EXPOSE 8080 -# Health-check: no wget/curl in distroless — use the bundled Node binary -# directly via exec form (no shell needed). HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \ CMD ["/nodejs/bin/node", "server/healthcheck.js"] -# The distroless ENTRYPOINT is already /nodejs/bin/node; CMD is the argument. CMD ["server/index.js"] \ No newline at end of file diff --git a/config/game.settings.json b/config/game.settings.json index 718e915..db6ff1c 100644 --- a/config/game.settings.json +++ b/config/game.settings.json @@ -1,6 +1,6 @@ { "clickCooldownSeconds": 10, - "databaseWipeoutIntervalSeconds": 21600, + "databaseWipeoutIntervalSeconds": 604800, "configReloadIntervalSeconds": 30, "elementWorth": { "common": 0.1, diff --git a/public/src/game.js b/public/src/game.js index 506cb85..c007b66 100644 --- a/public/src/game.js +++ b/public/src/game.js @@ -209,14 +209,19 @@ export function applyConfigPayload(data) { } export function updateResetCountdown() { - if (!GAME_CONFIG.seedPeriodEndsAtUtc) { resetCountEl.textContent = "--:--:--"; return; } + if (!GAME_CONFIG.seedPeriodEndsAtUtc) { resetCountEl.textContent = "--d --:--:--"; return; } const diff = new Date(GAME_CONFIG.seedPeriodEndsAtUtc).getTime() - Date.now(); - if (diff <= 0) { resetCountEl.textContent = "00:00:00"; return; } + if (diff <= 0) { resetCountEl.textContent = "00d 00:00:00"; return; } const s = Math.floor(diff / 1000); + const days = Math.floor(s / 86400); + const hh = Math.floor((s % 86400) / 3600); + const mm = Math.floor((s % 3600) / 60); + const ss = s % 60; resetCountEl.textContent = - String(Math.floor(s / 3600)).padStart(2, "0") + ":" + - String(Math.floor((s % 3600) / 60)).padStart(2, "0") + ":" + - String(s % 60).padStart(2, "0"); + String(days).padStart(2, "0") + "d " + + String(hh).padStart(2, "0") + ":" + + String(mm).padStart(2, "0") + ":" + + String(ss).padStart(2, "0"); }