Server: web client under /app/ (D-132), analytics E-34 trends and E-35 forecast (D-133)
Build Kvitt images / build (push) Failing after 1m20s
Build Kvitt images / build (push) Failing after 1m20s
Part A (D-132): KVITT_WEB_DIR (default /app) served under /app/ by a small handler: /app redirects to /app/, app routes fall back to index.html, a missing assets/* file is 404, assets cached immutable, index.html no-store, the app's own CSP plus nosniff, DENY and no-referrer. Unsafe paths never reach the file system. /app paths count against the unauthenticated per-IP limit. Without the directory every /app path stays 404 not_found. server/Dockerfile gains a Node 24 LTS stage (pinned by digest) building web/, passed through the Rust stage so npm and rustc never run at once on the Pi 3; the build context is the repository root with an allowlist .dockerignore (server/.dockerignore removed); workflow, build-images.sh and infra/dev/build.sh follow. Part B (D-133): GET /analytics/trends (E-34) and GET /analytics/forecast (E-35), authenticated, read-only, SQL month buckets; rules (half-up average, trailing 3-month moving average, percent against the rounded average, template occurrence per month) in kvitt-domain::analytics. OpenAPI, examples, contracts-check and tests updated (35 operations). Cargo.lock: rustls-webpki 0.103.16 and webpki-root-certs 1.0.10 (RUSTSEC-2026-0382, -0383) so that cargo audit passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
+31
-6
@@ -1,9 +1,28 @@
|
||||
# Kvitt server image (SPEC §10.3). Build context: server/. Built on the owner's M4 Mac, never on the Pi:
|
||||
# docker build --platform linux/arm64 -f server/Dockerfile -t kvitt-server:dev server/
|
||||
# Kvitt server image (SPEC §10.3). Build context: the repository root (D-132; the root .dockerignore lets only
|
||||
# server/, web/ and contracts/ in). From the repository root:
|
||||
# docker build --platform linux/arm64 -f server/Dockerfile -t kvitt-server:dev .
|
||||
# linux/amd64 builds the same way (--platform linux/amd64; emulated on the Mac, slow). The Rust version is the one
|
||||
# pinned in rust-toolchain.toml; the two always change together.
|
||||
# pinned in rust-toolchain.toml; the two always change together. The image also carries the web client (web/,
|
||||
# a Vite single-page app built with base '/app/'), which the server serves under /app/ from KVITT_WEB_DIR (/app).
|
||||
|
||||
# ---- stage 1: build --------------------------------------------------------------------------------------------
|
||||
# ---- stage 1: web client ---------------------------------------------------------------------------------------
|
||||
# Node.js 24, the Active LTS line on 2026-10-21 (26 becomes LTS at the end of October 2026). Pinned by tag and by
|
||||
# the digest of its multi-arch index. Runs on the build platform: the output is static files, the same for every
|
||||
# target, so an amd64 image never emulates npm. web/ without package.json or package-lock.json fails here, by design.
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:24.22.1-bookworm-slim@sha256:55fb5f5a527ea2c5c4a95f963e48ef90aa38c7b346054fdc8ee9957a2b55d205 AS web
|
||||
ENV CI=true \
|
||||
NPM_CONFIG_AUDIT=false \
|
||||
NPM_CONFIG_FUND=false \
|
||||
NPM_CONFIG_UPDATE_NOTIFIER=false
|
||||
# The repository layout is kept (/repo/web beside /repo/contracts), so the web build may read ../contracts.
|
||||
WORKDIR /repo/web
|
||||
COPY web/package.json web/package-lock.json ./
|
||||
RUN --mount=type=cache,target=/root/.npm npm ci
|
||||
COPY contracts/ /repo/contracts/
|
||||
COPY web/ ./
|
||||
RUN npm run build && test -f dist/index.html
|
||||
|
||||
# ---- stage 2: server build -------------------------------------------------------------------------------------
|
||||
FROM docker.io/library/rust:1.99.1-bookworm AS build
|
||||
ARG TARGETARCH
|
||||
# Parallel rustc jobs. Unset (the default) means cargo's own default, one per core, as on the Mac. The Gitea runner
|
||||
@@ -16,7 +35,11 @@ ARG CARGO_BUILD_JOBS
|
||||
ENV RUSTUP_TOOLCHAIN=1.99.1 \
|
||||
CARGO_TERM_COLOR=never
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
COPY server/ .
|
||||
# The web client passes through this stage on its way to the runtime image. That also orders the builds: BuildKit
|
||||
# runs independent stages in parallel, and npm next to rustc could exhaust the 1 GB of the Pi 3 runner. A web-only
|
||||
# change re-runs the cargo step below, which then finds nothing to compile (the target directory is a cache mount).
|
||||
COPY --from=web /repo/web/dist /out/web
|
||||
# Native build for the target platform: aarch64-unknown-linux-gnu on arm64 (generic Armv8-A, no
|
||||
# target-cpu=native: the Pi 3 is a Cortex-A53), x86_64-unknown-linux-gnu on amd64.
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
@@ -32,11 +55,13 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
&& mkdir -p /out \
|
||||
&& cp "target/$triple/release/kvitt-server" "target/$triple/release/kvitt-import" /out/
|
||||
|
||||
# ---- stage 2: runtime ------------------------------------------------------------------------------------------
|
||||
# ---- stage 3: runtime ------------------------------------------------------------------------------------------
|
||||
# glibc + libgcc, no shell, no package manager. Pinned by digest (multi-arch index of :nonroot). It carries the
|
||||
# Debian CA bundle at /etc/ssl/certs/ca-certificates.crt, which rustls-platform-verifier reads to reach Google.
|
||||
FROM gcr.io/distroless/cc-debian12:nonroot@sha256:9dac0a79194e45a7da0158a9c6da57b217585af0786db3845d1f0ec1a0dd182f
|
||||
COPY --from=build /out/kvitt-server /out/kvitt-import /usr/local/bin/
|
||||
# The web client, served under /app/ (KVITT_WEB_DIR defaults to /app); owned by root, read-only for uid 10001.
|
||||
COPY --from=build /out/web /app
|
||||
# Runs as uid/gid 10001 with a read-only root filesystem; /data is the only writable volume and /secrets is
|
||||
# mounted read-only (SPEC §8.9). Listens on 8080 inside the compose network.
|
||||
USER 10001:10001
|
||||
|
||||
Reference in New Issue
Block a user