# syntax=docker.io/docker/dockerfile:1 # Force cache invalidation for HuggingFace Spaces ARG CACHEBUST=1 FROM node:slim AS base # Install Python and build tools for native dependencies # Install Chromium and dependencies for Puppeteer RUN apt-get update && apt-get install -y \ python3 make g++ \ chromium \ fonts-noto-cjk \ fonts-liberation \ fonts-freefont-ttf \ ca-certificates \ libnss3 \ libatk-bridge2.0-0 \ libdrm2 \ libxcomposite1 \ libxdamage1 \ libxfixes3 \ libxrandr2 \ libgbm1 \ libxkbcommon0 \ libasound2 \ libatspi2.0-0 \ libgtk-3-0 \ libglib2.0-0 \ libxss1 \ libgconf-2-4 \ && rm -rf /var/lib/apt/lists/* # Set Puppeteer to use installed Chromium ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium # Install dependencies only when needed FROM base AS deps WORKDIR /app # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci --include=optional; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi # Install the correct platform-specific packages based on architecture RUN if [ "$(uname -m)" = "x86_64" ]; then \ npm install lightningcss-linux-x64-gnu@1.29.1 --no-save; \ elif [ "$(uname -m)" = "aarch64" ]; then \ npm install lightningcss-linux-arm64-gnu@1.29.1 --no-save; \ fi && \ npm rebuild @tailwindcss/oxide --update-binary # Rebuild the source code only when needed FROM base AS builder WORKDIR /app # Clear all cache to avoid build issues RUN rm -rf .next node_modules/.cache COPY --from=deps /app/node_modules ./node_modules COPY . . # Rebuild native dependencies for the builder stage # Rebuild sharp and other native modules for the platform RUN if [ "$(uname -m)" = "x86_64" ]; then \ npm install lightningcss-linux-x64-gnu@1.29.1 --no-save; \ elif [ "$(uname -m)" = "aarch64" ]; then \ npm install lightningcss-linux-arm64-gnu@1.29.1 --no-save; \ fi && \ npm rebuild sharp && \ npm rebuild @tailwindcss/oxide --update-binary # Clean up build artifacts RUN rm -rf .next/cache node_modules/.cache # Build arguments for API keys (passed from HuggingFace Spaces environment) ARG OPENAI_TEMPLATE_AI_DEV ARG OPENAI_GENERATE_FV_AI ARG CLAUDE_TEMPLATE_AI_DEV ARG ANTHROPIC_API_KEY ARG GOOGLE_API_KEY ARG NEXT_PUBLIC_APP_ENV ARG OKTA_OAUTH2_ISSUER ARG OKTA_OAUTH2_CLIENT_ID ARG OKTA_OAUTH2_CLIENT_SECRET ARG SECRET # Set environment variables for build time ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_PUBLIC_APP_ENV=$NEXT_PUBLIC_APP_ENV ENV OPENAI_TEMPLATE_AI_DEV=$OPENAI_TEMPLATE_AI_DEV ENV OPENAI_GENERATE_FV_AI=$OPENAI_GENERATE_FV_AI ENV CLAUDE_TEMPLATE_AI_DEV=$CLAUDE_TEMPLATE_AI_DEV ENV ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY ENV GOOGLE_API_KEY=$GOOGLE_API_KEY ENV OKTA_OAUTH2_ISSUER=$OKTA_OAUTH2_ISSUER ENV OKTA_OAUTH2_CLIENT_ID=$OKTA_OAUTH2_CLIENT_ID ENV OKTA_OAUTH2_CLIENT_SECRET=$OKTA_OAUTH2_CLIENT_SECRET ENV SECRET=$SECRET # Set memory limit for build process ENV NODE_OPTIONS="--max-old-space-size=2048" RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi # Remove build cache to reduce image size (keep only necessary runtime files) RUN rm -rf .next/cache/webpack .next/cache/swc node_modules/.cache # Production image, copy all the files and run next FROM base AS runner WORKDIR /app ENV NODE_ENV=production # Disable Next.js telemetry and set cache to writable location ENV NEXT_TELEMETRY_DISABLED=1 ENV TMPDIR=/tmp # Runtime environment variables for API keys ARG OPENAI_TEMPLATE_AI_DEV ARG OPENAI_GENERATE_FV_AI ARG CLAUDE_TEMPLATE_AI_DEV ARG ANTHROPIC_API_KEY ARG GOOGLE_API_KEY ARG NEXT_PUBLIC_APP_ENV ARG OKTA_OAUTH2_ISSUER ARG OKTA_OAUTH2_CLIENT_ID ARG OKTA_OAUTH2_CLIENT_SECRET ARG SECRET ENV NEXT_PUBLIC_APP_ENV=$NEXT_PUBLIC_APP_ENV ENV OPENAI_TEMPLATE_AI_DEV=$OPENAI_TEMPLATE_AI_DEV ENV OPENAI_GENERATE_FV_AI=$OPENAI_GENERATE_FV_AI ENV CLAUDE_TEMPLATE_AI_DEV=$CLAUDE_TEMPLATE_AI_DEV ENV ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY ENV GOOGLE_API_KEY=$GOOGLE_API_KEY ENV OKTA_OAUTH2_ISSUER=$OKTA_OAUTH2_ISSUER ENV OKTA_OAUTH2_CLIENT_ID=$OKTA_OAUTH2_CLIENT_ID ENV OKTA_OAUTH2_CLIENT_SECRET=$OKTA_OAUTH2_CLIENT_SECRET ENV SECRET=$SECRET RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs --home /home/nextjs # Copy node_modules for runtime (including Puppeteer) COPY --from=builder /app/node_modules ./node_modules RUN chown -R nextjs:nodejs ./node_modules # Ensure nextjs user has access to home directory RUN chown -R nextjs:nodejs /home/nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Copy .next directory but exclude cache to avoid permission issues COPY --from=builder --chown=nextjs:nodejs /app/.next/server ./.next/server COPY --from=builder --chown=nextjs:nodejs /app/.next/required-server-files.json ./.next/required-server-files.json COPY --from=builder --chown=nextjs:nodejs /app/.next/routes-manifest.json ./.next/routes-manifest.json COPY --from=builder --chown=nextjs:nodejs /app/.next/build-manifest.json ./.next/build-manifest.json COPY --from=builder --chown=nextjs:nodejs /app/.next/package.json ./.next/package.json # Copy optional manifest files (may not exist in all builds) RUN --mount=type=bind,from=builder,source=/app/.next,target=/tmp/source \ if [ -f /tmp/source/prerender-manifest.json ]; then cp /tmp/source/prerender-manifest.json ./.next/; fi && \ if [ -f /tmp/source/react-loadable-manifest.json ]; then cp /tmp/source/react-loadable-manifest.json ./.next/; fi && \ chown -R nextjs:nodejs ./.next/ # Create cache directories and ensure full permissions (combining both approaches) RUN mkdir -p /tmp/.next/cache/images && \ mkdir -p /app/.next/cache/images && \ chmod -R 777 /tmp && \ chmod -R 755 /app/.next && \ chmod -R 777 /app/.next/cache/images && \ chown -R nextjs:nodejs /tmp/.next && \ chown -R nextjs:nodejs /app/.next USER nextjs EXPOSE 7860 ENV PORT=7860 ENV HOSTNAME="0.0.0.0" # Optimize for HuggingFace free tier ENV NODE_OPTIONS="--max-old-space-size=1024" CMD ["node", "server.js"]