Commit Graph

14 Commits

Author SHA1 Message Date
Matthew Meszaros 2430095197 feat: move infrastructure state off dynamodb
Remove DynamoDB-backed storage paths, add Postgres/HTTP repositories for mailbox state maps, wire the internal message-map API, and add provisioning runner/migration plumbing.
2026-06-02 15:54:12 +02:00
Matthew Meszaros 559d05a458 feat: use dot-separated event-bus topic names for Kafka compatibility 2026-06-01 12:49:24 +02:00
Matthew Meszaros adedcc337e feat: support remote dev infra
Allow native dev services to target infrastructure running on another host by parameterizing local endpoints with INFRA_HOST and SELF_HOST.

Document the workflow and make Kafka advertise a configurable host for off-box clients.
2026-05-31 08:53:21 +02:00
Matthew Meszaros bf40834557 feat: update unibox experience 2026-05-30 16:37:50 +00:00
Matthew Meszaros 0740a8bbbe feat: fix local migrations and kafka topics 2026-05-30 14:11:47 +00:00
Matthew Meszaros e463df748f fix: add zookeeper healthcheck so kafka waits for sessions to be ready 2026-05-25 04:32:41 +00:00
Matthew Meszaros 6eb3a3bdfd merge: bring origin/main into branch, layer full seed on top of seedRich 2026-05-24 11:54:49 +00:00
Matthew Meszaros e5c0c8a448 chore(dev): standardize ports where they don't conflict
Most container ports go back to their natural defaults — the offsets
that existed weren't justified, they just made URLs harder to remember.
Now standard:

  backend         8080   (was always 8080)
  tracking        3000   (was 13000)
  realtime        4000   (was 14000)
  web             5173   (was 15173 — already changed)
  kafka           9092   (was 19092)
  schema-registry 8081   (was 18081)
  localstack      4566   (was 14566)
  cloud-tasks     8123   (was 18123)
  stripe-mock     12111  (always was)

Kept offset (the defaults conflict too often on real dev machines):

  postgres        15432  (system postgres / sibling project)
  redis           16379  (sibling docker projects with redis)
  mailpit ui      18025  (sibling docker projects with mailpit)
  mailpit smtp    11025  (same)
  kafka-ui        18090  (8080 already used by backend)

Touched: docker-compose.yml, Makefile (test-seed SEED_TEST_DB), READMEs
(root + deploy), local-development.md + deployment-guide.md. Internal
docker-network refs (kafka:29092, mailpit:1025, etc.) unchanged — only
host-port mappings moved. Compose validated, all default-profile
services come up healthy on the new ports.
2026-05-22 14:51:16 +00:00
Matthew Meszaros 33ea340660 fix(dev): set APP_URL on backend so CORS allows the web origin
The backend's CORS allow-list resolution falls back through:
  1. CORS_ALLOW_ORIGINS
  2. APP_URL
  3. origin derived from WEBSOCKET_URL

In compose only WEBSOCKET_URL was set, so allow-list landed at
http://localhost:14000 — the realtime port, not where the browser is
loaded from. The dev fallback that includes localhost:5173 only fires
when the list is empty, not when it's wrong-by-derivation. Result:
every POST to /auth/login etc. failed preflight with 403, which axios
surfaces as a generic "NetworkError" — login appeared to silently fail
after entering the password.

Setting APP_URL=http://localhost:5173 on the backend service makes the
allow-list match where the Vite dev server actually serves from. Sanity
checked with curl -X OPTIONS — preflight now returns 204.
2026-05-22 04:04:42 +00:00
Matthew Meszaros 34ab7ad266 fix(web): correct Request import depth + move web back to port 5173
The new admin API clients (audit, credentials, workers) imported Request
with four '..' segments instead of three. Vite's import-analysis failed
with "Failed to resolve import ../../../../Request" because that path
resolves to api/Request, not client/Request. tsc didn't catch it because
the resolver was permissive enough to keep going, but the runtime is
strict. Matched the existing pattern from roles/getRoles.ts (three dots
for Request, four for models).

Separately: web was on host port 15173, offset from the canonical 5173
to avoid colliding with a locally-running Vite outside Docker. Nobody
actually runs Vite locally in this setup, and the offset makes the URL
non-obvious. Moved back to 5173:5173 and updated VITE_APP_URL plus the
docs.

If a developer one day wants to run a host-side Vite alongside the
container, change the mapping back to "15173:5173" — the offset is the
escape hatch, not the default.
2026-05-22 03:55:34 +00:00
Matthew Meszaros 5a7fa6c71b fix(web): unbreak pnpm install in docker — drop rolldown-vite, fix retry loop
Four interlocking problems were causing the web container to spin in
its retry loop forever:

