Ctrl K
ComunidadGuías…

Dockerfile

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# openbooks — production image (Next.js standalone + bundled bootstrap).
#
# Build:  docker build -t openbooks .
# Run:    use compose.yaml, which executes the privileged bootstrap as a
#         one-shot service before starting web/worker with an RLS-constrained
#         database role.

# --- deps: workspace-aware install ------------------------------------------
FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS deps
WORKDIR /app
COPY package.json package-lock.json ./
COPY schema/package.json schema/
COPY engine/package.json engine/
COPY web/package.json web/
COPY packages/analytics/package.json packages/analytics/
COPY packages/customization/package.json packages/customization/
COPY packages/emails/package.json packages/emails/
COPY packages/forms-core/package.json packages/forms-core/
COPY packages/jobs/package.json packages/jobs/
COPY packages/networking/package.json packages/networking/
COPY packages/office/package.json packages/office/
COPY packages/pdf/package.json packages/pdf/
COPY packages/reports/package.json packages/reports/
COPY packages/ui/package.json packages/ui/
RUN npm ci

# --- build: next standalone + bootstrap bundle -------------------------------
FROM deps AS build
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN cd web && NODE_OPTIONS=--max-old-space-size=12288 npx next build --webpack
RUN npx esbuild scripts/bootstrap.ts \
      --bundle --platform=node --format=esm \
      --external:pg-native \
      --banner:js="import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);" \
      --outfile=/out/bootstrap.mjs
# The background worker (BullMQ consumers + schedulers) as a self-contained
# bundle, so the same image can run either the web server (default CMD) or the
# worker (command override in the compose `worker` service). The bundle source
# is the process composition entry (scripts/worker-entry.ts), which registers
# worker-composed duties such as the automation tick before booting
# the worker. Duty registration stays in composition so the worker module
# keeps its declared dependencies and the engine module graph remains acyclic.
RUN npx esbuild scripts/worker-entry.mts \
      --bundle --platform=node --format=esm --conditions=react-server --tsconfig=web/tsconfig.json \
      --external:pg-native --external:jsdom --external:pdfkit \
      --banner:js="import { createRequire as openbooksCreateRequire } from 'node:module'; const require = openbooksCreateRequire(import.meta.url);" \
      --outfile=/out/worker.mjs
RUN node --check /out/worker.mjs
# Deployment qualification exercises the unified schedule renderer with bundled native font assets.
RUN npx esbuild scripts/verify-native-schedule-pdf.ts \
      --bundle --platform=node --format=esm --external:jsdom --external:pdfkit \
      --banner:js="import { createRequire as openbooksCreateRequire } from 'node:module'; const require = openbooksCreateRequire(import.meta.url);" \
      --outfile=/out/verify-native-schedule-pdf.mjs
# Deterministic master demos are prepared explicitly by installation operators.
# The same source is used by the setup wizard when a master is not yet present.
RUN npx esbuild engine/src/sample-companies/cli.ts \
      --bundle --platform=node --format=esm \
      --external:pg-native --external:jsdom \
      --banner:js="import { createRequire as openbooksCreateRequire } from 'node:module'; const require = openbooksCreateRequire(import.meta.url);" \
      --outfile=/out/sample-companies.mjs
RUN node --check /out/sample-companies.mjs

# --- PDF renderer: verified native browser archive -----------------------------
FROM deps AS pdf-verification
COPY scripts/verify-pdf-browser.mjs ./scripts/
RUN npx esbuild scripts/verify-pdf-browser.mjs \
      --bundle --platform=node --format=esm \
      --banner:js="import { createRequire as openbooksPdfCreateRequire } from 'node:module'; const require = openbooksPdfCreateRequire(import.meta.url);" \
      --outfile=/out/verify-pdf-browser.mjs

FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS pdf-browser
ARG TARGETARCH
WORKDIR /browser-install
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates unzip
COPY scripts/pdf-browser.json scripts/install-pdf-browser.mjs ./
RUN node install-pdf-browser.mjs "$TARGETARCH" /opt/chromium

