diff --git a/.dockerignore b/.dockerignore index 26ef3e9..7fefa66 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,5 @@ +.DS_Store + node_modules .git .github diff --git a/Dockerfile b/Dockerfile index a767ced..9339da6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,30 @@ -FROM node:25-alpine +FROM node:lts-alpine + +# Use a non-root working directory WORKDIR /app -# Install dependencies -COPY package*.json ./ -RUN npm install --production +# Copy package files first to leverage Docker layer caching +COPY package.json package-lock.json ./ -# Copy application code +# Install production dependencies deterministically +# --omit=dev keeps devDependencies out of the final install +RUN npm ci --omit=dev --no-audit --no-fund + +# Copy the rest of the application COPY . . -# Create directory for database with open permissions -RUN mkdir -p /app/data && chmod 777 /app/data +# Install tiny helper to drop privileges at container start +# and keep image small (no cache) +RUN apk add --no-cache su-exec -# Set environment variable for database path +# Runtime entrypoint ensures the data dir ownership and drops privileges +COPY scripts/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENV NODE_ENV=production ENV DB_PATH=/app/data/data.db EXPOSE 3000 +ENTRYPOINT ["/entrypoint.sh"] CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml index 5237a2e..6c0ede9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,15 +3,14 @@ services: build: context: . dockerfile: Dockerfile - platforms: - - linux/amd64 - - linux/arm64 image: registry.cn-beijing.aliyuncs.com/licsber/timeline:latest ports: - "3000:3000" environment: + - NODE_ENV=production - PORT=3000 - JWT_SECRET=${JWT_SECRET:-change-this-secret-key-in-production} + - DB_PATH=/app/data/data.db volumes: - ./data:/app/data restart: unless-stopped diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100644 index 0000000..8d3cb6a --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +# Ensure the data directory exists and is writable by the 'node' user. +# This runs as root inside the container during startup, fixes ownership for +# both named volumes and bind mounts, then drops privileges to the 'node' user. + +DATA_DIR=/app/data + +mkdir -p "$DATA_DIR" +chown -R node:node "$DATA_DIR" || true +chmod 755 "$DATA_DIR" || true + +# Exec the requested command as the 'node' user +exec su-exec node "$@" diff --git a/scripts/init-data-dir.sh b/scripts/init-data-dir.sh deleted file mode 100644 index 3678b5a..0000000 --- a/scripts/init-data-dir.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# Create data directory if it doesn't exist -mkdir -p /app/data - -# Try to create the database file if it doesn't exist -touch /app/data/data.db 2>/dev/null || true - -# Set permissions for the data directory and database file -chmod 777 /app/data 2>/dev/null || true -chmod 666 /app/data/data.db 2>/dev/null || true - -echo "Data directory initialized with open permissions" - -# Execute the main command -exec "$@" diff --git a/server/config.js b/server/config.js index 9b51707..22ddf14 100644 --- a/server/config.js +++ b/server/config.js @@ -2,7 +2,7 @@ const path = require('path'); const PORT = process.env.PORT || 3000; const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; -const DB_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'data.db'); +const DB_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'data.db'); module.exports = { PORT, diff --git a/server/database.js b/server/database.js index 0eb675b..19f0f14 100644 --- a/server/database.js +++ b/server/database.js @@ -3,7 +3,7 @@ const { DB_PATH } = require('./config'); const fs = require('fs'); const path = require('path'); -// Ensure the directory exists +// Ensure the directory exists with proper permissions const dbDir = path.dirname(DB_PATH); if (!fs.existsSync(dbDir)) { try {