Build & Deploy¶
Build / scripts¶
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server.js",
"dev": "nodemon server.js"
}
}
Both start and dev invoke nodemon. As elsewhere in the platform, production presumably overrides this via PM2 node server.js (verify with the ops owner).
Dockerfile (multi-stage, optimised)¶
A well-crafted multi-stage Dockerfile is committed. Highlights:
# Stage 1: Builder
FROM node:20.19.2-slim AS builder
ENV NODE_ENV=production \
YARN_CACHE_FOLDER=/tmp/.yarn-cache \
YARN_NETWORK_CONCURRENCY=8
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=true --prefer-offline --link-duplicates && \
find node_modules -type d \( -name "test*" -o -name "example*" -o -name "docs" -o -name ".git" \) -prune -exec rm -rf {} + && \
find node_modules -type f \( -name "*.md" -o -name "*.ts" -not -path "*/typescript/*" -o -name "LICENSE*" -o -name "CHANGELOG*" -o -name "*.o" \) -delete && \
yarn cache clean
# Stage 2: Runtime
FROM node:20.19.2-slim
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx libnss3 libexpat1 fontconfig && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY .env conf.js favicon.ico ./
COPY nginx.conf /etc/nginx/nginx.conf
COPY *.js ./
COPY routes/ ./routes/
COPY modules/ ./modules/
COPY helper/ ./helper/
COPY actions/ ./actions/
COPY conf/ ./conf/
RUN echo '#!/bin/bash\nnginx -g "daemon off;" &\nnode server.js' > /entrypoint.sh && \
chmod +x /entrypoint.sh
EXPOSE 80 8080
CMD ["/entrypoint.sh"]
Notable:
- Two-stage: builder strips test files / docs / LICENSE / CHANGELOG /
.ofiles fromnode_modulesto slim the runtime image - Runtime libs for Puppeteer:
libnss3,libexpat1,fontconfigare present — needed for headless Chrome - nginx in same container: not a sidecar; nginx is launched alongside node via the entrypoint script. nginx + node share the container's network and file system.
.envis COPY'd into the image: this means the.envfile must exist in the build context and is baked into the image. Anyone who pulls the image can read the secrets. This is a finding — see security.md. Better: pass.envas a runtime mount (docker run -v), or use Docker secrets / env vars on the orchestrator.
nginx.conf¶
In the repo. Not inspected in detail in this audit; should be audited for:
- Reverse-proxy target (likely
127.0.0.1:5002or similar) - HSTS / X-Frame-Options / Referrer-Policy / X-Content-Type-Options headers
- Body-size limit
- Suppressed server header
push.sh¶
A manual SSH-deploy script. Contents not inspected; typical pattern is something like:
No CI¶
- No
Jenkinsfile - No
.github/workflows/ - Deploys are presumably manual via
push.shorgit pull+pm2 restart
This is a gap. someli-api has both Jenkins and GitHub Actions; designer-api has neither in repo.
PM2 management¶
No ecosystem.config.js. With 57 jobs + 6 bots + the main server, the deploy box must somehow pm2 start each one. Either:
- The team does this manually on first deploy and PM2 saves the list via
pm2 save/pm2 startup - There's an external ops repo with PM2 manifests
- Everything runs inside
server.js's process via therequire()of the bots (which also kicks off their crons) — would mean PM2 only manages one process
Audit recommendation: commit an ecosystem.config.js. If everything is in one process, document it; if there are many processes, declare them.
Health checks¶
No /health endpoint. nginx may have its own health-check that just verifies the upstream responds. Verify.
Rollback¶
- Docker:
docker run <previous-tag>(if tags are kept) - Bare-metal:
git checkout <previous-sha> && pm2 restart
Neither is documented in repo.
Recommended next steps¶
| ID | Recommendation | Effort |
|---|---|---|
| D-1 | Remove .env COPY from Dockerfile; use runtime mount or orchestrator env vars |
Small |
| D-2 | Commit a CI workflow (.github/workflows/dev-designer-api-deploy.yml modelled after someli-api's) |
Small |
| D-3 | Commit ecosystem.config.js with all processes declared |
Small |
| D-4 | Add a /health endpoint |
Trivial |
| D-5 | Audit nginx.conf for security headers |
Small |
| D-6 | Document the rollback runbook | Trivial |
| D-7 | Tag Docker images with the git SHA so rollback is well-defined | Small |