# --- runtime ------------------------------------------------------------------
FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS runtime-base
WORKDIR /app
ARG OPENBOOKS_VERSION=development
# HTML-authored reports and forms are printed by the shared Chromium renderer.
# Ship the renderer and deterministic multilingual fonts in the production
# image so PDF availability and typography never depend on the host machine.
# qpdf encrypts confidential record PDFs (pay stubs): neither renderer can
# write an encrypted file, so encryption is a post-processing pass. The browser
# uses Google's versioned native releases, checksum-pinned for both supported
# architectures; the fonts stay pinned for deterministic typography.
# The renderer runs sandboxed by default (packages/pdf/src/browser-pool.ts)
# even though this image runs as non-root node: Chrome sandboxes via
# unprivileged user namespaces, which needs no root. Swarm's default seccomp
# profile denies the unshare the sandbox needs, so there the launch falls
# back to --no-sandbox with a warning (or set OPENBOOKS_CHROMIUM_NO_SANDBOX=1
# to skip the doomed first attempt); the child environment is scrubbed of
# secrets either way.
RUN apt-get update \
    # Base-image security rot: the node:24-trixie-slim digest still ships
    # util-linux 2.41-5 (CVE-2026-53615, fixed in 2.41.5-0+deb13u1) and no
    # rebuilt base exists yet. Upgrade only the already-installed packages so
    # OS security fixes land without disturbing the pinned font set below.
    && apt-get upgrade -y --no-install-recommends \
    && apt-get install -y --no-install-recommends \
      libasound2t64 libatk-bridge2.0-0t64 libatk1.0-0t64 libatspi2.0-0t64 \
      libcairo2 libcups2t64 libdbus-1-3 libdrm2 libgbm1 libglib2.0-0t64 \
      libgtk-3-0t64 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 \
      libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 \
      qpdf poppler-utils \
      fonts-liberation=1:2.1.5-3 \
      fonts-noto-core=20201225-2 \
      fonts-noto-cjk=1:20240730+repack1-1 \
      fonts-noto-color-emoji=2.051-0+deb13u1 \
    && rm -rf \
      /var/lib/apt/lists/* \
      /usr/local/lib/node_modules/corepack \
      /usr/local/lib/node_modules/npm \
      /opt/yarn-* \
    && rm -f \
      /usr/local/bin/corepack \
      /usr/local/bin/npm \
      /usr/local/bin/npx \
      /usr/local/bin/pnpm \
      /usr/local/bin/pnpx \
      /usr/local/bin/yarn \
      /usr/local/bin/yarnpkg
ENV NODE_ENV=production \
    OPENBOOKS_VERSION=${OPENBOOKS_VERSION} \
    NEXT_TELEMETRY_DISABLED=1 \
    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \
    HOSTNAME=0.0.0.0 \
    PORT=3000
COPY --from=pdf-browser /opt/chromium /opt/chromium
RUN ln -s /opt/chromium/chrome /usr/bin/chromium
COPY --chown=node:node scripts/pdf-browser.json ./scripts/
COPY --chown=node:node --from=pdf-verification /out/verify-pdf-browser.mjs ./scripts/verify-pdf-browser.mjs
RUN su -s /bin/sh node -c 'node scripts/verify-pdf-browser.mjs'

FROM runtime-base AS runtime
# Standalone output is rooted at the monorepo (outputFileTracingRoot):
# node_modules + web/server.js + web/.next live inside it.
COPY --chown=node:node --from=build /app/web/.next/standalone ./
COPY --chown=node:node --from=build /app/web/.next/static ./web/.next/static
# Standalone output omits public assets: the service worker, offline page and
# installable-app icons are served from here.
COPY --chown=node:node --from=build /app/web/public ./web/public
COPY --chown=node:node --from=build /out/sample-companies.mjs ./scripts/sample-companies.mjs
COPY --chown=node:node --from=build /out/bootstrap.mjs ./scripts/bootstrap.mjs
COPY --chown=node:node --from=build /out/worker.mjs ./scripts/worker.mjs
# Native PDFKit resolves standard font metrics relative to its package directory.
COPY --chown=node:node --from=deps /app/node_modules/pdfkit ./node_modules/pdfkit
COPY --chown=node:node scripts/verify-native-pdf.mjs ./scripts/verify-native-pdf.mjs
COPY --chown=node:node --from=build /out/verify-native-schedule-pdf.mjs ./scripts/verify-native-schedule-pdf.mjs
RUN su -s /bin/sh node -c 'node scripts/verify-native-pdf.mjs && node scripts/verify-native-schedule-pdf.mjs'
# The bootstrap reads migration SQL relative to its own location (/app/scripts → /app).
COPY --chown=node:node schema/migrations ./schema/migrations
RUN set -eu; \
    output=$(node scripts/worker.mjs 2>&1) && { echo "worker unexpectedly started without database credentials" >&2; exit 1; }; \
    printf '%s' "$output" | grep -Fq 'OPENBOOKS_BYPASS_DB_URL must name the dedicated BYPASSRLS login'
RUN NODE_ENV=test OPENBOOKS_DB_URL= node --conditions=react-server --input-type=module \
    -e "const entry = await import('./scripts/worker.mjs'); const transfer = await entry.loadDataTransferWorker(); if (typeof transfer.startDataTransferWorker !== 'function') throw new Error('Transfer worker startup did not load'); console.log('Deferred transfer runtime verified');"

EXPOSE 3000
# Database bootstrap is intentionally not part of this process: the web server
# must never receive migration-owner credentials.
USER node
CMD ["node", "web/server.js"]