Files
parking_solution/apps/collector/Dockerfile
T
julian b485e9870b
CI / check (push) Failing after 40s
Build & push images / images (push) Failing after 32s
Build desktop / desktop (push) Successful in 5m24s
feat(collector): review collector skeleton — apps/collector, its own Komodo stack on the reviewer's host
The far end of the Car Wash review outbox (wiki/concepts/vision-review-outbox.md): a small
Fastify + SQLite service in the monorepo (shares the payload contract and the class
vocabulary via @parking/shared), delivered to art-docker-station by its own stack so
nothing booth-side lands there and nothing of it on a booth.

- POST /ingest: bearer token per booth (constant-time), X-Booth-Id must match, multipart
  meta + JPEG (magic checked, 2 MB cap), meta validated against the contract, idempotent on
  the item id; crop stored at crops/<booth>/<item>.jpg on the volume + one items row.
- /review + /api/*: the reviewer's screen served by the process (Basic auth, one login):
  one pending crop at a time, operator's pick and camera's pick beside it, one button/key
  per vocabulary class + unusable + skip; stats per booth and per hashed operator
  (agree / disagree / unusable — disagree = the reviewer's class is outside the operator's
  category).
- GET /export/labels.csv: reviewed usable rows for training; formula-leading cells are
  neutralised (booth-supplied names). Crops stay on the volume for the trainer on the host.
- Booth payload now carries operatorCategory.classes so the comparison needs no site setup.
- Delivery: apps/collector/Dockerfile (monorepo context), docker-compose.collector.yml
  (bind to the overlay IP; commented `trainer` profile seam for the GPU), a third build
  step in build-images.yml, a `wash-collector` stack in komodo/resources.toml with one
  secret per booth referenced from both the collector's token list and the booth's own
  stack (park-2 lines templated, commented, DNS name for the URL).
- Tests: app.test.ts (ingest ok/dup/refusals, review + stats + export, config). Image
  built and smoke-tested locally (health, ingest, duplicate, auth, verdict, export).

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
2026-09-07 08:26:22 +02:00

49 lines
2.1 KiB
Docker

# parking-collector — the Car Wash review collector (wiki/concepts/vision-review-outbox.md).
# Built from the monorepo root (context: .) like the server image, so it shares the
# lockfile and @parking/shared. Runs on the REVIEWER's host (not a booth), delivered by
# its own Komodo stack (docker-compose.collector.yml). Data on /data: collector.sqlite +
# crops/<booth>/<item>.jpg — the trainer on the same host reads the crops off that volume.
FROM node:22-alpine AS deps
WORKDIR /app
RUN apk add --no-cache python3 make g++ # node-gyp for better-sqlite3
RUN corepack enable && corepack prepare pnpm@10.24.0 --activate
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
COPY apps/server/package.json apps/server/
COPY apps/web/package.json apps/web/
COPY apps/vision/package.json apps/vision/
COPY apps/collector/package.json apps/collector/
COPY packages/db/package.json packages/db/
COPY packages/devices/package.json packages/devices/
COPY packages/shared/package.json packages/shared/
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm fetch
FROM deps AS build
ENV CI=true
COPY . .
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile --offline
RUN pnpm turbo run build --filter=@parking/collector
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm --filter=@parking/collector --legacy deploy --prod /deploy
FROM node:22-alpine AS runtime
WORKDIR /app
ARG BUILD_VERSION=""
ENV BUILD_VERSION=$BUILD_VERSION
ENV NODE_ENV=production
RUN apk add --no-cache libstdc++ wget # better-sqlite3 native runtime; wget for the healthcheck
RUN addgroup -S app && adduser -S -G app app
COPY --from=build --chown=app:app /deploy ./
ENV COLLECTOR_DATA_DIR=/data
ENV COLLECTOR_HOST=0.0.0.0
ENV COLLECTOR_PORT=8090
RUN mkdir -p /data && chown app:app /data
VOLUME ["/data"]
USER app
EXPOSE 8090
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- "http://localhost:${COLLECTOR_PORT:-8090}/health" >/dev/null 2>&1 || exit 1
CMD ["node", "dist/index.js"]