mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-08-02 04:15:40 +00:00
ci/cd(backend): offline test suite + Dockerize + audit-grade deploy (b + c)
Closes the "backend has zero CI + manual SSH deploy" audit finding for the indexer. Part B — CI-runnable tests (the integration-tests/*.live.test.js need a live chain and are excluded, not stubbed): extract injectable cores (indexer.js: DB+HTTP+decode, no @pezkuwi import; council.js: createApp factory for the council/KYC routes). New node:test suite (19/19, fully offline) — in-memory sqlite, dependency-injected chain stub, an in-memory fake-supabase, and REAL @pezkuwi keyring signatures proving 401 bad-sig / 400 msg-mismatch / 200 valid / 409 dup / 403 non-member / threshold auto-execute. Backend job now runs npm ci + npm test and is in ci-gate (failing tests block merge). Fixed a latent bug: block indexing now awaits each insert (was fire-and-forget forEach). Added runtime deps server.js imported but were missing from package.json (@supabase/supabase-js, pino, pino-http) — server.js could not have `npm ci`-run before. Part C — Dockerfile (non-root, HEALTHCHECK /health, sqlite state on a /data VOLUME kept out of the image) + backend deploy pipeline mirroring the web one: build+push to GHCR, cosign keyless sign + verify, ssh deploy with the DB volume preserved across deploys, /health poll, auto-rollback to previous SHA, same Telegram CEO approval gate, main/tags only, never fork PRs. New secrets documented in backend/DEPLOY.md (BACKEND_VPS_HOST/USER/ SSH_KEY). DB_PATH env (default ./transactions.db) lets prod point at the volume. Note for owner: if server.js (council/KYC bootstrap) is legacy/unused, its newly-added deps can be dropped instead.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# pwap indexer service — runnable Node container (not a static-dist image).
|
||||
#
|
||||
# Two stages on the SAME debian base so the natively-compiled sqlite3 binding
|
||||
# built in stage 1 is ABI-compatible with the runtime in stage 2 (do NOT mix
|
||||
# alpine/musl here — sqlite3 would fail to load).
|
||||
#
|
||||
# STATEFUL DATA: the indexer's sqlite file is the service's memory. It is kept
|
||||
# OUT of the image, under /data, which MUST be a mounted volume. A deploy
|
||||
# replaces the container/image but never the volume — see backend/DEPLOY.md.
|
||||
|
||||
# ─── Stage 1: install production deps (compiles sqlite3) ────────
|
||||
FROM node:20-bookworm AS builder
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
# --omit=dev: the test-only devDeps (supertest, @pezkuwi/keyring) never ship.
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
# ─── Stage 2: runtime ──────────────────────────────────────────
|
||||
FROM node:20-bookworm-slim
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3001
|
||||
# DB on the volume, never in the image layer.
|
||||
ENV DB_PATH=/data/transactions.db
|
||||
WORKDIR /app
|
||||
|
||||
# curl only, for the HEALTHCHECK probe.
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
COPY package.json ./
|
||||
COPY src ./src
|
||||
|
||||
# /data holds the stateful sqlite DB — declared as a volume and owned by the
|
||||
# non-root runtime user so the container can write to the mount.
|
||||
RUN mkdir -p /data && chown -R node:node /app /data
|
||||
VOLUME ["/data"]
|
||||
|
||||
USER node
|
||||
EXPOSE 3001
|
||||
|
||||
# Probe the in-container /health endpoint added to the indexer API.
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
||||
CMD sh -c 'curl -fsS "http://localhost:${PORT:-3001}/health" || exit 1'
|
||||
|
||||
CMD ["node", "src/index.js"]
|
||||
Reference in New Issue
Block a user