From 37025c9aca8f730c2dc87f3a630180b8eb9cad68 Mon Sep 17 00:00:00 2001 From: thelinuxguy-ssh Date: Mon, 3 Aug 2026 14:26:33 +0530 Subject: [PATCH] Dockerfile Optimisations, Security Header issue fixed, SQLite database bind mount fix for docker container --- Dockerfile | 25 ++++++++++++++++--------- docker-compose.yml | 2 ++ duskmeter-dev.sqlite-wal | Bin 1594472 -> 2991152 bytes src/hooks.server.ts | 4 ++-- src/lib/server/scheduler.ts | 10 ++++++++++ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index e61e812..f72fa62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,26 +16,33 @@ COPY static/ static/ RUN npm run build -# Production stage — minimal runtime with native module support +# Production stage — minimal runtime FROM node:22-slim -RUN apt-get update && apt-get install -y --no-install-recommends \ - python3 make g++ && rm -rf /var/lib/apt/lists/* - WORKDIR /app COPY package.json package-lock.json ./ -RUN npm ci --omit=dev - -RUN apt-get remove -y python3 make g++ && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* - +COPY --from=builder /app/node_modules node_modules/ COPY --from=builder /app/build build/ RUN mkdir -p /app/data/viirs /app/data/gan +# Handle bind mount creating a directory when SQLite file does not exist. +# Docker bind mounts a non-existent file as a directory, which breaks better-sqlite3. +# This entrypoint script checks and fixes the mismatch before starting. +RUN printf '#!/bin/sh\n\ +DB_PATH="/app/duskmeter.sqlite"\n\ +if [ -d "$DB_PATH" ]; then\n\ + rmdir "$DB_PATH" 2>/dev/null && touch "$DB_PATH"\n\ +fi\n\ +if [ ! -e "$DB_PATH" ]; then\n\ + touch "$DB_PATH"\n\ +fi\n\ +exec node build/index.js\n' > /entrypoint.sh && chmod +x /entrypoint.sh + ENV NODE_ENV=production ENV PORT=3000 EXPOSE 3000 -CMD ["node", "build"] +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 5158bcb..5b00624 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,4 +16,6 @@ services: - ./data/viirs:/app/data/viirs - ./data/gan:/app/data/gan - ./duskmeter.sqlite:/app/duskmeter.sqlite + extra_hosts: + - "host.docker.internal:host-gateway" restart: unless-stopped diff --git a/duskmeter-dev.sqlite-wal b/duskmeter-dev.sqlite-wal index d44913afd7bf5c55c8744443ba9454282e2789a8..2e78c0a2442dc7bcfe3e6ff793822f227372be37 100644 GIT binary patch delta 37137 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 delta 58 zcmdmRVjJU&6^RWkj4ezp%q=V}tSxLU>@6HEoGn}}+$}sUye)h!{4D}4f-OQV!Yv{# NqAg-A;#(v#LI7hk6Bz&i diff --git a/src/hooks.server.ts b/src/hooks.server.ts index eae2eb0..8310b17 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -100,8 +100,8 @@ export const handle: Handle = async ({ event, resolve }) => { const csp = [ "default-src 'self'", - "script-src 'self'", - "style-src 'self' 'unsafe-inline'", + "script-src 'self' 'unsafe-inline'", + "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", "img-src 'self' data: https:", "font-src 'self' https://fonts.gstatic.com", "connect-src 'self' https://fonts.googleapis.com", diff --git a/src/lib/server/scheduler.ts b/src/lib/server/scheduler.ts index 57e4b89..267c12b 100644 --- a/src/lib/server/scheduler.ts +++ b/src/lib/server/scheduler.ts @@ -4,6 +4,7 @@ import db from "./db"; import { logger } from "./logger"; let timer: NodeJS.Timeout | null = null; +let currentLockId: string | null = null; const LOCK_KEY = "scheduler_lock"; const LOCK_TTL_MS = 30 * 60 * 1000; @@ -60,6 +61,7 @@ async function runScheduledTasks() { logger.info("scheduler", "skipped: lock held by another instance"); return; } + currentLockId = lockId; logger.info("scheduler", "running assessment + alert check", { instanceId: lockId }); try { @@ -70,5 +72,13 @@ async function runScheduledTasks() { logger.error("scheduler", e, { instanceId: lockId }); } finally { releaseLock(lockId); + currentLockId = null; } } + +process.on("sveltekit:shutdown", () => { + if (currentLockId) { + releaseLock(currentLockId); + currentLockId = null; + } +});