exifcleaner-web/Dockerfile
obuvuyoviz26-lab dfbd680737
B: Deployable webapp — Vite build, web adapters, JPEG/PDF strategies, PWA, Docker, CI
Phase B ships a static-asset web build that processes files entirely in the browser via WASM and pure-TS strategies — no server, no upload, fully offline. Same renderer code path as Electron; WASM strategies plug in via the existing `FormatStrategy` interface, and `platform.isWeb` drives routing.

## Web build infrastructure

- `vite.config.web.ts` + dedicated entry `src/web/main.tsx`
- Web adapters (`FileRegistry`, `BrowserFileBytes`, `makeWebApi`) mirror the Electron preload contract so the renderer is unchanged
- File picker button (`FileBrowseButton`) gated on `platform.isWeb`; rendered both in the empty state and in the StatusBar so users can add to a mid-batch without clearing

## PWA + deployment

- `manifest.webmanifest`, service worker via `vite-plugin-pwa` (Workbox)
- `Dockerfile` (multi-stage Node 22 → nginx:alpine) with `nginx.conf` carrying COOP/COEP/CSP/cache headers
- Cloudflare Pages: `public/_headers` mirrors the nginx config; deploy workflow shipped at `.github/workflows/deploy-web.yml` (gated to `workflow_dispatch` until secrets are wired)
- Unified deploy guide at `docs/deploying.md` covering Cloudflare Pages, Docker + Caddy, Docker + nginx + certbot, Cloudflare Tunnel, and Tailscale Funnel

## JPEG strategy — replaces piexifjs entirely

Hand-rolled marker walker at `src/infrastructure/wasm/strategies/jpeg_strategy.ts`. Mirrors ExifTool's `-all=` policy with two deliberate exceptions: APP14 (Adobe DCT) always kept, APP2 (ICC) kept on opt-in via `preserveColorProfile`. Drops APP0/APP1/APP3–APP13/APP15 + COM. Entropy-stream byte-stuffing and RST markers preserved verbatim.

- Fill-byte tolerance per T.81 §B.1.1.2
- Truncation surfaces as `Result<_, ExifError>` rather than silently emitting a no-EOI file
- Removes `piexifjs` and `@types/piexifjs` as production deps
- Closes the prior `TextDecoder("latin1")` corruption bug (WHATWG aliases `latin1` to windows-1252, so 0x80–0x9F bytes were silently rewritten)

## PDF strategy — rewrites the previous "setX('')" pass

`pdf-lib` with `updateMetadata: false` to defeat auto-stamp of Producer / ModDate / Creator / CreationDate. Direct Info-dict key deletion. Drops the catalog `/Metadata` reference *and* the indirect XMP stream object (orphan-free). Scrubs annotation `/T`, `/Contents`, `/M`, `/CreationDate`, `/RC`, `/Subj`. Drops `/Lang`, `/PageLabels`, `/OutputIntents`, per-page `/Metadata`, per-page `/Thumb`. AcroForm and `/EmbeddedFiles` deferred behind opt-ins.

Also corrects the previous "pdf-lib re-injects Producer" claim in source comments + README — it does not (verified empirically; the string is an in-memory `getProducer()` fallback, not file content).

## Documentation pattern

Three companion folders for each format:

- `docs/gap-analysis/<format>.md` — current vs reference vs theoretical, *before* implementation. Contains `pdf.md` and `jpeg.md`.
- `docs/poc/<approach>.md` — library evaluations. Contains `little-exif-wasm.md` and `exiv2-wasm.md` (both ruled out — full size + coverage data).
- `docs/forensic/<format>.md` — adversarial recovery tests *after* implementation, with reproducible runner under `tools/forensic/`. Contains `pdf.md` (zero sentinel survival across `strings`, `exiftool -PDF-update:all=`, `qpdf --qdf`, and in-process pdf-lib indirect-object walk; ExifTool's own output leaks 8/10 sentinels via the same battery).

## CI

New workflow runs lint + typecheck + tests + electron compile + Playwright e2e on PRs and master. Platform builds compile on PRs but skip artifact upload to avoid the GitHub Actions storage quota; uploads only happen on master pushes.

## Deferred (tracked as issues)

- HEIC strategy for mobile (issue #48 — most-hit format on iPhone)
- Mobile touch UX pass (#49)
- iOS Photos picker UX note (#50)
- Unsupported-format messaging (#51)
- PWA install prompt UX (#52)
- `preserveOrientation` honoring inside JPEG (Phase 2 of `docs/gap-analysis/jpeg.md`)
- Forensic comparison-corpus runs for JPEG and other formats

## Stats

- 369/369 unit tests, lint clean, typecheck clean
- Electron build: ~640 KB renderer bundle
- Web build: 1.1 MB precache (PWA shell)
- 30 commits on the branch (squashed into this one)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-07 17:56:50 +04:00

17 lines
416 B
Docker

# Stage 1: build the static web bundle
FROM node:22-alpine AS builder
WORKDIR /app
# Install dependencies first (better layer caching)
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
# Copy source and build
COPY . .
RUN yarn build:web
# Stage 2: serve with nginx
FROM nginx:alpine AS server
COPY --from=builder /app/dist/web /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80