CI / ci (push) Canceled after 0s
The session built: Appointments page: a single list, newest first. Block Time: edit and delete. Customer CSV export: with barber notes excluded. Guest-link regression test: a test that an appointment id alone is rejected, plus fixes to the stale docs. It built the scheduler, job runner with live progress, backup and restore, and the health alerts. Its own report says a real delete-and-restore on throwaway Docker passed, but I haven’t repeated that. It also says real SMTP and large dumps were not exercised. The manager image needs rebuilding for the backups to work, because it now installs 7zip.
58 lines
2.4 KiB
Docker
58 lines
2.4 KiB
Docker
# Standalone image for the management UI — separate from the main app's
|
|
# Dockerfile (see ../Dockerfile) since this needs the Docker CLI itself
|
|
# baked in (it shells out to `docker compose`/`docker exec` against the
|
|
# host's dockerd via the mounted socket — see src/lib/docker.ts), which
|
|
# the main business app never needs.
|
|
#
|
|
# Only one instance of this ever runs (unlike the business image, which
|
|
# is built once and reused across every business — see
|
|
# src/lib/compose-template.ts's own note) so a per-deploy `build:` in
|
|
# stacks/manager/compose.yaml, rather than a separately tagged image, is
|
|
# the simpler choice here.
|
|
|
|
FROM node:22-alpine AS deps
|
|
WORKDIR /app
|
|
COPY package.json package-lock.json* ./
|
|
RUN npm install
|
|
|
|
FROM node:22-alpine AS builder
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
RUN npm run build
|
|
|
|
FROM node:22-alpine AS runner
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production
|
|
|
|
# The Docker CLI + compose plugin — this container never runs its own
|
|
# Docker daemon, it only talks to the HOST's via the socket mounted in
|
|
# stacks/infra/compose.yaml's manager service (docker.sock).
|
|
RUN apk add --no-cache docker-cli docker-cli-compose
|
|
|
|
# 7-Zip for encrypted backups (`7zz a -mhe=on -p…`, see src/lib/sevenzip.ts).
|
|
# Alpine's package is named `7zip` and installs /usr/bin/7zz (checked
|
|
# against node:22-alpine, Alpine 3.24: `apk add 7zip` → 7-Zip 26.01).
|
|
RUN apk add --no-cache 7zip
|
|
|
|
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 manager
|
|
|
|
COPY --from=builder --chown=manager:nodejs /app/node_modules ./node_modules
|
|
COPY --from=builder --chown=manager:nodejs /app/.next ./.next
|
|
COPY --from=builder --chown=manager:nodejs /app/public ./public
|
|
COPY --from=builder --chown=manager:nodejs /app/package.json ./package.json
|
|
COPY --from=builder --chown=manager:nodejs /app/next.config.ts ./next.config.ts
|
|
|
|
# Deliberately still root at runtime, not the `manager` user created
|
|
# above — this container's whole job is calling the Docker CLI against
|
|
# the host's socket, and on most VPS setups only root (or a user in the
|
|
# `docker` group, which would need to be created with a matching host
|
|
# GID) can use that socket. Worth being explicit about: root inside this
|
|
# container plus the mounted socket effectively means root on the host —
|
|
# this app must stay behind its own login (see src/lib/auth.ts) and never
|
|
# be exposed without that gate.
|
|
EXPOSE 3000
|
|
ENV PORT=3000
|
|
|
|
ENTRYPOINT ["node_modules/.bin/next", "start", "-p", "3000"]
|