File size: 6,640 Bytes
68f7925
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# 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"]