# syntax=docker/dockerfile:1.7

# ---- Build stage: compile TypeScript ----
FROM node:20-alpine AS builder
ENV COREPACK_DEFAULT_TO_LATEST=0
RUN corepack enable && corepack prepare pnpm@10.15.1 --activate
RUN apk add --no-cache python3 make g++
WORKDIR /build
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml tsconfig.base.json ./
COPY apps/server/package.json ./apps/server/
COPY apps/web/package.json ./apps/web/
COPY apps/booterm/package.json ./apps/booterm/
RUN pnpm install --frozen-lockfile
COPY apps/booterm ./apps/booterm
RUN pnpm --filter=@boocode/booterm build

# ---- Prod-deps stage: hoisted, native built via npm rebuild ----
# v1.10.2: switched to bookworm-slim (glibc) so node-pty's native .node is
# compiled against the same libc as the runtime stage. A musl-built .node
# won't dlopen in a glibc node binary, so both stages must match.
FROM node:20-bookworm-slim AS proddeps
ENV COREPACK_DEFAULT_TO_LATEST=0
RUN corepack enable && corepack prepare pnpm@10.15.1 --activate
RUN apt-get update && apt-get install -y --no-install-recommends \
    python3 make g++ ca-certificates \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /prod
COPY apps/booterm/package.json ./package.json
RUN pnpm install --prod --config.node-linker=hoisted --config.strict-peer-dependencies=false
# pnpm 10 ignores build scripts; force compile with npm directly.
# node-gyp is bundled with npm in the node:20-bookworm-slim image.
RUN cd node_modules/node-pty && npm run install
# Sanity check — fail the build if the artifact still isn't there
RUN test -f node_modules/node-pty/build/Release/pty.node && echo "pty.node OK" || (echo "pty.node MISSING" && exit 1)

# ---- Runtime ----
# v1.10.2: switched from node:20-alpine (musl) to node:20-bookworm-slim (glibc)
# so glibc-linked binaries from /home/samkintop (Claude Code, opencode, the
# host's nvm node) run inside the container when invoked from the terminal
# pane. Side-effect: su-exec is alpine-only — Debian replacement is gosu.
FROM node:20-bookworm-slim AS runtime
# v1.10.8d: openssh-client added so the terminal can ssh -t samkintop@host
# (matching boolab's pattern) — that's how the in-pane shell gets access to
# host tools (docker, claude, opencode) that don't exist inside the container.
RUN apt-get update && apt-get install -y --no-install-recommends \
    tmux bash gosu ca-certificates procps openssh-client \
    && rm -rf /var/lib/apt/lists/*
# Mirror uid/gid 1000:1000 from the host so the bind-mounted /home/samkintop
# (added in docker-compose) is owned by the user from the container's view.
# bookworm-slim ships a `node` user at 1000 — wipe whatever sits on uid/gid
# 1000 first, then create samkintop fresh.
RUN if id -u 1000 >/dev/null 2>&1; then \
        userdel -r "$(id -un 1000)" 2>/dev/null || true; \
    fi; \
    if getent group 1000 >/dev/null 2>&1; then \
        groupdel "$(getent group 1000 | cut -d: -f1)" 2>/dev/null || true; \
    fi; \
    groupadd -g 1000 samkintop && \
    useradd -m -u 1000 -g 1000 -s /bin/bash samkintop
WORKDIR /app
COPY --from=builder /build/apps/booterm/dist ./dist
COPY --from=proddeps /prod/package.json ./package.json
COPY --from=proddeps /prod/node_modules ./node_modules
COPY apps/booterm/tmux.conf /etc/booterm/tmux.conf
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/index.js"]
