FROM python:3.12-slim

WORKDIR /app

# tzdata/fonts in their own layer, kept separate from the much larger
# Node.js/npm layers below -- see those layers' own comments for why
# they're split up the way they are. tzdata: python:3.12-slim doesn't
# include it by default, so the zoneinfo database backing the web UI's
# "Timezone" setting (used by "Quiet hours") would have no named zones
# to resolve without this -- ZoneInfo() would raise for anything other
# than "UTC". fontconfig/fonts-dejavu-core: whiteboard mode's
# render-service/ (own README there) needs something to render
# whiteboard text with.
RUN apt-get update && apt-get install -y --no-install-recommends \
    tzdata fontconfig fonts-dejavu-core \
    && rm -rf /var/lib/apt/lists/*

# Node.js: whiteboard frame mode's render-service/ runs as a second
# process in this same container rather than a separate compose service
# -- it's a lightweight, stateless, localhost-only sidecar with nothing
# worth independently scaling or restarting. NodeSource's setup script is
# used instead of Debian bookworm's own apt Node package, which is both
# older than jsdom's minimum (20.19+) and inconsistently available.
# curl/gnupg are only needed to add and fetch NodeSource's repo -- purged
# again in this same RUN (not a later one; Docker layers are immutable,
# so removing them in a *different* instruction wouldn't shrink this
# one's actual pushed size) so their bytes don't end up in the image at
# all, only nodejs's.
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates gnupg \
    && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
    && apt-get install -y --no-install-recommends nodejs \
    && apt-get purge -y --auto-remove curl gnupg \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# render-service/'s dependencies installed as several separate layers
# rather than one `npm install` covering all of them -- a from-scratch
# push of this image once hit Cloudflare's payload-size limit on a
# single blob/layer upload (the registry sits behind it), and splitting
# a big layer into several smaller ones is the direct fix for exactly
# that failure mode, independent of anything about the registry itself.
# --no-save: package.json already fully declares these (with the exact
# same version pins used here) as the single source of truth for what
# this service depends on -- these calls are just about *when* each one
# gets installed for layer-size reasons, not re-deciding what's needed.
COPY render-service/package.json ./render-service/package.json
WORKDIR /app/render-service
RUN npm install --omit=dev --no-save express@^5.2.1 && npm cache clean --force
RUN npm install --omit=dev --no-save jsdom@^29.1.1 && npm cache clean --force
RUN npm install --omit=dev --no-save @excalidraw/utils@0.1.3-test32 && npm cache clean --force
RUN npm install --omit=dev --no-save @resvg/resvg-js@2.6.2 && npm cache clean --force
WORKDIR /app

COPY render-service/server.js ./render-service/server.js
COPY app ./app
COPY start.sh .
RUN chmod +x start.sh

EXPOSE 8420

CMD ["./start.sh"]
