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 d44913a..2e78c0a 100644 Binary files a/duskmeter-dev.sqlite-wal and b/duskmeter-dev.sqlite-wal differ 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; + } +});