The frontend the app needs to be usable at all. - web/: Svelte 5 + Vite + TypeScript SPA. Login, app shell with per-type nav, library list/create/scan/delete, item grid with covers, admin user management, and an epub.js reader that syncs progress. - Reader resumes by spine index — an integer — which is why the polymorphic progress table needs no text column. - Session state uses classic stores in a plain .ts module rather than runes in a .svelte.ts module, avoiding special module resolution. - Embedding: web/embed.go embeds web/dist. A committed .gitkeep keeps the directive valid before the frontend is built, so `go build ./...` never fails. Unknown non-API paths serve index.html for client routes; /api/* and /healthz always return JSON and never fall back to HTML. - Dockerfile builds the UI in a node stage and overlays dist into the Go stage. - pnpm 12 needs allowBuilds in pnpm-workspace.yaml; esbuild approved, the two unused transitive scripts explicitly denied so install exits 0. svelte-check: 0 errors, 0 warnings. Verified served: SPA shell at /, route fallback, hashed asset, and JSON 404 on /api/*.
68 lines
2.4 KiB
Docker
68 lines
2.4 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# ── Frontend ───────────────────────────────────────────────────────────
|
|
# Built first so the Go stage can embed the result. Runs on the builder's
|
|
# native platform; the output is architecture-independent.
|
|
FROM --platform=$BUILDPLATFORM node:22-alpine AS web
|
|
|
|
WORKDIR /web
|
|
RUN corepack enable
|
|
|
|
# Dependency layer, cached independently of source changes.
|
|
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
COPY web/ ./
|
|
RUN pnpm build
|
|
|
|
# ── Go build ───────────────────────────────────────────────────────────
|
|
# CGO_ENABLED=0 works because the SQLite driver (modernc.org/sqlite) is pure
|
|
# Go, so there is no cgo and no libc to match against.
|
|
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS build
|
|
|
|
ARG TARGETOS=linux
|
|
ARG TARGETARCH=amd64
|
|
ARG VERSION=dev
|
|
|
|
WORKDIR /src
|
|
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
COPY . .
|
|
# Overlay the freshly built UI over the committed placeholder.
|
|
COPY --from=web /web/dist ./web/dist
|
|
|
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \
|
|
-o /out/vellum ./cmd/vellum
|
|
|
|
# ── Runtime ────────────────────────────────────────────────────────────
|
|
FROM alpine:3.22
|
|
|
|
RUN apk add --no-cache ca-certificates tzdata su-exec
|
|
|
|
COPY --from=build /out/vellum /usr/local/bin/vellum
|
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
|
|
# /data persistent state (SQLite database) — back this up
|
|
# /config regenerable cache (thumbnails, extracted pages)
|
|
# /media the library, mounted read-only; Vellum never writes here
|
|
VOLUME ["/data", "/config"]
|
|
|
|
ENV VELLUM_DATA_DIR=/data \
|
|
VELLUM_CONFIG_DIR=/config \
|
|
VELLUM_LISTEN=:7788
|
|
|
|
# PUID/PGID are consumed by entrypoint.sh, which drops root privileges after
|
|
# taking ownership of the data and cache directories.
|
|
ENV PUID=1001 \
|
|
PGID=1001
|
|
|
|
EXPOSE 7788
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -qO- http://127.0.0.1:7788/healthz || exit 1
|
|
|
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|