1. web/package.json aliased vite to "npm:rolldown-vite@7.1.14".
   rolldown-vite is being deprecated (its own warning told us to use
   7.3.1 for migration, or move to vite 8). vitest 4.x has a transitive
   `vite` dep that pnpm tried to resolve against the public registry,
   where vite@7.1.14 doesn't exist as a release (only 7.3.3 and 8.x).
   Result: ERR_PNPM_NO_MATCHING_VERSION on every retry.

2. The "resolutions" block was meant to force the alias on transitive
   deps. resolutions is Yarn syntax; pnpm doesn't read it. So the alias
   wasn't propagating, which is exactly why (1) blew up.

3. pnpm 11 stopped reading the "pnpm" field in package.json. Settings
   moved to pnpm-workspace.yaml. New file added with allowBuilds.esbuild
   set so pnpm doesn't refuse to compile esbuild's native binary at
   install time (it's transitively pulled in by vite + vitest).

4. The docker-compose web service had `until pnpm install; do echo
   "pnpm install retry..."; sleep 3; done` which spins forever on
   permanent dep-resolution errors and buries the actual message under
   thousands of retries. Replaced with fail-fast that prints a hint
   directing the admin to fix package.json and `make restart web`.

Also deleted the stale pnpm-lock.yaml so pnpm regenerates against the
fresh dep tree. Verified vite v7.3.3 boots, esbuild postinstall runs,
and Vite serves on http://localhost:15173 cleanly.

vite.config.ts has no rolldown-specific config, so the move from
rolldown-vite to plain vite is a no-op behaviourally.
2026-05-22 03:52:49 +00:00
Matthew Meszaros f95a12f2a1 Revert "feat(dev): hot reload for Go services in docker-compose"
This reverts commit 3a84e33155.
2026-05-22 03:37:34 +00:00
Matthew Meszaros 3a84e33155 feat(dev): hot reload for Go services in docker-compose
Until now, only the web service hot-reloaded (Vite HMR via the
node:22-alpine container + ./web mount). The Go services (backend,
consumer, worker) used their production multi-stage Dockerfiles, so
every code change meant `docker compose build <svc> && docker compose
up -d <svc>` — ~30s per service.

Switched all Go services to a shared dev image (go.dev.Dockerfile)
that ships:
  - full Go 1.25 toolchain on alpine
  - CGO deps for librdkafka (gcc, musl-dev, librdkafka-dev, pkgconf)
  - air v1.61.7 (the source watcher / hot-recompile tool)

docker-compose mounts the repo at /app and runs `air -c <config>`.
Each Go service has its own air.SERVICE.toml (build target +
exclusions). Named volumes for the Go module cache and build cache
so the first build is slow (~60s for module download) but subsequent
rebuilds after a save are ~2s.

Per-service compose changes:
  - backend, consumer: dockerfile, volumes, and command updated
  - worker-base (the YAML anchor used by all 3 workers): same

Production Dockerfiles in deploy/docker/{backend,consumer,worker}.
Dockerfile are unchanged and still used by release CI. The seed
one-shot in compose continues to use backend.Dockerfile (it's a
short-lived job, no benefit from the dev image).

Rust (tracking) and Elixir (realtime) still build-on-change. They
change far less often; documenting the workaround in
resources/local-development.md for now.
2026-05-22 03:37:05 +00:00
Matthew Meszaros d25eed3eb6 feat(dev): root docker-compose with profiles, LocalStack, richer seed
Hoist the dev/sim stack to a single docker-compose.yml at the repo root.
Adds profiles (default / sim / seed / tools) so you can opt into heavier
setups, and bundles dependencies that were previously missing:

- LocalStack (KMS + DynamoDB + S3) with a localstack-init one-shot that
  idempotently creates alias/master-key-dev, the UserEncryptedKeys and
  EmailMessageData tables, and the main S3 bucket. Backend and workers
  wait on it via service_completed_successfully.
- stripe-mock for billing flows
- kafka-ui under the tools profile

Three workers with deterministic UUIDv5 hostnames (shared / premium /
dedicated) so assignment, rebalancing, and per-pool routing all have
real targets to exercise.

Richer seed (cmd/seed/main.go) loads 3 orgs across tiers, 6 mailboxes
joined to free/premium warmup pools, a Beta campaign with a 2-step
sequence, and 10 contacts (2 unsubscribed) so suppression behaviour is
visible in the UI. Idempotent — safe to re-run.

Makefile targets:
  make dev    — infra + app + one worker
  make sim    — adds premium + dedicated workers
  make seed   — rich fixtures
  make tools  — kafka-ui at :18090
  make reset  — nuke volumes
2026-05-18 13:08:34 +00:00