- add .dockerignore: without it the build context shipped .env.local (PB_TOKEN + CIPC passwords), node_modules and the full .git history - bake NEXT_PUBLIC_PB_URL / NEXT_PUBLIC_BASE_URL / NEXT_PUBLIC_APP_URL as build args; NEXT_PUBLIC_PB_URL was read by client components but never supplied, so browser-side PocketBase calls would break - docker-compose: same build args + Telegram runtime env - document that agent-browser / PDFD sidecar routes stay on the PC
109 lines
4.9 KiB
Docker
109 lines
4.9 KiB
Docker
# ─────────────────────────────────────────────────────────────────
|
|
# Workflow PocketBase Frontend — Dockerfile (Coolify-ready)
|
|
#
|
|
# Multi-stage build for a small production image:
|
|
# - deps: install everything
|
|
# - builder: compile the Next.js app (writes standalone output)
|
|
# - runner: copy the standalone build + public assets + .next/static
|
|
# onto the alpine image
|
|
#
|
|
# Image runs as non-root user `nextjs` on port 3000.
|
|
#
|
|
# Build context: this folder (where package.json lives).
|
|
# Local: docker build -t workflow-frontend .
|
|
# docker run --rm -p 3000:3000 \
|
|
# -e PB_URL=https://pb.agent.apphub.co.za \
|
|
# -e PB_TOKEN=... \
|
|
# workflow-frontend
|
|
#
|
|
# Coolify: set "Dockerfile" as the build pack, port 3000, and configure
|
|
# Build Arguments (NEXT_PUBLIC_PB_URL, NEXT_PUBLIC_BASE_URL,
|
|
# NEXT_PUBLIC_APP_URL) + Runtime Environment (PB_URL, PB_TOKEN,
|
|
# TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, ONBOARDING_TOKEN_PEPPER).
|
|
# See docker-compose.yml for the same contract in one place.
|
|
#
|
|
# NOTE — routes that shell out to local Windows tooling do NOT work in
|
|
# this Linux image: the CIPC/BizPortal routes drive agent-browser and the
|
|
# BizPortal sidecar, and PDF rendering expects a Windows Chrome or the
|
|
# PDFD_URL sidecar. They stay on the PC; host them separately.
|
|
# ─────────────────────────────────────────────────────────────────
|
|
|
|
# ─── 1. Deps ─────────────────────────────────────────────────────
|
|
FROM node:20-alpine AS deps
|
|
WORKDIR /app
|
|
COPY package.json package-lock.json* ./
|
|
RUN \
|
|
if [ -f package-lock.json ]; then npm ci; \
|
|
else npm install; \
|
|
fi
|
|
|
|
# ─── 2. Builder ──────────────────────────────────────────────────
|
|
FROM node:20-alpine AS builder
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
|
|
# Required at build-time so Next.js bakes them into the client bundle.
|
|
# Next.js INLINES every NEXT_PUBLIC_* at build time — if they are missing
|
|
# here, the browser bundle ships with an empty PocketBase URL and every
|
|
# client-side PB call fails. Set them as Build Arguments in Coolify.
|
|
#
|
|
# NEXT_PUBLIC_PB_URL — read by components/CipcAnnualReturns.tsx and
|
|
# components/DocumentsPanel.tsx
|
|
# NEXT_PUBLIC_BASE_URL — read by app/onboarding/new/page.tsx (link the
|
|
# client sends in the onboarding e-mail)
|
|
# NEXT_PUBLIC_APP_URL — absolute app URL used in appointment e-mails
|
|
#
|
|
# Server-only secrets (PB_URL, PB_TOKEN, TELEGRAM_*, ONBOARDING_TOKEN_PEPPER)
|
|
# are NOT needed here — they are read at runtime, never bundled.
|
|
ARG NEXT_PUBLIC_PB_URL
|
|
ARG NEXT_PUBLIC_BASE_URL
|
|
ARG NEXT_PUBLIC_APP_URL
|
|
ENV NEXT_PUBLIC_PB_URL=${NEXT_PUBLIC_PB_URL} \
|
|
NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL} \
|
|
NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \
|
|
NEXT_TELEMETRY_DISABLED=1
|
|
|
|
RUN npm run build
|
|
|
|
# ─── 3. Runner ───────────────────────────────────────────────────
|
|
FROM node:20-alpine AS runner
|
|
WORKDIR /app
|
|
|
|
# wget is needed by the HEALTHCHECK below (not in node:20-alpine
|
|
# by default — busybox-extra ships it).
|
|
RUN apk add --no-cache wget
|
|
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV PORT=3000
|
|
ENV HOSTNAME=0.0.0.0
|
|
|
|
# Non-root user
|
|
RUN addgroup --system --gid 1001 nodejs && \
|
|
adduser --system --uid 1001 nextjs
|
|
|
|
# Copy the standalone build (Next.js output tracing) + static assets.
|
|
# `public/` is optional (PWA manifest, favicons, robots.txt). Next
|
|
# inlines any source `public/` into the standalone build, so we only
|
|
# need to copy it if it actually exists. Projects without a public/
|
|
# dir (e.g. this one pre-PWA) no-op cleanly.
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
RUN if [ -d ./public ] && [ -n "$(ls -A ./public 2>/dev/null)" ]; then \
|
|
echo "public/ assets found in standalone build, keeping"; \
|
|
else \
|
|
rm -rf ./public 2>/dev/null || true; \
|
|
fi
|
|
|
|
USER nextjs
|
|
EXPOSE 3000
|
|
|
|
# Health check — Coolify / Docker can probe /api/pb/records/list and
|
|
# 401 / 500 is fine here (it means the app booted; PB may be
|
|
# unreachable but the proxy is running). A connection refused means
|
|
# the Next.js server hasn't bound yet.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
CMD wget -qO- http://127.0.0.1:3000/api/pb/records/list?collection=ysterbaard_directors\&perPage=1 >/dev/null || exit 1
|
|
|
|
CMD ["node", "server.js"]
|