Skip to content

Consolidate frontend + backend into a single container#493

Open
kcarnold wants to merge 2 commits into
mainfrom
single-container-consolidation
Open

Consolidate frontend + backend into a single container#493
kcarnold wants to merge 2 commits into
mainfrom
single-container-consolidation

Conversation

@kcarnold

Copy link
Copy Markdown
Contributor

Collapses the two production containers (nginx serving `frontend/dist` + proxying `/api/`, and the Hono backend) into one: the Hono server now serves the built frontend static files and the `/api/*` routes. Deploy/topology change only — app behaviour, the API, and local dev are unchanged.

Spec/record: docs/single-container-consolidation.md.

What's in here

  • Backend static serving (`backend/src/static.ts`): `serveStatic` with Vite-shaped cache rules, registered after every `/api/*` route (incl. the dynamic device/debug ones). MPA → unmatched GETs 404 (no SPA fallback). No-op in local dev.
  • Root `Dockerfile` + `.dockerignore`: build context is the repo root; builds the frontend (incl. the google-docs bundle) and backend, serves `dist/` as the static root and the google-docs bundle at `/gdocs/`.
  • docker-compose (base + dev/staging/prod): drop the `frontend` service, point `backend.build` at the root Dockerfile, move public ports onto `backend`, set `PORT=5000`.
  • Single persistent volume per env at `/app/backend/data` (auth.db + logs via `LOG_DIR`).
  • GitHub Actions (`build-addin-image.yml`): build & push the combined image to GHCR, mirroring the experiment workflow; runs in parallel with Jenkins during the deploy migration.
  • Delete `frontend/Dockerfile` + `frontend/nginx.conf`.

Bugs fixed (both pre-existing, exposed by verification)

  • nginx/old cache regex `.[a-f0-9]{8,}.` matched zero Vite assets → every hashed bundle silently got the 1h cache instead of `immutable`.
  • The google-docs lib build re-copied the raw `public/manifest.xml` over the prod-transformed one (shipping a `localhost:3000` / `-dev` manifest). Fixed with `publicDir: false` in `vite.google-docs.config.ts`.

Verified against the built image

`/api/ping` 200 · HTML/manifest `no-store` · manifest `application/xml` + prod-transformed · hashed bundle `immutable` · unknown route 404 (MPA) · `/api/does-not-exist` 404 · `/gdocs/...` served · `POST /api/log` writes to the single `data/logs/` volume · backend tests 13/13 · frontend `test:build` green.

Before merge / deploy

  • ⚠️ One-time host data migration before first staging/prod deploy (single volume starts empty otherwise) — commands in doc §7.
  • Not testable locally: live SSE with a real `OPENAI_API_KEY`, and loading the add-in in Word against the deployed origin.

🤖 Generated with Claude Code

@kcarnold kcarnold force-pushed the single-container-consolidation branch from 9f18e0a to 4623900 Compare June 24, 2026 17:27
Collapse the two production containers (nginx serving frontend/dist +
proxying /api/, and the Hono backend) into one: the Hono server now serves
the built frontend static files and the /api/* routes.

- backend/src/static.ts: serveStatic with Vite-shaped cache rules. The old
  nginx regex (\.[a-f0-9]{8,}\.) matched zero Vite assets, silently
  downgrading every hashed bundle from immutable to the 1h rule; the new
  rule matches Vite's name-<hash>.ext output. HTML and manifest.xml are
  no-store (the only correctness-critical rule). Registered after all
  /api/* routes (incl. the dynamic device/debug ones) so the API wins; MPA,
  so unmatched GETs 404 (no SPA fallback). No-op in local dev.
- Root Dockerfile + .dockerignore: build context is the repo root; builds
  the frontend (incl. google-docs bundle) and backend, serves dist/ as the
  static root and the google-docs bundle at /gdocs/.
- docker-compose: drop the frontend service, point backend build at the
  root Dockerfile, move the public ports onto backend, set PORT=5000.
- Single persistent volume per env at /app/backend/data (auth.db + logs/
  via LOG_DIR). Requires a one-time host data migration (see doc §7).
- .github/workflows/build-addin-image.yml: build & push the combined image
  to GHCR (SHA-tagged), mirroring the experiment workflow; runs in parallel
  with Jenkins during the deployment migration.
- vite.google-docs.config.ts: publicDir:false so the lib build no longer
  re-copies the raw public/manifest.xml over the prod-transformed one.
- Delete frontend/Dockerfile and frontend/nginx.conf.
- Rewrite docs/single-container-consolidation.md for the Vite reality.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kcarnold kcarnold force-pushed the single-container-consolidation branch from 4623900 to 0e86c00 Compare June 24, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant