Implemented all 5 critical efficiency improvements to optimize performance, reduce resource usage, and improve scalability. ## 1. Database Indexes - Added indexes on sermon_notes foreign keys (user_id, sermon_id) - Added composite index on sermons (archived, date DESC) - Added indexes on frequently queried columns across all tables - Impact: Faster queries as data grows, better JOIN performance ## 2. Eliminated N+1 Query Pattern - Reduced 2 API calls to 1 on home page load - Changed from separate active/archived fetches to single call - Filter archived sermons client-side using computed properties - Impact: 50% reduction in HTTP requests per page load ## 3. Scheduled Database Cleanup - Extended existing plugin to clean expired sessions hourly - Added cleanup for expired rate limits every hour - Added cleanup for expired password reset codes every hour - Sermon cleanup continues to run daily based on retention policy - Impact: Prevents database table growth, better performance ## 4. Multi-stage Docker Build - Implemented 3-stage build: deps -> builder -> runtime - Separated build-time and runtime dependencies - Added non-root user (nuxt:nodejs) for security - Integrated dumb-init for proper signal handling - Added health check endpoint at /api/health - Impact: Smaller image size, faster deployments, better security ## 5. HTTP Caching - Static assets: 1 year cache (immutable) - Logos/images: 1 year cache (immutable) - API routes: No cache (always fresh) - HTML pages: 10 minute cache with revalidation - Impact: Reduced bandwidth, faster page loads, less server load All optimizations follow best practices and maintain backward compatibility with existing functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
93 lines
2.4 KiB
Docker
93 lines
2.4 KiB
Docker
# ======================
|
|
# Stage 1: Dependencies
|
|
# ======================
|
|
FROM node:20-alpine AS deps
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy package files
|
|
COPY package*.json ./
|
|
|
|
# Install dependencies (including devDependencies for build)
|
|
RUN npm ci
|
|
|
|
# ======================
|
|
# Stage 2: Builder
|
|
# ======================
|
|
FROM node:20-alpine AS builder
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy dependencies from deps stage
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy application source
|
|
COPY . .
|
|
|
|
# Accept build arguments for Nuxt build-time configuration
|
|
ARG SITE_URL=https://church.example.com
|
|
ARG EMAIL_HOST=smtp.example.com
|
|
ARG EMAIL_PORT=587
|
|
ARG EMAIL_USER=noreply@example.com
|
|
ARG EMAIL_PASSWORD=your-email-password
|
|
ARG EMAIL_FROM=New Life Christian Church <noreply@example.com>
|
|
|
|
# Set environment variables for build
|
|
ENV SITE_URL=$SITE_URL
|
|
ENV EMAIL_HOST=$EMAIL_HOST
|
|
ENV EMAIL_PORT=$EMAIL_PORT
|
|
ENV EMAIL_USER=$EMAIL_USER
|
|
ENV EMAIL_PASSWORD=$EMAIL_PASSWORD
|
|
ENV EMAIL_FROM=$EMAIL_FROM
|
|
|
|
# Build the application
|
|
RUN npm run build
|
|
|
|
# Remove development dependencies after build
|
|
RUN npm prune --production
|
|
|
|
# ======================
|
|
# Stage 3: Runtime
|
|
# ======================
|
|
FROM node:20-alpine AS runtime
|
|
|
|
WORKDIR /app
|
|
|
|
# Install dumb-init for proper signal handling
|
|
RUN apk add --no-cache dumb-init
|
|
|
|
# Create non-root user for security
|
|
RUN addgroup -g 1001 -S nodejs && \
|
|
adduser -S nuxt -u 1001
|
|
|
|
# Copy only production dependencies and built output
|
|
COPY --from=builder --chown=nuxt:nodejs /app/.output ./.output
|
|
COPY --from=builder --chown=nuxt:nodejs /app/node_modules ./node_modules
|
|
COPY --from=builder --chown=nuxt:nodejs /app/package.json ./package.json
|
|
|
|
# Create data directory for SQLite database with proper permissions
|
|
RUN mkdir -p /app/data && chown -R nuxt:nodejs /app/data
|
|
|
|
# Switch to non-root user
|
|
USER nuxt
|
|
|
|
# Expose port
|
|
EXPOSE 3000
|
|
|
|
# Set runtime environment variables
|
|
ENV NODE_ENV=production
|
|
ENV NUXT_HOST=0.0.0.0
|
|
ENV NUXT_PORT=3000
|
|
|
|
# Security: AUTH_SECRET and admin credentials are auto-generated on first launch
|
|
# They are stored in the database and logged once to container logs
|
|
# Use: docker logs <container-name> | grep "ADMIN CREDENTIALS" to retrieve them
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
|
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
|
|
|
|
# Use dumb-init to properly handle signals
|
|
ENTRYPOINT ["dumb-init", "--"]
|
|
CMD ["node", ".output/server/index.mjs"]
|