Files
bookorbit/Dockerfile
T
neon 97cd03325f feat(reader): open DjVu documents in the web reader
Scanned books are commonly distributed as DjVu, which the scanner only stored as a supplement.
Decode pages on the server with DjVuLibre so DjVu imports with a cover and page count, opens in
the image reader with its embedded OCR text, and can be the preferred format of an ordinary email
recipient. Migration 0113 widens the recipient format check to allow it.

Decoding runs on validated private snapshots behind a bounded, reader-first queue, and
revalidation and cached pages are answered without a decoder. Released iOS builds cannot open
DjVu, so series continuation offers it only to callers sending includeDjvu=true, and the
dashboard and notebook read file never name it. The first full scan after upgrading promotes
stored .djvu supplements to content.
2026-10-14 21:37:41 +00:00

94 lines
3.9 KiB
Docker

ARG NODE_IMAGE=node:26.11.1-alpine3.23@sha256:f1394b3ffc021bc3f4aaf363e70a8d9667f7a9a63538bc824d7cc583129fffad
FROM ${NODE_IMAGE} AS base
RUN npm install -g pnpm@11.22.0
# Stage 1: Build client
FROM base AS client-builder
WORKDIR /app
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY patches/ ./patches/
COPY packages/types/package.json ./packages/types/
COPY client/package.json ./client/
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --filter client... --frozen-lockfile
COPY packages/ ./packages/
COPY client/ ./client/
# pnpm 11 defaults verifyDepsBeforeRun to "install", so running a script
# re-installs first. Each stage installed its own filtered subset with a frozen
# lockfile two steps up, and no stage carries the whole workspace, so that
# re-install is both redundant and wrong: it resolves against a partial
# workspace. In the server stage it is fatal, because client/ is absent and the
# @embedpdf patches then look unused.
RUN pnpm --config.verify-deps-before-run=false --filter client run build-only
# Stage 2: Build server + create deploy bundle
FROM base AS server-builder
WORKDIR /app
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY patches/ ./patches/
COPY packages/types/package.json ./packages/types/
COPY server/package.json ./server/
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --filter server... --frozen-lockfile
COPY packages/ ./packages/
COPY server/ ./server/
RUN pnpm --config.verify-deps-before-run=false --filter server run build
# pnpm deploy prunes to prod deps; dist/ is gitignored so copy it in after.
RUN pnpm --config.allow-unused-patches=true --filter server deploy --prod --legacy /deploy
RUN cp -r /app/server/dist /deploy/dist
RUN mkdir -p /deploy/migrations && cp -r /app/server/src/db/migrations/. /deploy/migrations/
# Stage 3: Runtime image
FROM ${NODE_IMAGE} AS runtime
WORKDIR /app
ARG APP_VERSION=dev
ENV APP_VERSION=${APP_VERSION}
ENV KOBO_CLOUDSCRAPER_PYTHON=/opt/bookorbit-python/bin/python
ENV KOREADER_PLUGIN_PATH=/app/koreader-plugin/bookorbit.koplugin
COPY server/requirements/kobo-cloudscraper.txt /tmp/kobo-cloudscraper-requirements.txt
# pip is build-only here. Leaving it installed also leaves pip/_vendor/vendor.txt,
# which Trivy reads as installed msgpack and setuptools and fails the image scan on.
RUN apk upgrade --no-cache && \
apk add --no-cache poppler-utils djvulibre util-linux su-exec ffmpeg python3 py3-pip tini tzdata && \
python3 -m venv /opt/bookorbit-python && \
/opt/bookorbit-python/bin/python -m pip install --no-cache-dir -r /tmp/kobo-cloudscraper-requirements.txt && \
/opt/bookorbit-python/bin/python -m pip uninstall -y pip && \
apk del py3-pip && \
rm -f /tmp/kobo-cloudscraper-requirements.txt && \
rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx
ENV NODE_ENV=production
ENV PORT=3000
COPY --from=server-builder --chown=node:node /deploy ./
COPY --from=client-builder --chown=node:node /app/client/dist ./public
COPY --from=server-builder --chown=node:node /app/server/entrypoint.sh /app/server/file-env.sh ./
COPY --chown=node:node LICENSE NOTICE ADDITIONAL_TERMS.md ./
COPY --chown=node:node server/bin/kepubify/ ./bin/kepubify/
COPY --chown=node:node koreader-plugin/bookorbit.koplugin/ ./koreader-plugin/bookorbit.koplugin/
RUN sed -i 's/\r$//' /app/entrypoint.sh /app/file-env.sh && chmod +x /app/entrypoint.sh /app/bin/kepubify/* && mkdir -p /books /data/covers /data/book-bucket /tmp && chown -R node:node /data /tmp
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD host="$(printf '%s' "${HOST:-}" | tr -d '[:space:]')"; \
case "$host" in \
''|0.0.0.0) host=127.0.0.1 ;; \
::) host='[::1]' ;; \
*:*) host="[$host]" ;; \
esac; \
wget -q -T 4 -O /dev/null "http://${host}:${PORT:-3000}/api/v1/health"
ENTRYPOINT ["/sbin/tini", "-s", "--"]
CMD ["sh", "/app/entrypoint.sh"]