-
Notifications
You must be signed in to change notification settings - Fork 0
Build frontend dist in Docker image for production deployment #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ChrisAdamsdevelopment
merged 1 commit into
main
from
codex/update-dockerfile-for-frontend-dist-build
May 4, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,42 @@ | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| # ───────────────────────────────────────────────────────────────────────────── | ||
| # SpectraCleanse AI – Backend Dockerfile | ||
| # Base: node:18-alpine | Exposes port 3001 | ||
| # SpectraCleanse AI – Production Dockerfile (builds frontend + backend runtime) | ||
| # ───────────────────────────────────────────────────────────────────────────── | ||
|
|
||
| FROM node:18-alpine | ||
| FROM node:18-bookworm-slim AS builder | ||
| WORKDIR /app | ||
|
|
||
| # ExifTool requires Perl, which is not included in Alpine by default | ||
| RUN apk add --no-cache perl | ||
| COPY package.json package-lock.json ./ | ||
| RUN npm ci | ||
|
|
||
| # Create a non-root user to run the process | ||
| RUN addgroup -S appgroup && adduser -S appuser -G appgroup | ||
| COPY . . | ||
| RUN npm run build | ||
|
|
||
| FROM node:18-bookworm-slim AS runtime | ||
| ENV NODE_ENV=production | ||
| WORKDIR /app | ||
|
|
||
| # Install dependencies first so Docker can cache this layer | ||
| COPY package*.json ./ | ||
| RUN npm ci --omit=dev | ||
| # exiftool-vendored requires Perl at runtime | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends perl \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Create non-root user | ||
| RUN groupadd --system appgroup && useradd --system --gid appgroup appuser | ||
|
|
||
| COPY package.json package-lock.json ./ | ||
| RUN npm ci --omit=dev \ | ||
| && npm cache clean --force | ||
|
|
||
| # Copy application source | ||
| COPY server.js ./ | ||
| COPY --from=builder /app/dist ./dist | ||
|
|
||
| # Runtime uploads directory (ephemeral; processed files are deleted after download) | ||
| # Persistent data directory for SQLite – mount a volume here in production | ||
| RUN mkdir -p uploads /data && chown -R appuser:appgroup /app uploads /data | ||
| # Runtime directories (uploads ephemeral; /data intended for SQLite volume mounts) | ||
| RUN mkdir -p /app/uploads /data \ | ||
| && chown -R appuser:appgroup /app /data | ||
|
|
||
| # Drop to non-root for all subsequent instructions and at runtime | ||
| USER appuser | ||
|
|
||
| EXPOSE 3001 | ||
|
|
||
| CMD ["node", "server.js"] | ||
| CMD ["npm", "start"] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -45,3 +45,52 @@ The source code is available at [github.com/ChrisAdamsdevelopment/SpectraCleanse | |||||
| ## Contact | ||||||
|
|
||||||
| Questions, partnerships, or enterprise enquiries: [hello@spectracleanse.com](mailto:hello@spectracleanse.com) | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Docker production deployment | ||||||
|
|
||||||
| This repository includes a multi-stage `Dockerfile` that builds the frontend and packages `dist/` into the final runtime image so `server.js` can serve the SPA in production. | ||||||
|
|
||||||
| ### Build image | ||||||
|
|
||||||
| ```bash | ||||||
| docker build -t spectracleanseai:latest . | ||||||
| ``` | ||||||
|
|
||||||
| ### Run container | ||||||
|
|
||||||
| ```bash | ||||||
| docker run --rm -p 3001:3001 \ | ||||||
| -e NODE_ENV=production \ | ||||||
| -e JWT_SECRET=your_jwt_secret \ | ||||||
| -e STRIPE_SECRET_KEY=sk_live_xxx \ | ||||||
| -e STRIPE_WEBHOOK_SECRET=whsec_xxx \ | ||||||
| -e STRIPE_CREATOR_PRICE_ID=price_xxx \ | ||||||
| -e STRIPE_STUDIO_PRICE_ID=price_xxx \ | ||||||
| -e GEMINI_API_KEY=your_gemini_api_key \ | ||||||
| -e FRONTEND_URL=https://your-frontend-domain.example \ | ||||||
| -e DB_PATH=/data/spectra.db \ | ||||||
| -v spectracleanse_data:/data \ | ||||||
| spectracleanseai:latest | ||||||
| ``` | ||||||
|
|
||||||
| ### Required production environment variables | ||||||
|
|
||||||
| - `NODE_ENV=production` | ||||||
| - `JWT_SECRET` | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 suggestion (security): Clarify that Add a brief note to this bullet that
Suggested change
|
||||||
| - `STRIPE_SECRET_KEY` | ||||||
| - `STRIPE_WEBHOOK_SECRET` | ||||||
| - `STRIPE_CREATOR_PRICE_ID` | ||||||
| - `STRIPE_STUDIO_PRICE_ID` | ||||||
| - `GEMINI_API_KEY` | ||||||
| - `FRONTEND_URL` | ||||||
| - `DB_PATH` | ||||||
| - `REDIS_URL` (only if your deployment still uses Redis externally) | ||||||
|
|
||||||
| ### Stripe vs local mock checkout | ||||||
|
|
||||||
| - Local development may use `ENABLE_MOCK_CHECKOUT=true` when Stripe variables are not set. | ||||||
| - Production must use real Stripe configuration; do not rely on mock checkout in production. | ||||||
|
|
||||||
| Never commit real secrets to source control. | ||||||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (performance): Consider reusing node_modules from the builder stage to avoid a second full npm install
Dependencies are currently installed twice (
npm ciin the builder andnpm ci --omit=devin the runtime). To reduce build time and network usage, you could copynode_modulesfrom the builder into the runtime image and then runnpm prune --production(or equivalent) there, preserving the multi-stage setup without a second full install.Suggested implementation:
To fully implement reusing
node_modulesfrom the builder in the runtime image, you should also:In the
runtimestage (theFROM node:18-bookworm-slim AS runtimesection), replace the secondnpm ci --omit=devwith copyingnode_modulesfrom the builder and pruning dev dependencies, e.g.:Ensure you copy the built artifacts from the builder into the runtime image (if not already present), for example:
COPY --from=builder /app/dist ./distKeep the existing Perl installation and non-root user setup in the runtime stage as-is, so that
exiftool-vendoredcontinues to work and the container still runs asappuser.Adjust paths like
/app/distor the finalCMDto match your existing build output and startup command.